Compare commits
2 Commits
bf38318902
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66e212677c | ||
|
|
6e947f299e |
290
.eslintrc.json
290
.eslintrc.json
@@ -1,290 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": [
|
|
||||||
"eslint:recommended",
|
|
||||||
"plugin:prettier/recommended"
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
"import"
|
|
||||||
],
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 13
|
|
||||||
},
|
|
||||||
"env": {
|
|
||||||
"es2021": true,
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"ignorePatterns": [
|
|
||||||
"**/src/util/Voice.js",
|
|
||||||
"**/examples/**"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"import/order": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"groups": [
|
|
||||||
"builtin",
|
|
||||||
"external",
|
|
||||||
"internal",
|
|
||||||
"index",
|
|
||||||
"sibling",
|
|
||||||
"parent"
|
|
||||||
],
|
|
||||||
"alphabetize": {
|
|
||||||
"order": "asc"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"prettier/prettier": [
|
|
||||||
2,
|
|
||||||
{
|
|
||||||
"printWidth": 120,
|
|
||||||
"singleQuote": true,
|
|
||||||
"quoteProps": "as-needed",
|
|
||||||
"trailingComma": "all",
|
|
||||||
"endOfLine": "lf",
|
|
||||||
"arrowParens": "avoid"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"strict": [
|
|
||||||
"error",
|
|
||||||
"global"
|
|
||||||
],
|
|
||||||
// "no-await-in-loop": "warn",
|
|
||||||
"no-compare-neg-zero": "error",
|
|
||||||
"no-template-curly-in-string": "error",
|
|
||||||
"no-unsafe-negation": "error",
|
|
||||||
"valid-jsdoc": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"requireReturn": false,
|
|
||||||
"requireReturnDescription": false,
|
|
||||||
"prefer": {
|
|
||||||
"return": "returns",
|
|
||||||
"arg": "param"
|
|
||||||
},
|
|
||||||
"preferType": {
|
|
||||||
"String": "string",
|
|
||||||
"Number": "number",
|
|
||||||
"Boolean": "boolean",
|
|
||||||
"Symbol": "symbol",
|
|
||||||
"object": "Object",
|
|
||||||
"function": "Function",
|
|
||||||
"array": "Array",
|
|
||||||
"date": "Date",
|
|
||||||
"error": "Error",
|
|
||||||
"null": "void"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"accessor-pairs": "warn",
|
|
||||||
"array-callback-return": "error",
|
|
||||||
"consistent-return": "error",
|
|
||||||
"curly": [
|
|
||||||
"error",
|
|
||||||
"multi-line",
|
|
||||||
"consistent"
|
|
||||||
],
|
|
||||||
"dot-location": [
|
|
||||||
"error",
|
|
||||||
"property"
|
|
||||||
],
|
|
||||||
"dot-notation": "error",
|
|
||||||
"eqeqeq": "off",
|
|
||||||
// "no-empty-function": "error",
|
|
||||||
"no-floating-decimal": "error",
|
|
||||||
"no-implied-eval": "error",
|
|
||||||
"no-invalid-this": "error",
|
|
||||||
"no-lone-blocks": "error",
|
|
||||||
"no-multi-spaces": "error",
|
|
||||||
"no-new-func": "error",
|
|
||||||
"no-new-wrappers": "error",
|
|
||||||
"no-new": "error",
|
|
||||||
"no-octal-escape": "error",
|
|
||||||
"no-return-assign": "error",
|
|
||||||
"no-return-await": "error",
|
|
||||||
"no-self-compare": "error",
|
|
||||||
"no-sequences": "error",
|
|
||||||
"no-throw-literal": "error",
|
|
||||||
"no-unmodified-loop-condition": "error",
|
|
||||||
"no-unused-expressions": "error",
|
|
||||||
"no-useless-call": "error",
|
|
||||||
"no-useless-concat": "error",
|
|
||||||
"no-useless-escape": "error",
|
|
||||||
"no-useless-return": "error",
|
|
||||||
"no-void": "error",
|
|
||||||
// "no-warning-comments": "warn",
|
|
||||||
"prefer-promise-reject-errors": "error",
|
|
||||||
"require-await": "off",
|
|
||||||
"wrap-iife": "error",
|
|
||||||
"yoda": "error",
|
|
||||||
"no-label-var": "error",
|
|
||||||
// "no-shadow": "error",
|
|
||||||
"no-undef-init": "error",
|
|
||||||
"callback-return": "error",
|
|
||||||
"getter-return": "off",
|
|
||||||
"handle-callback-err": "error",
|
|
||||||
"no-mixed-requires": "error",
|
|
||||||
"no-new-require": "error",
|
|
||||||
"no-path-concat": "error",
|
|
||||||
"array-bracket-spacing": "error",
|
|
||||||
"block-spacing": "error",
|
|
||||||
"brace-style": [
|
|
||||||
"error",
|
|
||||||
"1tbs",
|
|
||||||
{
|
|
||||||
"allowSingleLine": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"capitalized-comments": [
|
|
||||||
"error",
|
|
||||||
"always",
|
|
||||||
{
|
|
||||||
"ignoreConsecutiveComments": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"comma-dangle": [
|
|
||||||
"error",
|
|
||||||
"always-multiline"
|
|
||||||
],
|
|
||||||
"comma-spacing": "error",
|
|
||||||
"comma-style": "error",
|
|
||||||
"computed-property-spacing": "error",
|
|
||||||
"consistent-this": [
|
|
||||||
"error",
|
|
||||||
"$this"
|
|
||||||
],
|
|
||||||
"eol-last": "error",
|
|
||||||
"func-names": "error",
|
|
||||||
"func-name-matching": "error",
|
|
||||||
"func-style": [
|
|
||||||
"error",
|
|
||||||
"declaration",
|
|
||||||
{
|
|
||||||
"allowArrowFunctions": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"key-spacing": "error",
|
|
||||||
"keyword-spacing": "error",
|
|
||||||
"max-depth": "error",
|
|
||||||
"max-len": [
|
|
||||||
"off",
|
|
||||||
120,
|
|
||||||
2
|
|
||||||
],
|
|
||||||
"max-nested-callbacks": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"max": 4
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"max-statements-per-line": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"max": 2
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"new-cap": "off",
|
|
||||||
"newline-per-chained-call": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"ignoreChainWithDepth": 4
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"no-array-constructor": "error",
|
|
||||||
"no-inline-comments": "off",
|
|
||||||
"no-lonely-if": "error",
|
|
||||||
"no-multiple-empty-lines": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"max": 2,
|
|
||||||
"maxEOF": 1,
|
|
||||||
"maxBOF": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"no-new-object": "error",
|
|
||||||
"no-spaced-func": "error",
|
|
||||||
"no-trailing-spaces": "error",
|
|
||||||
"no-unneeded-ternary": "error",
|
|
||||||
"no-whitespace-before-property": "error",
|
|
||||||
"nonblock-statement-body-position": "error",
|
|
||||||
"object-curly-spacing": [
|
|
||||||
"error",
|
|
||||||
"always"
|
|
||||||
],
|
|
||||||
"operator-assignment": "error",
|
|
||||||
"padded-blocks": [
|
|
||||||
"error",
|
|
||||||
"never"
|
|
||||||
],
|
|
||||||
"quote-props": [
|
|
||||||
"error",
|
|
||||||
"as-needed"
|
|
||||||
],
|
|
||||||
"quotes": [
|
|
||||||
"error",
|
|
||||||
"single",
|
|
||||||
{
|
|
||||||
"avoidEscape": true,
|
|
||||||
"allowTemplateLiterals": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"semi-spacing": "error",
|
|
||||||
"semi": "error",
|
|
||||||
"space-before-blocks": "error",
|
|
||||||
"space-before-function-paren": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"anonymous": "never",
|
|
||||||
"named": "never",
|
|
||||||
"asyncArrow": "always"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"space-in-parens": "error",
|
|
||||||
"space-infix-ops": "error",
|
|
||||||
"space-unary-ops": "error",
|
|
||||||
"spaced-comment": "error",
|
|
||||||
"template-tag-spacing": "error",
|
|
||||||
"unicode-bom": "error",
|
|
||||||
"arrow-body-style": "error",
|
|
||||||
"arrow-parens": [
|
|
||||||
"error",
|
|
||||||
"as-needed"
|
|
||||||
],
|
|
||||||
"arrow-spacing": "error",
|
|
||||||
"no-duplicate-imports": "error",
|
|
||||||
"no-useless-computed-key": "error",
|
|
||||||
"no-useless-constructor": "error",
|
|
||||||
"prefer-arrow-callback": "error",
|
|
||||||
"prefer-numeric-literals": "error",
|
|
||||||
"prefer-rest-params": "error",
|
|
||||||
"prefer-spread": "error",
|
|
||||||
"prefer-template": "error",
|
|
||||||
"rest-spread-spacing": "error",
|
|
||||||
"template-curly-spacing": "error",
|
|
||||||
"yield-star-spacing": "error",
|
|
||||||
"no-restricted-globals": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"name": "Buffer",
|
|
||||||
"message": "Import Buffer from `node:buffer` instead"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "process",
|
|
||||||
"message": "Import process from `node:process` instead"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "setTimeout",
|
|
||||||
"message": "Import setTimeout from `node:timers` instead"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "setInterval",
|
|
||||||
"message": "Import setInterval from `node:timers` instead"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "setImmediate",
|
|
||||||
"message": "Import setImmediate from `node:timers` instead"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"linebreak-style": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"singleQuote": true,
|
|
||||||
"printWidth": 120,
|
|
||||||
"trailingComma": "all",
|
|
||||||
"endOfLine": "lf",
|
|
||||||
"arrowParens": "avoid"
|
|
||||||
}
|
|
||||||
29
biome.json
Normal file
29
biome.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://biomejs.dev/schemas/2.3.8/schema.json",
|
||||||
|
"files": {
|
||||||
|
"includes": ["src/**/*.js", "typings/**/*.ts", "*.json"]
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"enabled": true,
|
||||||
|
"indentStyle": "space",
|
||||||
|
"indentWidth": 2
|
||||||
|
},
|
||||||
|
"javascript": {
|
||||||
|
"formatter": {
|
||||||
|
"quoteStyle": "single",
|
||||||
|
"semicolons": "always"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": false,
|
||||||
|
"style": {
|
||||||
|
"useNodejsImportProtocol": "warn"
|
||||||
|
},
|
||||||
|
"suspicious": {
|
||||||
|
"noExplicitAny": "warn"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
package.json
42
package.json
@@ -6,18 +6,14 @@
|
|||||||
"types": "./typings/index.d.ts",
|
"types": "./typings/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"all": "npm run build && npm publish",
|
"all": "npm run build && npm publish",
|
||||||
"test": "npm run lint:all && npm run docs:test && npm run test:typescript",
|
"test": "npm run lint && npm run test:typescript && npm run docs:test",
|
||||||
"fix:all": "npm run lint:fix && npm run lint:typings:fix && npm run format",
|
"fix:all": "npm run format",
|
||||||
"test:typescript": "tsc --noEmit && tsd",
|
"test:typescript": "tsc --noEmit && tsd",
|
||||||
"lint": "eslint .",
|
"lint": "biome check . --diagnostic-level=error",
|
||||||
"lint:fix": "eslint . --fix",
|
"format": "biome format --write .",
|
||||||
"lint:typings": "tslint typings/index.d.ts",
|
|
||||||
"lint:typings:fix": "tslint typings/index.d.ts --fix",
|
|
||||||
"format": "prettier --write src/**/*.js typings/**/*.ts",
|
|
||||||
"lint:all": "npm run lint && npm run lint:typings",
|
|
||||||
"docs": "docgen --source src --custom docs/index.yml --output docs/main.json",
|
"docs": "docgen --source src --custom docs/index.yml --output docs/main.json",
|
||||||
"docs:test": "docgen --source src --custom docs/index.yml",
|
"docs:test": "docgen --source src --custom docs/index.yml",
|
||||||
"build": "npm run lint:fix && npm run lint:typings:fix && npm run format && npm run docs"
|
"build": "npm run format && npm run docs"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"src",
|
||||||
@@ -51,39 +47,33 @@
|
|||||||
},
|
},
|
||||||
"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": "^1.6.3",
|
"@discordjs/builders": "^1.13.0",
|
||||||
"@discordjs/collection": "^2.1.1",
|
"@discordjs/collection": "^2.1.1",
|
||||||
"@sapphire/async-queue": "^1.5.5",
|
"@sapphire/async-queue": "^1.5.5",
|
||||||
"@sapphire/shapeshift": "^4.0.0",
|
"@sapphire/shapeshift": "^4.0.0",
|
||||||
"discord-api-types": "^0.38.15",
|
"discord-api-types": "^0.38.38",
|
||||||
"fetch-cookie": "^3.1.0",
|
"fetch-cookie": "^3.1.0",
|
||||||
"find-process": "^2.0.0",
|
"find-process": "^2.0.0",
|
||||||
"otplib": "^12.0.1",
|
"otplib": "^12.0.1",
|
||||||
"prism-media": "^1.3.5",
|
"prism-media": "^2.0.0-alpha.0",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"tough-cookie": "^5.1.2",
|
"tough-cookie": "^5.1.2",
|
||||||
"tree-kill": "^1.2.2",
|
"tree-kill": "^1.2.2",
|
||||||
"undici": "^7.11.0",
|
"undici": "^7.16.0",
|
||||||
"werift-rtp": "^0.8.4",
|
"werift-rtp": "^0.8.4",
|
||||||
"ws": "^8.16.0"
|
"ws": "^8.20.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.18"
|
"node": ">=20.18"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "latest",
|
||||||
"@discordjs/docgen": "^0.11.1",
|
"@discordjs/docgen": "^0.11.1",
|
||||||
"@types/debug": "^4.1.12",
|
"@types/debug": "^4.1.12",
|
||||||
"@types/node": "^22.10.7",
|
"@types/node": "^25.8.0",
|
||||||
"@types/ws": "^8.5.10",
|
"@types/ws": "^8.18.1",
|
||||||
"dtslint": "^4.2.1",
|
"patch-package": "^8.0.1",
|
||||||
"eslint": "^8.39.0",
|
"tsd": "^0.33.0",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"typescript": "^5.9.3"
|
||||||
"eslint-plugin-import": "^2.27.5",
|
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
|
||||||
"patch-package": "^8.0.0",
|
|
||||||
"prettier": "^2.8.8",
|
|
||||||
"tsd": "^0.32.0",
|
|
||||||
"tslint": "^6.1.3",
|
|
||||||
"typescript": "^5.5.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,4 +36,5 @@ exports.create = (gateway, query = {}, ...args) => {
|
|||||||
return ws;
|
return ws;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const state of ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED']) exports[state] = exports.WebSocket[state];
|
for (const state of ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'])
|
||||||
|
exports[state] = exports.WebSocket[state];
|
||||||
|
|||||||
@@ -228,7 +228,9 @@ class Client extends BaseClient {
|
|||||||
get emojis() {
|
get emojis() {
|
||||||
const emojis = new BaseGuildEmojiManager(this);
|
const emojis = new BaseGuildEmojiManager(this);
|
||||||
for (const guild of this.guilds.cache.values()) {
|
for (const guild of this.guilds.cache.values()) {
|
||||||
if (guild.available) for (const emoji of guild.emojis.cache.values()) emojis.cache.set(emoji.id, emoji);
|
if (guild.available)
|
||||||
|
for (const emoji of guild.emojis.cache.values())
|
||||||
|
emojis.cache.set(emoji.id, emoji);
|
||||||
}
|
}
|
||||||
return emojis;
|
return emojis;
|
||||||
}
|
}
|
||||||
@@ -312,7 +314,13 @@ class Client extends BaseClient {
|
|||||||
const initial = await this.api.auth.login.post({
|
const initial = await this.api.auth.login.post({
|
||||||
auth: false,
|
auth: false,
|
||||||
versioned: true,
|
versioned: true,
|
||||||
data: { gift_code_sku_id: null, login_source: null, undelete: false, login: email, password },
|
data: {
|
||||||
|
gift_code_sku_id: null,
|
||||||
|
login_source: null,
|
||||||
|
undelete: false,
|
||||||
|
login: email,
|
||||||
|
password,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if ('token' in initial) {
|
if ('token' in initial) {
|
||||||
@@ -323,7 +331,12 @@ class Client extends BaseClient {
|
|||||||
const totp = await this.api.auth.mfa.totp.post({
|
const totp = await this.api.auth.mfa.totp.post({
|
||||||
auth: false,
|
auth: false,
|
||||||
versioned: true,
|
versioned: true,
|
||||||
data: { gift_code_sku_id: null, login_source: null, code: otp, ticket: initial.ticket },
|
data: {
|
||||||
|
gift_code_sku_id: null,
|
||||||
|
login_source: null,
|
||||||
|
code: otp,
|
||||||
|
ticket: initial.ticket,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
if ('token' in totp) {
|
if ('token' in totp) {
|
||||||
return this.login(totp.token);
|
return this.login(totp.token);
|
||||||
@@ -393,7 +406,10 @@ class Client extends BaseClient {
|
|||||||
async fetchInvite(invite, options) {
|
async fetchInvite(invite, options) {
|
||||||
const code = DataResolver.resolveInviteCode(invite);
|
const code = DataResolver.resolveInviteCode(invite);
|
||||||
const data = await this.api.invites(code).get({
|
const data = await this.api.invites(code).get({
|
||||||
query: { with_counts: true, guild_scheduled_event_id: options?.guildScheduledEventId },
|
query: {
|
||||||
|
with_counts: true,
|
||||||
|
guild_scheduled_event_id: options?.guildScheduledEventId,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
return new Invite(this, data);
|
return new Invite(this, data);
|
||||||
}
|
}
|
||||||
@@ -439,7 +455,8 @@ class Client extends BaseClient {
|
|||||||
async fetchVoiceRegions() {
|
async fetchVoiceRegions() {
|
||||||
const apiRegions = await this.api.voice.regions.get();
|
const apiRegions = await this.api.voice.regions.get();
|
||||||
const regions = new Collection();
|
const regions = new Collection();
|
||||||
for (const region of apiRegions) regions.set(region.id, new VoiceRegion(region));
|
for (const region of apiRegions)
|
||||||
|
regions.set(region.id, new VoiceRegion(region));
|
||||||
return regions;
|
return regions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,7 +484,9 @@ class Client extends BaseClient {
|
|||||||
*/
|
*/
|
||||||
async fetchPremiumStickerPacks() {
|
async fetchPremiumStickerPacks() {
|
||||||
const data = await this.api('sticker-packs').get();
|
const data = await this.api('sticker-packs').get();
|
||||||
return new Collection(data.sticker_packs.map(p => [p.id, new StickerPack(this, p)]));
|
return new Collection(
|
||||||
|
data.sticker_packs.map((p) => [p.id, new StickerPack(this, p)]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* A last ditch cleanup function for garbage collection.
|
* A last ditch cleanup function for garbage collection.
|
||||||
@@ -484,7 +503,10 @@ class Client extends BaseClient {
|
|||||||
this.emit(Events.DEBUG, message);
|
this.emit(Events.DEBUG, message);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
this.emit(Events.DEBUG, `Garbage collection failed on ${name ?? 'an unknown item'}.`);
|
this.emit(
|
||||||
|
Events.DEBUG,
|
||||||
|
`Garbage collection failed on ${name ?? 'an unknown item'}.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,8 +531,13 @@ class Client extends BaseClient {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages = this.sweepers.sweepMessages(Sweepers.outdatedMessageSweepFilter(lifetime)());
|
const messages = this.sweepers.sweepMessages(
|
||||||
this.emit(Events.DEBUG, `Swept ${messages} messages older than ${lifetime} seconds`);
|
Sweepers.outdatedMessageSweepFilter(lifetime)(),
|
||||||
|
);
|
||||||
|
this.emit(
|
||||||
|
Events.DEBUG,
|
||||||
|
`Swept ${messages} messages older than ${lifetime} seconds`,
|
||||||
|
);
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,7 +572,7 @@ class Client extends BaseClient {
|
|||||||
*/
|
*/
|
||||||
async refreshAttachmentURL(...urls) {
|
async refreshAttachmentURL(...urls) {
|
||||||
// Clean up the URLs
|
// Clean up the URLs
|
||||||
urls = urls.map(url => {
|
urls = urls.map((url) => {
|
||||||
const urlObject = new URL(url);
|
const urlObject = new URL(url);
|
||||||
// Clean query
|
// Clean query
|
||||||
urlObject.search = '';
|
urlObject.search = '';
|
||||||
@@ -583,7 +610,7 @@ class Client extends BaseClient {
|
|||||||
* @returns {void} The `sleep` function is returning a Promise.
|
* @returns {void} The `sleep` function is returning a Promise.
|
||||||
*/
|
*/
|
||||||
sleep(timeout) {
|
sleep(timeout) {
|
||||||
return new Promise(r => setTimeout(r, timeout));
|
return new Promise((r) => setTimeout(r, timeout));
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
@@ -615,13 +642,18 @@ class Client extends BaseClient {
|
|||||||
* @example
|
* @example
|
||||||
* await client.acceptInvite('https://discord.gg/genshinimpact', { bypassOnboarding: true, bypassVerify: true })
|
* await client.acceptInvite('https://discord.gg/genshinimpact', { bypassOnboarding: true, bypassVerify: true })
|
||||||
*/
|
*/
|
||||||
async acceptInvite(invite, options = { bypassOnboarding: true, bypassVerify: true }) {
|
async acceptInvite(
|
||||||
|
invite,
|
||||||
|
options = { bypassOnboarding: true, bypassVerify: true },
|
||||||
|
) {
|
||||||
// ! throw new Error('METHOD_WARNING');
|
// ! throw new Error('METHOD_WARNING');
|
||||||
const code = DataResolver.resolveInviteCode(invite);
|
const code = DataResolver.resolveInviteCode(invite);
|
||||||
if (!code) throw new Error('INVITE_RESOLVE_CODE');
|
if (!code) throw new Error('INVITE_RESOLVE_CODE');
|
||||||
const i = await this.fetchInvite(code);
|
const i = await this.fetchInvite(code);
|
||||||
if (i.guild?.id && this.guilds.cache.has(i.guild?.id)) return this.guilds.cache.get(i.guild?.id);
|
if (i.guild?.id && this.guilds.cache.has(i.guild?.id))
|
||||||
if (this.channels.cache.has(i.channelId)) return this.channels.cache.get(i.channelId);
|
return this.guilds.cache.get(i.guild?.id);
|
||||||
|
if (this.channels.cache.has(i.channelId))
|
||||||
|
return this.channels.cache.get(i.channelId);
|
||||||
const data = await this.api.invites(code).post({
|
const data = await this.api.invites(code).post({
|
||||||
DiscordContext: { location: 'Markdown Link' },
|
DiscordContext: { location: 'Markdown Link' },
|
||||||
data: {
|
data: {
|
||||||
@@ -637,10 +669,11 @@ class Client extends BaseClient {
|
|||||||
return guild;
|
return guild;
|
||||||
}
|
}
|
||||||
if (options.bypassOnboarding) {
|
if (options.bypassOnboarding) {
|
||||||
const onboardingData = await this.api.guilds[i.guild?.id].onboarding.get();
|
const onboardingData =
|
||||||
|
await this.api.guilds[i.guild?.id].onboarding.get();
|
||||||
// Onboarding
|
// Onboarding
|
||||||
if (onboardingData.enabled) {
|
if (onboardingData.enabled) {
|
||||||
const prompts = onboardingData.prompts.filter(o => o.in_onboarding);
|
const prompts = onboardingData.prompts.filter((o) => o.in_onboarding);
|
||||||
if (prompts.length) {
|
if (prompts.length) {
|
||||||
const onboarding_prompts_seen = {};
|
const onboarding_prompts_seen = {};
|
||||||
const onboarding_responses = [];
|
const onboarding_responses = [];
|
||||||
@@ -648,10 +681,11 @@ class Client extends BaseClient {
|
|||||||
|
|
||||||
const currentDate = Date.now();
|
const currentDate = Date.now();
|
||||||
|
|
||||||
prompts.forEach(prompt => {
|
prompts.forEach((prompt) => {
|
||||||
onboarding_prompts_seen[prompt.id] = currentDate;
|
onboarding_prompts_seen[prompt.id] = currentDate;
|
||||||
if (prompt.required) onboarding_responses.push(prompt.options[0].id);
|
if (prompt.required)
|
||||||
prompt.options.forEach(option => {
|
onboarding_responses.push(prompt.options[0].id);
|
||||||
|
prompt.options.forEach((option) => {
|
||||||
onboarding_responses_seen[option.id] = currentDate;
|
onboarding_responses_seen[option.id] = currentDate;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -663,7 +697,10 @@ class Client extends BaseClient {
|
|||||||
onboarding_responses_seen,
|
onboarding_responses_seen,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.emit(Events.DEBUG, `[Invite > Guild ${i.guild?.id}] Bypassed onboarding`);
|
this.emit(
|
||||||
|
Events.DEBUG,
|
||||||
|
`[Invite > Guild ${i.guild?.id}] Bypassed onboarding`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -671,23 +708,36 @@ class Client extends BaseClient {
|
|||||||
if (data.show_verification_form && options.bypassVerify) {
|
if (data.show_verification_form && options.bypassVerify) {
|
||||||
// Check Guild
|
// Check Guild
|
||||||
if (i.guild.verificationLevel == 'VERY_HIGH' && !this.user.phone) {
|
if (i.guild.verificationLevel == 'VERY_HIGH' && !this.user.phone) {
|
||||||
this.emit(Events.DEBUG, `[Invite > Guild ${i.guild?.id}] Cannot bypass verify (Phone required)`);
|
this.emit(
|
||||||
|
Events.DEBUG,
|
||||||
|
`[Invite > Guild ${i.guild?.id}] Cannot bypass verify (Phone required)`,
|
||||||
|
);
|
||||||
return this.guilds.cache.get(i.guild?.id);
|
return this.guilds.cache.get(i.guild?.id);
|
||||||
}
|
}
|
||||||
if (i.guild.verificationLevel !== 'NONE' && !this.user.email) {
|
if (i.guild.verificationLevel !== 'NONE' && !this.user.email) {
|
||||||
this.emit(Events.DEBUG, `[Invite > Guild ${i.guild?.id}] Cannot bypass verify (Email required)`);
|
this.emit(
|
||||||
|
Events.DEBUG,
|
||||||
|
`[Invite > Guild ${i.guild?.id}] Cannot bypass verify (Email required)`,
|
||||||
|
);
|
||||||
return this.guilds.cache.get(i.guild?.id);
|
return this.guilds.cache.get(i.guild?.id);
|
||||||
}
|
}
|
||||||
const getForm = await this.api
|
const getForm = await this.api
|
||||||
.guilds(i.guild?.id)
|
.guilds(i.guild?.id)
|
||||||
['member-verification'].get({ query: { with_guild: false, invite_code: this.code } })
|
['member-verification'].get({
|
||||||
|
query: { with_guild: false, invite_code: this.code },
|
||||||
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
if (getForm && getForm.form_fields[0]) {
|
if (getForm && getForm.form_fields[0]) {
|
||||||
const form = Object.assign(getForm.form_fields[0], { response: true });
|
const form = Object.assign(getForm.form_fields[0], {
|
||||||
await this.api
|
response: true,
|
||||||
.guilds(i.guild?.id)
|
});
|
||||||
.requests['@me'].put({ data: { form_fields: [form], version: getForm.version } });
|
await this.api.guilds(i.guild?.id).requests['@me'].put({
|
||||||
this.emit(Events.DEBUG, `[Invite > Guild ${i.guild?.id}] Bypassed verify`);
|
data: { form_fields: [form], version: getForm.version },
|
||||||
|
});
|
||||||
|
this.emit(
|
||||||
|
Events.DEBUG,
|
||||||
|
`[Invite > Guild ${i.guild?.id}] Bypassed verify`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return guild;
|
return guild;
|
||||||
@@ -706,14 +756,21 @@ class Client extends BaseClient {
|
|||||||
redeemNitro(nitro, channel, paymentSourceId) {
|
redeemNitro(nitro, channel, paymentSourceId) {
|
||||||
if (typeof nitro !== 'string') throw new Error('INVALID_NITRO');
|
if (typeof nitro !== 'string') throw new Error('INVALID_NITRO');
|
||||||
const nitroCode =
|
const nitroCode =
|
||||||
nitro.match(/(discord.gift|discord.com|discordapp.com\/gifts)\/(\w{16,25})/) ||
|
nitro.match(
|
||||||
nitro.match(/(discord\.gift\/|discord\.com\/gifts\/|discordapp\.com\/gifts\/)(\w+)/);
|
/(discord.gift|discord.com|discordapp.com\/gifts)\/(\w{16,25})/,
|
||||||
|
) ||
|
||||||
|
nitro.match(
|
||||||
|
/(discord\.gift\/|discord\.com\/gifts\/|discordapp\.com\/gifts\/)(\w+)/,
|
||||||
|
);
|
||||||
if (!nitroCode) return false;
|
if (!nitroCode) return false;
|
||||||
const code = nitroCode[2];
|
const code = nitroCode[2];
|
||||||
channel = this.channels.resolveId(channel);
|
channel = this.channels.resolveId(channel);
|
||||||
return this.api.entitlements['gift-codes'](code).redeem.post({
|
return this.api.entitlements['gift-codes'](code).redeem.post({
|
||||||
auth: true,
|
auth: true,
|
||||||
data: { channel_id: channel || null, payment_source_id: paymentSourceId || null },
|
data: {
|
||||||
|
channel_id: channel || null,
|
||||||
|
payment_source_id: paymentSourceId || null,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -739,7 +796,11 @@ class Client extends BaseClient {
|
|||||||
authorizeURL(urlOAuth2, options = {}) {
|
authorizeURL(urlOAuth2, options = {}) {
|
||||||
// ! throw new Error('METHOD_WARNING');
|
// ! throw new Error('METHOD_WARNING');
|
||||||
const url = new URL(urlOAuth2);
|
const url = new URL(urlOAuth2);
|
||||||
if (!/^https:\/\/(?:canary\.|ptb\.)?discord\.com(?:\/api(?:\/v\d{1,2})?)?\/oauth2\/authorize\?/.test(urlOAuth2)) {
|
if (
|
||||||
|
!/^https:\/\/(?:canary\.|ptb\.)?discord\.com(?:\/api(?:\/v\d{1,2})?)?\/oauth2\/authorize\?/.test(
|
||||||
|
urlOAuth2,
|
||||||
|
)
|
||||||
|
) {
|
||||||
throw new Error('INVALID_URL', urlOAuth2);
|
throw new Error('INVALID_URL', urlOAuth2);
|
||||||
}
|
}
|
||||||
const searchParams = Object.fromEntries(url.searchParams);
|
const searchParams = Object.fromEntries(url.searchParams);
|
||||||
@@ -778,7 +839,7 @@ class Client extends BaseClient {
|
|||||||
with_guild: false,
|
with_guild: false,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then(rawData => {
|
.then((rawData) => {
|
||||||
const installTypes = rawData.integration_types_config['1'];
|
const installTypes = rawData.integration_types_config['1'];
|
||||||
if (installTypes) {
|
if (installTypes) {
|
||||||
return this.api.oauth2.authorize.post({
|
return this.api.oauth2.authorize.post({
|
||||||
@@ -808,8 +869,8 @@ class Client extends BaseClient {
|
|||||||
if (type === 'application') {
|
if (type === 'application') {
|
||||||
return this.api.oauth2.tokens
|
return this.api.oauth2.tokens
|
||||||
.get()
|
.get()
|
||||||
.then(data => data.find(o => o.application.id == id))
|
.then((data) => data.find((o) => o.application.id == id))
|
||||||
.then(o => this.api.oauth2.tokens(o.id).delete());
|
.then((o) => this.api.oauth2.tokens(o.id).delete());
|
||||||
} else {
|
} else {
|
||||||
return this.api.oauth2.tokens(id).delete();
|
return this.api.oauth2.tokens(id).delete();
|
||||||
}
|
}
|
||||||
@@ -828,7 +889,7 @@ class Client extends BaseClient {
|
|||||||
* @returns {Promise<Collection<Snowflake, AuthorizedApplicationData>>}
|
* @returns {Promise<Collection<Snowflake, AuthorizedApplicationData>>}
|
||||||
*/
|
*/
|
||||||
authorizedApplications() {
|
authorizedApplications() {
|
||||||
return this.api.oauth2.tokens.get().then(data => {
|
return this.api.oauth2.tokens.get().then((data) => {
|
||||||
const results = new Collection();
|
const results = new Collection();
|
||||||
for (const o of data) {
|
for (const o of data) {
|
||||||
const application = new Application(this, o.application);
|
const application = new Application(this, o.application);
|
||||||
@@ -864,50 +925,124 @@ class Client extends BaseClient {
|
|||||||
if (typeof options.makeCache !== 'function') {
|
if (typeof options.makeCache !== 'function') {
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'makeCache', 'a function');
|
throw new TypeError('CLIENT_INVALID_OPTION', 'makeCache', 'a function');
|
||||||
}
|
}
|
||||||
if (typeof options.messageCacheLifetime !== 'number' || isNaN(options.messageCacheLifetime)) {
|
if (
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'The messageCacheLifetime', 'a number');
|
typeof options.messageCacheLifetime !== 'number' ||
|
||||||
|
isNaN(options.messageCacheLifetime)
|
||||||
|
) {
|
||||||
|
throw new TypeError(
|
||||||
|
'CLIENT_INVALID_OPTION',
|
||||||
|
'The messageCacheLifetime',
|
||||||
|
'a number',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (typeof options.messageSweepInterval !== 'number' || isNaN(options.messageSweepInterval)) {
|
if (
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'messageSweepInterval', 'a number');
|
typeof options.messageSweepInterval !== 'number' ||
|
||||||
|
isNaN(options.messageSweepInterval)
|
||||||
|
) {
|
||||||
|
throw new TypeError(
|
||||||
|
'CLIENT_INVALID_OPTION',
|
||||||
|
'messageSweepInterval',
|
||||||
|
'a number',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (typeof options.sweepers !== 'object' || options.sweepers === null) {
|
if (typeof options.sweepers !== 'object' || options.sweepers === null) {
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'sweepers', 'an object');
|
throw new TypeError('CLIENT_INVALID_OPTION', 'sweepers', 'an object');
|
||||||
}
|
}
|
||||||
if (typeof options.invalidRequestWarningInterval !== 'number' || isNaN(options.invalidRequestWarningInterval)) {
|
if (
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'invalidRequestWarningInterval', 'a number');
|
typeof options.invalidRequestWarningInterval !== 'number' ||
|
||||||
|
isNaN(options.invalidRequestWarningInterval)
|
||||||
|
) {
|
||||||
|
throw new TypeError(
|
||||||
|
'CLIENT_INVALID_OPTION',
|
||||||
|
'invalidRequestWarningInterval',
|
||||||
|
'a number',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (!Array.isArray(options.partials)) {
|
if (!Array.isArray(options.partials)) {
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'partials', 'an Array');
|
throw new TypeError('CLIENT_INVALID_OPTION', 'partials', 'an Array');
|
||||||
}
|
}
|
||||||
if (typeof options.DMChannelVoiceStatusSync !== 'number' || isNaN(options.DMChannelVoiceStatusSync)) {
|
if (
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'DMChannelVoiceStatusSync', 'a number');
|
typeof options.DMChannelVoiceStatusSync !== 'number' ||
|
||||||
|
isNaN(options.DMChannelVoiceStatusSync)
|
||||||
|
) {
|
||||||
|
throw new TypeError(
|
||||||
|
'CLIENT_INVALID_OPTION',
|
||||||
|
'DMChannelVoiceStatusSync',
|
||||||
|
'a number',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (typeof options.waitGuildTimeout !== 'number' || isNaN(options.waitGuildTimeout)) {
|
if (
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'waitGuildTimeout', 'a number');
|
typeof options.waitGuildTimeout !== 'number' ||
|
||||||
|
isNaN(options.waitGuildTimeout)
|
||||||
|
) {
|
||||||
|
throw new TypeError(
|
||||||
|
'CLIENT_INVALID_OPTION',
|
||||||
|
'waitGuildTimeout',
|
||||||
|
'a number',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (typeof options.restWsBridgeTimeout !== 'number' || isNaN(options.restWsBridgeTimeout)) {
|
if (
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'restWsBridgeTimeout', 'a number');
|
typeof options.restWsBridgeTimeout !== 'number' ||
|
||||||
|
isNaN(options.restWsBridgeTimeout)
|
||||||
|
) {
|
||||||
|
throw new TypeError(
|
||||||
|
'CLIENT_INVALID_OPTION',
|
||||||
|
'restWsBridgeTimeout',
|
||||||
|
'a number',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (typeof options.restRequestTimeout !== 'number' || isNaN(options.restRequestTimeout)) {
|
if (
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'restRequestTimeout', 'a number');
|
typeof options.restRequestTimeout !== 'number' ||
|
||||||
|
isNaN(options.restRequestTimeout)
|
||||||
|
) {
|
||||||
|
throw new TypeError(
|
||||||
|
'CLIENT_INVALID_OPTION',
|
||||||
|
'restRequestTimeout',
|
||||||
|
'a number',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (typeof options.restGlobalRateLimit !== 'number' || isNaN(options.restGlobalRateLimit)) {
|
if (
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'restGlobalRateLimit', 'a number');
|
typeof options.restGlobalRateLimit !== 'number' ||
|
||||||
|
isNaN(options.restGlobalRateLimit)
|
||||||
|
) {
|
||||||
|
throw new TypeError(
|
||||||
|
'CLIENT_INVALID_OPTION',
|
||||||
|
'restGlobalRateLimit',
|
||||||
|
'a number',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (typeof options.restSweepInterval !== 'number' || isNaN(options.restSweepInterval)) {
|
if (
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'restSweepInterval', 'a number');
|
typeof options.restSweepInterval !== 'number' ||
|
||||||
|
isNaN(options.restSweepInterval)
|
||||||
|
) {
|
||||||
|
throw new TypeError(
|
||||||
|
'CLIENT_INVALID_OPTION',
|
||||||
|
'restSweepInterval',
|
||||||
|
'a number',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (typeof options.retryLimit !== 'number' || isNaN(options.retryLimit)) {
|
if (typeof options.retryLimit !== 'number' || isNaN(options.retryLimit)) {
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'retryLimit', 'a number');
|
throw new TypeError('CLIENT_INVALID_OPTION', 'retryLimit', 'a number');
|
||||||
}
|
}
|
||||||
if (typeof options.failIfNotExists !== 'boolean') {
|
if (typeof options.failIfNotExists !== 'boolean') {
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'failIfNotExists', 'a boolean');
|
throw new TypeError(
|
||||||
|
'CLIENT_INVALID_OPTION',
|
||||||
|
'failIfNotExists',
|
||||||
|
'a boolean',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
typeof options.rejectOnRateLimit !== 'undefined' &&
|
typeof options.rejectOnRateLimit !== 'undefined' &&
|
||||||
!(typeof options.rejectOnRateLimit === 'function' || Array.isArray(options.rejectOnRateLimit))
|
!(
|
||||||
|
typeof options.rejectOnRateLimit === 'function' ||
|
||||||
|
Array.isArray(options.rejectOnRateLimit)
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'rejectOnRateLimit', 'an array or a function');
|
throw new TypeError(
|
||||||
|
'CLIENT_INVALID_OPTION',
|
||||||
|
'rejectOnRateLimit',
|
||||||
|
'an array or a function',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (typeof options.TOTPKey === 'string') {
|
if (typeof options.TOTPKey === 'string') {
|
||||||
// Convert to base32 if not already
|
// Convert to base32 if not already
|
||||||
|
|||||||
@@ -39,7 +39,11 @@ class WebhookClient extends BaseClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
Object.defineProperty(this, 'token', { value: token, writable: true, configurable: true });
|
Object.defineProperty(this, 'token', {
|
||||||
|
value: token,
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// These are here only for documentation purposes - they are implemented by Webhook
|
// These are here only for documentation purposes - they are implemented by Webhook
|
||||||
|
|||||||
@@ -38,14 +38,20 @@ class GenericAction {
|
|||||||
if (!('recipients' in data)) {
|
if (!('recipients' in data)) {
|
||||||
// Try to resolve the recipient, but do not add the client user.
|
// Try to resolve the recipient, but do not add the client user.
|
||||||
const recipient = data.author ?? data.user ?? { id: data.user_id };
|
const recipient = data.author ?? data.user ?? { id: data.user_id };
|
||||||
if (recipient.id !== this.client.user.id) payloadData.recipients = [recipient];
|
if (recipient.id !== this.client.user.id)
|
||||||
|
payloadData.recipients = [recipient];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id !== undefined) payloadData.id = id;
|
if (id !== undefined) payloadData.id = id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
data[this.client.actions.injectedChannel] ??
|
data[this.client.actions.injectedChannel] ??
|
||||||
this.getPayload({ ...data, ...payloadData }, this.client.channels, id, PartialTypes.CHANNEL)
|
this.getPayload(
|
||||||
|
{ ...data, ...payloadData },
|
||||||
|
this.client.channels,
|
||||||
|
id,
|
||||||
|
PartialTypes.CHANNEL,
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,12 +88,20 @@ class GenericAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getMember(data, guild) {
|
getMember(data, guild) {
|
||||||
return this.getPayload(data, guild.members, data.user.id, PartialTypes.GUILD_MEMBER);
|
return this.getPayload(
|
||||||
|
data,
|
||||||
|
guild.members,
|
||||||
|
data.user.id,
|
||||||
|
PartialTypes.GUILD_MEMBER,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getUser(data) {
|
getUser(data) {
|
||||||
const id = data.user_id;
|
const id = data.user_id;
|
||||||
return data[this.client.actions.injectedUser] ?? this.getPayload({ id }, this.client.users, id, PartialTypes.USER);
|
return (
|
||||||
|
data[this.client.actions.injectedUser] ??
|
||||||
|
this.getPayload({ id }, this.client.users, id, PartialTypes.USER)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getUserFromMember(data) {
|
getUserFromMember(data) {
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ class AutoModerationActionExecutionAction extends Action {
|
|||||||
* @param {AutoModerationActionExecution} autoModerationActionExecution The data of the execution
|
* @param {AutoModerationActionExecution} autoModerationActionExecution The data of the execution
|
||||||
* @deprecated This event is not received by user accounts.
|
* @deprecated This event is not received by user accounts.
|
||||||
*/
|
*/
|
||||||
client.emit(Events.AUTO_MODERATION_ACTION_EXECUTION, new AutoModerationActionExecution(data, guild));
|
client.emit(
|
||||||
|
Events.AUTO_MODERATION_ACTION_EXECUTION,
|
||||||
|
new AutoModerationActionExecution(data, guild),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ class AutoModerationRuleUpdateAction extends Action {
|
|||||||
const guild = client.guilds.cache.get(data.guild_id);
|
const guild = client.guilds.cache.get(data.guild_id);
|
||||||
|
|
||||||
if (guild) {
|
if (guild) {
|
||||||
const oldAutoModerationRule = guild.autoModerationRules.cache.get(data.id)?._clone() ?? null;
|
const oldAutoModerationRule =
|
||||||
|
guild.autoModerationRules.cache.get(data.id)?._clone() ?? null;
|
||||||
const newAutoModerationRule = guild.autoModerationRules._add(data);
|
const newAutoModerationRule = guild.autoModerationRules._add(data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,7 +21,11 @@ class AutoModerationRuleUpdateAction extends Action {
|
|||||||
* @param {AutoModerationRule} newAutoModerationRule The auto moderation rule after the update
|
* @param {AutoModerationRule} newAutoModerationRule The auto moderation rule after the update
|
||||||
* @deprecated This event is not received by user accounts.
|
* @deprecated This event is not received by user accounts.
|
||||||
*/
|
*/
|
||||||
client.emit(Events.AUTO_MODERATION_RULE_UPDATE, oldAutoModerationRule, newAutoModerationRule);
|
client.emit(
|
||||||
|
Events.AUTO_MODERATION_RULE_UPDATE,
|
||||||
|
oldAutoModerationRule,
|
||||||
|
newAutoModerationRule,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ class ChannelUpdateAction extends Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (channel.isText() && newChannel.isText()) {
|
if (channel.isText() && newChannel.isText()) {
|
||||||
for (const [id, message] of channel.messages.cache) newChannel.messages.cache.set(id, message);
|
for (const [id, message] of channel.messages.cache)
|
||||||
|
newChannel.messages.cache.set(id, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
channel = newChannel;
|
channel = newChannel;
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ class GuildBanRemove extends Action {
|
|||||||
* @param {GuildBan} ban The ban that was removed
|
* @param {GuildBan} ban The ban that was removed
|
||||||
*/
|
*/
|
||||||
if (guild) {
|
if (guild) {
|
||||||
const ban = guild.bans.cache.get(data.user.id) ?? new GuildBan(client, data, guild);
|
const ban =
|
||||||
|
guild.bans.cache.get(data.user.id) ?? new GuildBan(client, data, guild);
|
||||||
guild.bans.cache.delete(ban.user.id);
|
guild.bans.cache.delete(ban.user.id);
|
||||||
client.emit(Events.GUILD_BAN_REMOVE, ban);
|
client.emit(Events.GUILD_BAN_REMOVE, ban);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ class GuildDeleteAction extends Action {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const channel of guild.channels.cache.values()) this.client.channels._remove(channel.id);
|
for (const channel of guild.channels.cache.values())
|
||||||
|
this.client.channels._remove(channel.id);
|
||||||
client.voice.adapters.get(data.id)?.destroy();
|
client.voice.adapters.get(data.id)?.destroy();
|
||||||
|
|
||||||
// Delete guild
|
// Delete guild
|
||||||
@@ -58,7 +59,10 @@ class GuildDeleteAction extends Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scheduleForDeletion(id) {
|
scheduleForDeletion(id) {
|
||||||
setTimeout(() => this.deleted.delete(id), this.client.options.restWsBridgeTimeout).unref();
|
setTimeout(
|
||||||
|
() => this.deleted.delete(id),
|
||||||
|
this.client.options.restWsBridgeTimeout,
|
||||||
|
).unref();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ class GuildMemberRemoveAction extends Action {
|
|||||||
* @param {GuildMember} member The member that has left/been kicked from the guild
|
* @param {GuildMember} member The member that has left/been kicked from the guild
|
||||||
* @deprecated See {@link https://github.com/aiko-chan-ai/discord.js-selfbot-v13/issues/197 this issue} for more information.
|
* @deprecated See {@link https://github.com/aiko-chan-ai/discord.js-selfbot-v13/issues/197 this issue} for more information.
|
||||||
*/
|
*/
|
||||||
if (shard.status === Status.READY) client.emit(Events.GUILD_MEMBER_REMOVE, member);
|
if (shard.status === Status.READY)
|
||||||
|
client.emit(Events.GUILD_MEMBER_REMOVE, member);
|
||||||
}
|
}
|
||||||
guild.presences.cache.delete(data.user.id);
|
guild.presences.cache.delete(data.user.id);
|
||||||
guild.voiceStates.cache.delete(data.user.id);
|
guild.voiceStates.cache.delete(data.user.id);
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ class GuildMemberUpdateAction extends Action {
|
|||||||
* @param {GuildMember} newMember The member after the update
|
* @param {GuildMember} newMember The member after the update
|
||||||
* @deprecated See {@link https://github.com/aiko-chan-ai/discord.js-selfbot-v13/issues/197 this issue} for more information.
|
* @deprecated See {@link https://github.com/aiko-chan-ai/discord.js-selfbot-v13/issues/197 this issue} for more information.
|
||||||
*/
|
*/
|
||||||
if (shard.status === Status.READY && !member.equals(old)) client.emit(Events.GUILD_MEMBER_UPDATE, old, member);
|
if (shard.status === Status.READY && !member.equals(old))
|
||||||
|
client.emit(Events.GUILD_MEMBER_UPDATE, old, member);
|
||||||
} else {
|
} else {
|
||||||
const newMember = guild.members._add(data);
|
const newMember = guild.members._add(data);
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ class GuildScheduledEventUpdateAction extends Action {
|
|||||||
const guild = client.guilds.cache.get(data.guild_id);
|
const guild = client.guilds.cache.get(data.guild_id);
|
||||||
|
|
||||||
if (guild) {
|
if (guild) {
|
||||||
const oldGuildScheduledEvent = guild.scheduledEvents.cache.get(data.id)?._clone() ?? null;
|
const oldGuildScheduledEvent =
|
||||||
|
guild.scheduledEvents.cache.get(data.id)?._clone() ?? null;
|
||||||
const newGuildScheduledEvent = guild.scheduledEvents._add(data);
|
const newGuildScheduledEvent = guild.scheduledEvents._add(data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,7 +19,11 @@ class GuildScheduledEventUpdateAction extends Action {
|
|||||||
* @param {?GuildScheduledEvent} oldGuildScheduledEvent The guild scheduled event object before the update
|
* @param {?GuildScheduledEvent} oldGuildScheduledEvent The guild scheduled event object before the update
|
||||||
* @param {GuildScheduledEvent} newGuildScheduledEvent The guild scheduled event object after the update
|
* @param {GuildScheduledEvent} newGuildScheduledEvent The guild scheduled event object after the update
|
||||||
*/
|
*/
|
||||||
client.emit(Events.GUILD_SCHEDULED_EVENT_UPDATE, oldGuildScheduledEvent, newGuildScheduledEvent);
|
client.emit(
|
||||||
|
Events.GUILD_SCHEDULED_EVENT_UPDATE,
|
||||||
|
oldGuildScheduledEvent,
|
||||||
|
newGuildScheduledEvent,
|
||||||
|
);
|
||||||
|
|
||||||
return { oldGuildScheduledEvent, newGuildScheduledEvent };
|
return { oldGuildScheduledEvent, newGuildScheduledEvent };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,11 @@ class GuildScheduledEventUserAddAction extends Action {
|
|||||||
* @param {GuildScheduledEvent} guildScheduledEvent The guild scheduled event
|
* @param {GuildScheduledEvent} guildScheduledEvent The guild scheduled event
|
||||||
* @param {User} user The user who subscribed
|
* @param {User} user The user who subscribed
|
||||||
*/
|
*/
|
||||||
client.emit(Events.GUILD_SCHEDULED_EVENT_USER_ADD, guildScheduledEvent, user);
|
client.emit(
|
||||||
|
Events.GUILD_SCHEDULED_EVENT_USER_ADD,
|
||||||
|
guildScheduledEvent,
|
||||||
|
user,
|
||||||
|
);
|
||||||
|
|
||||||
return { guildScheduledEvent, user };
|
return { guildScheduledEvent, user };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,11 @@ class GuildScheduledEventUserRemoveAction extends Action {
|
|||||||
* @param {GuildScheduledEvent} guildScheduledEvent The guild scheduled event
|
* @param {GuildScheduledEvent} guildScheduledEvent The guild scheduled event
|
||||||
* @param {User} user The user who unsubscribed
|
* @param {User} user The user who unsubscribed
|
||||||
*/
|
*/
|
||||||
client.emit(Events.GUILD_SCHEDULED_EVENT_USER_REMOVE, guildScheduledEvent, user);
|
client.emit(
|
||||||
|
Events.GUILD_SCHEDULED_EVENT_USER_REMOVE,
|
||||||
|
guildScheduledEvent,
|
||||||
|
user,
|
||||||
|
);
|
||||||
|
|
||||||
return { guildScheduledEvent, user };
|
return { guildScheduledEvent, user };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ class MessageCreateAction extends Action {
|
|||||||
if (!channel.isText()) return {};
|
if (!channel.isText()) return {};
|
||||||
|
|
||||||
const existing = channel.messages.cache.get(data.id);
|
const existing = channel.messages.cache.get(data.id);
|
||||||
if (existing && existing.author?.id !== this.client.user.id) return { message: existing };
|
if (existing && existing.author?.id !== this.client.user.id)
|
||||||
|
return { message: existing };
|
||||||
const message = existing ?? channel.messages._add(data);
|
const message = existing ?? channel.messages._add(data);
|
||||||
channel.lastMessageId = data.id;
|
channel.lastMessageId = data.id;
|
||||||
|
|
||||||
@@ -37,7 +38,10 @@ class MessageCreateAction extends Action {
|
|||||||
*/
|
*/
|
||||||
if (client.emit('message', message) && !deprecationEmitted) {
|
if (client.emit('message', message) && !deprecationEmitted) {
|
||||||
deprecationEmitted = true;
|
deprecationEmitted = true;
|
||||||
process.emitWarning('The message event is deprecated. Use messageCreate instead', 'DeprecationWarning');
|
process.emitWarning(
|
||||||
|
'The message event is deprecated. Use messageCreate instead',
|
||||||
|
'DeprecationWarning',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { message };
|
return { message };
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ const { Events } = require('../../util/Constants');
|
|||||||
class MessageDeleteAction extends Action {
|
class MessageDeleteAction extends Action {
|
||||||
handle(data) {
|
handle(data) {
|
||||||
const client = this.client;
|
const client = this.client;
|
||||||
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
|
const channel = this.getChannel({
|
||||||
|
id: data.channel_id,
|
||||||
|
...('guild_id' in data && { guild_id: data.guild_id }),
|
||||||
|
});
|
||||||
let message;
|
let message;
|
||||||
if (channel) {
|
if (channel) {
|
||||||
if (!channel.isText()) return {};
|
if (!channel.isText()) return {};
|
||||||
|
|||||||
@@ -35,7 +35,9 @@ class MessageReactionAdd extends Action {
|
|||||||
if (!message) return false;
|
if (!message) return false;
|
||||||
|
|
||||||
// Verify reaction
|
// Verify reaction
|
||||||
const includePartial = this.client.options.partials.includes(PartialTypes.REACTION);
|
const includePartial = this.client.options.partials.includes(
|
||||||
|
PartialTypes.REACTION,
|
||||||
|
);
|
||||||
if (message.partial && !includePartial) return false;
|
if (message.partial && !includePartial) return false;
|
||||||
const reaction = message.reactions._add({
|
const reaction = message.reactions._add({
|
||||||
emoji: data.emoji,
|
emoji: data.emoji,
|
||||||
@@ -59,7 +61,10 @@ class MessageReactionAdd extends Action {
|
|||||||
* @param {User} user The user that applied the guild or reaction emoji
|
* @param {User} user The user that applied the guild or reaction emoji
|
||||||
* @param {MessageReactionEventDetails} details Details of adding the reaction
|
* @param {MessageReactionEventDetails} details Details of adding the reaction
|
||||||
*/
|
*/
|
||||||
this.client.emit(Events.MESSAGE_REACTION_ADD, reaction, user, { type: data.type, burst: data.burst });
|
this.client.emit(Events.MESSAGE_REACTION_ADD, reaction, user, {
|
||||||
|
type: data.type,
|
||||||
|
burst: data.burst,
|
||||||
|
});
|
||||||
|
|
||||||
return { message, reaction, user };
|
return { message, reaction, user };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,10 @@ class MessageReactionRemove extends Action {
|
|||||||
* @param {User} user The user whose emoji or reaction emoji was removed
|
* @param {User} user The user whose emoji or reaction emoji was removed
|
||||||
* @param {MessageReactionEventDetails} details Details of removing the reaction
|
* @param {MessageReactionEventDetails} details Details of removing the reaction
|
||||||
*/
|
*/
|
||||||
this.client.emit(Events.MESSAGE_REACTION_REMOVE, reaction, user, { type: data.type, burst: data.burst });
|
this.client.emit(Events.MESSAGE_REACTION_REMOVE, reaction, user, {
|
||||||
|
type: data.type,
|
||||||
|
burst: data.burst,
|
||||||
|
});
|
||||||
|
|
||||||
return { message, reaction, user };
|
return { message, reaction, user };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ const { Events } = require('../../util/Constants');
|
|||||||
class MessageReactionRemoveAll extends Action {
|
class MessageReactionRemoveAll extends Action {
|
||||||
handle(data) {
|
handle(data) {
|
||||||
// Verify channel
|
// Verify channel
|
||||||
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
|
const channel = this.getChannel({
|
||||||
|
id: data.channel_id,
|
||||||
|
...('guild_id' in data && { guild_id: data.guild_id }),
|
||||||
|
});
|
||||||
if (!channel || !channel.isText()) return false;
|
if (!channel || !channel.isText()) return false;
|
||||||
|
|
||||||
// Verify message
|
// Verify message
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ const { Events } = require('../../util/Constants');
|
|||||||
|
|
||||||
class MessageReactionRemoveEmoji extends Action {
|
class MessageReactionRemoveEmoji extends Action {
|
||||||
handle(data) {
|
handle(data) {
|
||||||
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
|
const channel = this.getChannel({
|
||||||
|
id: data.channel_id,
|
||||||
|
...('guild_id' in data && { guild_id: data.guild_id }),
|
||||||
|
});
|
||||||
if (!channel || !channel.isText()) return false;
|
if (!channel || !channel.isText()) return false;
|
||||||
|
|
||||||
const message = this.getMessage(data, channel);
|
const message = this.getMessage(data, channel);
|
||||||
@@ -13,7 +16,8 @@ class MessageReactionRemoveEmoji extends Action {
|
|||||||
|
|
||||||
const reaction = this.getReaction(data, message);
|
const reaction = this.getReaction(data, message);
|
||||||
if (!reaction) return false;
|
if (!reaction) return false;
|
||||||
if (!message.partial) message.reactions.cache.delete(reaction.emoji.id ?? reaction.emoji.name);
|
if (!message.partial)
|
||||||
|
message.reactions.cache.delete(reaction.emoji.id ?? reaction.emoji.name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emitted when a bot removes an emoji reaction from a cached message.
|
* Emitted when a bot removes an emoji reaction from a cached message.
|
||||||
|
|||||||
@@ -4,12 +4,18 @@ const Action = require('./Action');
|
|||||||
|
|
||||||
class MessageUpdateAction extends Action {
|
class MessageUpdateAction extends Action {
|
||||||
handle(data) {
|
handle(data) {
|
||||||
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
|
const channel = this.getChannel({
|
||||||
|
id: data.channel_id,
|
||||||
|
...('guild_id' in data && { guild_id: data.guild_id }),
|
||||||
|
});
|
||||||
if (channel) {
|
if (channel) {
|
||||||
if (!channel.isText()) return {};
|
if (!channel.isText()) return {};
|
||||||
|
|
||||||
const { id, channel_id, guild_id, author, timestamp, type } = data;
|
const { id, channel_id, guild_id, author, timestamp, type } = data;
|
||||||
const message = this.getMessage({ id, channel_id, guild_id, author, timestamp, type }, channel);
|
const message = this.getMessage(
|
||||||
|
{ id, channel_id, guild_id, author, timestamp, type },
|
||||||
|
channel,
|
||||||
|
);
|
||||||
if (message) {
|
if (message) {
|
||||||
const old = message._update(data);
|
const old = message._update(data);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -8,13 +8,18 @@ class PresenceUpdateAction extends Action {
|
|||||||
handle(data) {
|
handle(data) {
|
||||||
let user = this.client.users.cache.get(data.user.id);
|
let user = this.client.users.cache.get(data.user.id);
|
||||||
if (!user && data.user?.username) user = this.client.users._add(data.user);
|
if (!user && data.user?.username) user = this.client.users._add(data.user);
|
||||||
if (!user && ('username' in data.user || this.client.options.partials.includes(PartialTypes.USER))) {
|
if (
|
||||||
|
!user &&
|
||||||
|
('username' in data.user ||
|
||||||
|
this.client.options.partials.includes(PartialTypes.USER))
|
||||||
|
) {
|
||||||
user = this.client.users._add(data.user);
|
user = this.client.users._add(data.user);
|
||||||
}
|
}
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
|
|
||||||
if (data.user?.username) {
|
if (data.user?.username) {
|
||||||
if (!user._equals(data.user)) this.client.actions.UserUpdate.handle(data.user);
|
if (!user._equals(data.user))
|
||||||
|
this.client.actions.UserUpdate.handle(data.user);
|
||||||
}
|
}
|
||||||
|
|
||||||
const guild = this.client.guilds.cache.get(data.guild_id);
|
const guild = this.client.guilds.cache.get(data.guild_id);
|
||||||
@@ -31,11 +36,17 @@ class PresenceUpdateAction extends Action {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldPresence = (guild || this.client).presences.cache.get(user.id)?._clone() ?? null;
|
const oldPresence =
|
||||||
|
(guild || this.client).presences.cache.get(user.id)?._clone() ?? null;
|
||||||
|
|
||||||
const newPresence = (guild || this.client).presences._add(Object.assign(data, { guild }));
|
const newPresence = (guild || this.client).presences._add(
|
||||||
|
Object.assign(data, { guild }),
|
||||||
|
);
|
||||||
|
|
||||||
if (this.client.listenerCount(Events.PRESENCE_UPDATE) && !newPresence.equals(oldPresence)) {
|
if (
|
||||||
|
this.client.listenerCount(Events.PRESENCE_UPDATE) &&
|
||||||
|
!newPresence.equals(oldPresence)
|
||||||
|
) {
|
||||||
/**
|
/**
|
||||||
* Emitted whenever a guild member's presence (e.g. status, activity) is changed.
|
* Emitted whenever a guild member's presence (e.g. status, activity) is changed.
|
||||||
* @event Client#presenceUpdate
|
* @event Client#presenceUpdate
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ const { Events } = require('../../util/Constants');
|
|||||||
class StageInstanceCreateAction extends Action {
|
class StageInstanceCreateAction extends Action {
|
||||||
handle(data) {
|
handle(data) {
|
||||||
const client = this.client;
|
const client = this.client;
|
||||||
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
|
const channel = this.getChannel({
|
||||||
|
id: data.channel_id,
|
||||||
|
guild_id: data.guild_id,
|
||||||
|
});
|
||||||
|
|
||||||
if (channel) {
|
if (channel) {
|
||||||
const stageInstance = channel.guild.stageInstances._add(data);
|
const stageInstance = channel.guild.stageInstances._add(data);
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ const { Events } = require('../../util/Constants');
|
|||||||
class StageInstanceDeleteAction extends Action {
|
class StageInstanceDeleteAction extends Action {
|
||||||
handle(data) {
|
handle(data) {
|
||||||
const client = this.client;
|
const client = this.client;
|
||||||
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
|
const channel = this.getChannel({
|
||||||
|
id: data.channel_id,
|
||||||
|
guild_id: data.guild_id,
|
||||||
|
});
|
||||||
|
|
||||||
if (channel) {
|
if (channel) {
|
||||||
const stageInstance = channel.guild.stageInstances._add(data);
|
const stageInstance = channel.guild.stageInstances._add(data);
|
||||||
|
|||||||
@@ -6,10 +6,14 @@ const { Events } = require('../../util/Constants');
|
|||||||
class StageInstanceUpdateAction extends Action {
|
class StageInstanceUpdateAction extends Action {
|
||||||
handle(data) {
|
handle(data) {
|
||||||
const client = this.client;
|
const client = this.client;
|
||||||
const channel = this.getChannel({ id: data.channel_id, guild_id: data.guild_id });
|
const channel = this.getChannel({
|
||||||
|
id: data.channel_id,
|
||||||
|
guild_id: data.guild_id,
|
||||||
|
});
|
||||||
|
|
||||||
if (channel) {
|
if (channel) {
|
||||||
const oldStageInstance = channel.guild.stageInstances.cache.get(data.id)?._clone() ?? null;
|
const oldStageInstance =
|
||||||
|
channel.guild.stageInstances.cache.get(data.id)?._clone() ?? null;
|
||||||
const newStageInstance = channel.guild.stageInstances._add(data);
|
const newStageInstance = channel.guild.stageInstances._add(data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,7 +22,11 @@ class StageInstanceUpdateAction extends Action {
|
|||||||
* @param {?StageInstance} oldStageInstance The stage instance before the update
|
* @param {?StageInstance} oldStageInstance The stage instance before the update
|
||||||
* @param {StageInstance} newStageInstance The stage instance after the update
|
* @param {StageInstance} newStageInstance The stage instance after the update
|
||||||
*/
|
*/
|
||||||
client.emit(Events.STAGE_INSTANCE_UPDATE, oldStageInstance, newStageInstance);
|
client.emit(
|
||||||
|
Events.STAGE_INSTANCE_UPDATE,
|
||||||
|
oldStageInstance,
|
||||||
|
newStageInstance,
|
||||||
|
);
|
||||||
|
|
||||||
return { oldStageInstance, newStageInstance };
|
return { oldStageInstance, newStageInstance };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class ThreadListSyncAction extends Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeStale(channel) {
|
removeStale(channel) {
|
||||||
channel.threads?.cache.forEach(thread => {
|
channel.threads?.cache.forEach((thread) => {
|
||||||
if (!thread.archived) {
|
if (!thread.archived) {
|
||||||
this.client.channels._remove(thread.id);
|
this.client.channels._remove(thread.id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ class ThreadMembersUpdateAction extends Action {
|
|||||||
const old = thread.members.cache.clone();
|
const old = thread.members.cache.clone();
|
||||||
thread.memberCount = data.member_count;
|
thread.memberCount = data.member_count;
|
||||||
|
|
||||||
data.added_members?.forEach(rawMember => {
|
data.added_members?.forEach((rawMember) => {
|
||||||
thread.members._add(rawMember);
|
thread.members._add(rawMember);
|
||||||
});
|
});
|
||||||
|
|
||||||
data.removed_member_ids?.forEach(memberId => {
|
data.removed_member_ids?.forEach((memberId) => {
|
||||||
thread.members.cache.delete(memberId);
|
thread.members.cache.delete(memberId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,17 @@ const { Events } = require('../../util/Constants');
|
|||||||
|
|
||||||
class TypingStart extends Action {
|
class TypingStart extends Action {
|
||||||
handle(data) {
|
handle(data) {
|
||||||
const channel = this.getChannel({ id: data.channel_id, ...('guild_id' in data && { guild_id: data.guild_id }) });
|
const channel = this.getChannel({
|
||||||
|
id: data.channel_id,
|
||||||
|
...('guild_id' in data && { guild_id: data.guild_id }),
|
||||||
|
});
|
||||||
if (!channel) return;
|
if (!channel) return;
|
||||||
|
|
||||||
if (!channel.isText()) {
|
if (!channel.isText()) {
|
||||||
this.client.emit(Events.WARN, `Discord sent a typing packet to a ${channel.type} channel ${channel.id}`);
|
this.client.emit(
|
||||||
|
Events.WARN,
|
||||||
|
`Discord sent a typing packet to a ${channel.type} channel ${channel.id}`,
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ class UserUpdateAction extends Action {
|
|||||||
handle(data) {
|
handle(data) {
|
||||||
const client = this.client;
|
const client = this.client;
|
||||||
|
|
||||||
const newUser = data.id === client.user.id ? client.user : client.users.cache.get(data.id);
|
const newUser =
|
||||||
|
data.id === client.user.id
|
||||||
|
? client.user
|
||||||
|
: client.users.cache.get(data.id);
|
||||||
const oldUser = newUser._update(data);
|
const oldUser = newUser._update(data);
|
||||||
|
|
||||||
if (!oldUser.equals(newUser)) {
|
if (!oldUser.equals(newUser)) {
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ class VoiceStateUpdate extends Action {
|
|||||||
if (guild) {
|
if (guild) {
|
||||||
// Update the state
|
// Update the state
|
||||||
const oldState =
|
const oldState =
|
||||||
guild.voiceStates.cache.get(data.user_id)?._clone() ?? new VoiceState(guild, { user_id: data.user_id });
|
guild.voiceStates.cache.get(data.user_id)?._clone() ??
|
||||||
|
new VoiceState(guild, { user_id: data.user_id });
|
||||||
|
|
||||||
const newState = guild.voiceStates._add(data);
|
const newState = guild.voiceStates._add(data);
|
||||||
|
|
||||||
@@ -33,7 +34,8 @@ class VoiceStateUpdate extends Action {
|
|||||||
} else {
|
} else {
|
||||||
// Update the state
|
// Update the state
|
||||||
const oldState =
|
const oldState =
|
||||||
client.voiceStates.cache.get(data.user_id)?._clone() ?? new VoiceState({ client }, { user_id: data.user_id });
|
client.voiceStates.cache.get(data.user_id)?._clone() ??
|
||||||
|
new VoiceState({ client }, { user_id: data.user_id });
|
||||||
|
|
||||||
const newState = client.voiceStates._add(data);
|
const newState = client.voiceStates._add(data);
|
||||||
|
|
||||||
@@ -41,7 +43,10 @@ class VoiceStateUpdate extends Action {
|
|||||||
}
|
}
|
||||||
// Emit event
|
// Emit event
|
||||||
if (data.user_id === client.user?.id) {
|
if (data.user_id === client.user?.id) {
|
||||||
client.emit('debug', `[VOICE] received voice state update: ${JSON.stringify(data)}`);
|
client.emit(
|
||||||
|
'debug',
|
||||||
|
`[VOICE] received voice state update: ${JSON.stringify(data)}`,
|
||||||
|
);
|
||||||
client.voice.onVoiceStateUpdate(data);
|
client.voice.onVoiceStateUpdate(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,14 +61,25 @@ class ClientVoiceManager {
|
|||||||
onVoiceStateUpdate(payload) {
|
onVoiceStateUpdate(payload) {
|
||||||
const { guild_id, session_id, channel_id } = payload;
|
const { guild_id, session_id, channel_id } = payload;
|
||||||
// @discordjs/voice
|
// @discordjs/voice
|
||||||
if (payload.guild_id && payload.session_id && payload.user_id === this.client.user?.id) {
|
if (
|
||||||
|
payload.guild_id &&
|
||||||
|
payload.session_id &&
|
||||||
|
payload.user_id === this.client.user?.id
|
||||||
|
) {
|
||||||
this.adapters.get(payload.guild_id)?.onVoiceStateUpdate(payload);
|
this.adapters.get(payload.guild_id)?.onVoiceStateUpdate(payload);
|
||||||
} else if (payload.channel_id && payload.session_id && payload.user_id === this.client.user?.id) {
|
} else if (
|
||||||
|
payload.channel_id &&
|
||||||
|
payload.session_id &&
|
||||||
|
payload.user_id === this.client.user?.id
|
||||||
|
) {
|
||||||
this.adapters.get(payload.channel_id)?.onVoiceStateUpdate(payload);
|
this.adapters.get(payload.channel_id)?.onVoiceStateUpdate(payload);
|
||||||
}
|
}
|
||||||
// Main lib
|
// Main lib
|
||||||
const connection = this.connection;
|
const connection = this.connection;
|
||||||
this.client.emit('debug', `[VOICE] connection? ${!!connection}, ${guild_id} ${session_id} ${channel_id}`);
|
this.client.emit(
|
||||||
|
'debug',
|
||||||
|
`[VOICE] connection? ${!!connection}, ${guild_id} ${session_id} ${channel_id}`,
|
||||||
|
);
|
||||||
if (!connection) return;
|
if (!connection) return;
|
||||||
if (!channel_id) {
|
if (!channel_id) {
|
||||||
connection._disconnect();
|
connection._disconnect();
|
||||||
@@ -80,7 +91,10 @@ class ClientVoiceManager {
|
|||||||
connection.channel = channel;
|
connection.channel = channel;
|
||||||
connection.setSessionId(session_id);
|
connection.setSessionId(session_id);
|
||||||
} else {
|
} else {
|
||||||
this.client.emit('debug', `[VOICE] disconnecting from guild ${guild_id} as channel ${channel_id} is uncached`);
|
this.client.emit(
|
||||||
|
'debug',
|
||||||
|
`[VOICE] disconnecting from guild ${guild_id} as channel ${channel_id} is uncached`,
|
||||||
|
);
|
||||||
connection.disconnect();
|
connection.disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,8 +131,11 @@ class ClientVoiceManager {
|
|||||||
} else {
|
} else {
|
||||||
connection = new VoiceConnection(this, channel);
|
connection = new VoiceConnection(this, channel);
|
||||||
if (config?.videoCodec) connection.setVideoCodec(config.videoCodec);
|
if (config?.videoCodec) connection.setVideoCodec(config.videoCodec);
|
||||||
connection.on('debug', msg =>
|
connection.on('debug', (msg) =>
|
||||||
this.client.emit('debug', `[VOICE (${channel.guild?.id || channel.id}:${connection.status})]: ${msg}`),
|
this.client.emit(
|
||||||
|
'debug',
|
||||||
|
`[VOICE (${channel.guild?.id || channel.id}:${connection.status})]: ${msg}`,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
connection.authenticate({
|
connection.authenticate({
|
||||||
self_mute: Boolean(config.selfMute),
|
self_mute: Boolean(config.selfMute),
|
||||||
@@ -128,7 +145,7 @@ class ClientVoiceManager {
|
|||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.once('failed', reason => {
|
connection.once('failed', (reason) => {
|
||||||
this.connection = null;
|
this.connection = null;
|
||||||
reject(reason);
|
reject(reason);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,12 @@ const { parseStreamKey } = require('./util/Function');
|
|||||||
const PlayInterface = require('./util/PlayInterface');
|
const PlayInterface = require('./util/PlayInterface');
|
||||||
const Silence = require('./util/Silence');
|
const Silence = require('./util/Silence');
|
||||||
const { Error } = require('../../errors');
|
const { Error } = require('../../errors');
|
||||||
const { Opcodes, VoiceOpcodes, VoiceStatus, Events } = require('../../util/Constants');
|
const {
|
||||||
|
Opcodes,
|
||||||
|
VoiceOpcodes,
|
||||||
|
VoiceStatus,
|
||||||
|
Events,
|
||||||
|
} = require('../../util/Constants');
|
||||||
const Speaking = require('../../util/Speaking');
|
const Speaking = require('../../util/Speaking');
|
||||||
const Util = require('../../util/Util');
|
const Util = require('../../util/Util');
|
||||||
|
|
||||||
@@ -90,9 +95,12 @@ class VoiceConnection extends EventEmitter {
|
|||||||
* The audio player for this voice connection
|
* The audio player for this voice connection
|
||||||
* @type {MediaPlayer}
|
* @type {MediaPlayer}
|
||||||
*/
|
*/
|
||||||
this.player = new MediaPlayer(this, this.constructor.name === 'StreamConnection');
|
this.player = new MediaPlayer(
|
||||||
|
this,
|
||||||
|
this.constructor.name === 'StreamConnection',
|
||||||
|
);
|
||||||
|
|
||||||
this.player.on('debug', m => {
|
this.player.on('debug', (m) => {
|
||||||
/**
|
/**
|
||||||
* Debug info from the connection.
|
* Debug info from the connection.
|
||||||
* @event VoiceConnection#debug
|
* @event VoiceConnection#debug
|
||||||
@@ -101,7 +109,7 @@ class VoiceConnection extends EventEmitter {
|
|||||||
this.emit('debug', `media player - ${m}`);
|
this.emit('debug', `media player - ${m}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.player.on('error', e => {
|
this.player.on('error', (e) => {
|
||||||
/**
|
/**
|
||||||
* Warning info from the connection.
|
* Warning info from the connection.
|
||||||
* @event VoiceConnection#warn
|
* @event VoiceConnection#warn
|
||||||
@@ -212,7 +220,7 @@ class VoiceConnection extends EventEmitter {
|
|||||||
ssrc: this.authentication.ssrc,
|
ssrc: this.authentication.ssrc,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch((e) => {
|
||||||
this.emit('debug', e);
|
this.emit('debug', e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -223,7 +231,8 @@ class VoiceConnection extends EventEmitter {
|
|||||||
* @returns {VoiceConnection}
|
* @returns {VoiceConnection}
|
||||||
*/
|
*/
|
||||||
setVideoCodec(value) {
|
setVideoCodec(value) {
|
||||||
if (!SUPPORTED_CODECS.includes(value)) throw new Error('INVALID_VIDEO_CODEC', SUPPORTED_CODECS);
|
if (!SUPPORTED_CODECS.includes(value))
|
||||||
|
throw new Error('INVALID_VIDEO_CODEC', SUPPORTED_CODECS);
|
||||||
this.videoCodec = value;
|
this.videoCodec = value;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -247,7 +256,7 @@ class VoiceConnection extends EventEmitter {
|
|||||||
streams: [],
|
streams: [],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch((e) => {
|
||||||
this.emit('debug', e);
|
this.emit('debug', e);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -277,7 +286,7 @@ class VoiceConnection extends EventEmitter {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch((e) => {
|
||||||
this.emit('debug', e);
|
this.emit('debug', e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -310,7 +319,10 @@ class VoiceConnection extends EventEmitter {
|
|||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.emit('debug', `Sending voice state update: ${JSON.stringify(options)}`);
|
this.emit(
|
||||||
|
'debug',
|
||||||
|
`Sending voice state update: ${JSON.stringify(options)}`,
|
||||||
|
);
|
||||||
|
|
||||||
return this.channel.client.ws.broadcast({
|
return this.channel.client.ws.broadcast({
|
||||||
op: Opcodes.VOICE_STATE_UPDATE,
|
op: Opcodes.VOICE_STATE_UPDATE,
|
||||||
@@ -349,7 +361,10 @@ class VoiceConnection extends EventEmitter {
|
|||||||
this.authentication.token = token;
|
this.authentication.token = token;
|
||||||
this.authentication.endpoint = endpoint;
|
this.authentication.endpoint = endpoint;
|
||||||
this.checkAuthenticated();
|
this.checkAuthenticated();
|
||||||
} else if (token !== this.authentication.token || endpoint !== this.authentication.endpoint) {
|
} else if (
|
||||||
|
token !== this.authentication.token ||
|
||||||
|
endpoint !== this.authentication.endpoint
|
||||||
|
) {
|
||||||
this.reconnect(token, endpoint);
|
this.reconnect(token, endpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,7 +375,10 @@ class VoiceConnection extends EventEmitter {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
setSessionId(sessionId) {
|
setSessionId(sessionId) {
|
||||||
this.emit('debug', `Setting sessionId ${sessionId} (stored as "${this.authentication.sessionId}")`);
|
this.emit(
|
||||||
|
'debug',
|
||||||
|
`Setting sessionId ${sessionId} (stored as "${this.authentication.sessionId}")`,
|
||||||
|
);
|
||||||
if (!sessionId) {
|
if (!sessionId) {
|
||||||
this.authenticateFailed('VOICE_SESSION_ABSENT');
|
this.authenticateFailed('VOICE_SESSION_ABSENT');
|
||||||
return;
|
return;
|
||||||
@@ -441,7 +459,10 @@ class VoiceConnection extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
authenticate(options = {}) {
|
authenticate(options = {}) {
|
||||||
this.sendVoiceStateUpdate(options);
|
this.sendVoiceStateUpdate(options);
|
||||||
this.connectTimeout = setTimeout(() => this.authenticateFailed('VOICE_CONNECTION_TIMEOUT'), 15_000).unref();
|
this.connectTimeout = setTimeout(
|
||||||
|
() => this.authenticateFailed('VOICE_CONNECTION_TIMEOUT'),
|
||||||
|
15_000,
|
||||||
|
).unref();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -537,10 +558,10 @@ class VoiceConnection extends EventEmitter {
|
|||||||
|
|
||||||
const { ws, udp } = this.sockets;
|
const { ws, udp } = this.sockets;
|
||||||
|
|
||||||
ws.on('debug', msg => this.emit('debug', msg));
|
ws.on('debug', (msg) => this.emit('debug', msg));
|
||||||
udp.on('debug', msg => this.emit('debug', msg));
|
udp.on('debug', (msg) => this.emit('debug', msg));
|
||||||
ws.on('error', err => this.emit('error', err));
|
ws.on('error', (err) => this.emit('error', err));
|
||||||
udp.on('error', err => this.emit('error', err));
|
udp.on('error', (err) => this.emit('error', err));
|
||||||
ws.on('ready', this.onReady.bind(this));
|
ws.on('ready', this.onReady.bind(this));
|
||||||
ws.on('sessionDescription', this.onSessionDescription.bind(this));
|
ws.on('sessionDescription', this.onSessionDescription.bind(this));
|
||||||
ws.on('startSpeaking', this.onStartSpeaking.bind(this));
|
ws.on('startSpeaking', this.onStartSpeaking.bind(this));
|
||||||
@@ -576,7 +597,10 @@ class VoiceConnection extends EventEmitter {
|
|||||||
this.status = VoiceStatus.CONNECTED;
|
this.status = VoiceStatus.CONNECTED;
|
||||||
const ready = () => {
|
const ready = () => {
|
||||||
clearTimeout(this.connectTimeout);
|
clearTimeout(this.connectTimeout);
|
||||||
this.emit('debug', `Ready with authentication details: ${JSON.stringify(this.authentication)}`);
|
this.emit(
|
||||||
|
'debug',
|
||||||
|
`Ready with authentication details: ${JSON.stringify(this.authentication)}`,
|
||||||
|
);
|
||||||
/**
|
/**
|
||||||
* Emitted once the connection is ready, when a promise to join a voice channel resolves,
|
* Emitted once the connection is ready, when a promise to join a voice channel resolves,
|
||||||
* the connection will already be ready.
|
* the connection will already be ready.
|
||||||
@@ -588,7 +612,10 @@ class VoiceConnection extends EventEmitter {
|
|||||||
ready();
|
ready();
|
||||||
} else {
|
} else {
|
||||||
// This serves to provide support for voice receive, sending audio is required to receive it.
|
// This serves to provide support for voice receive, sending audio is required to receive it.
|
||||||
const dispatcher = this.playAudio(new SingleSilence(), { type: 'opus', volume: false });
|
const dispatcher = this.playAudio(new SingleSilence(), {
|
||||||
|
type: 'opus',
|
||||||
|
volume: false,
|
||||||
|
});
|
||||||
dispatcher.once('finish', ready);
|
dispatcher.once('finish', ready);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -678,13 +705,22 @@ class VoiceConnection extends EventEmitter {
|
|||||||
if (this.streamConnection) {
|
if (this.streamConnection) {
|
||||||
return resolve(this.streamConnection);
|
return resolve(this.streamConnection);
|
||||||
} else {
|
} else {
|
||||||
const connection = (this.streamConnection = new StreamConnection(this.voiceManager, this.channel, this));
|
const connection = (this.streamConnection = new StreamConnection(
|
||||||
|
this.voiceManager,
|
||||||
|
this.channel,
|
||||||
|
this,
|
||||||
|
));
|
||||||
connection.setVideoCodec(this.videoCodec); // Sync :?
|
connection.setVideoCodec(this.videoCodec); // Sync :?
|
||||||
// Setup event...
|
// Setup event...
|
||||||
if (!this.eventHook) {
|
if (!this.eventHook) {
|
||||||
this.eventHook = true; // Dont listen this event two times :/
|
this.eventHook = true; // Dont listen this event two times :/
|
||||||
this.channel.client.on('raw', packet => {
|
this.channel.client.on('raw', (packet) => {
|
||||||
if (typeof packet !== 'object' || !packet.t || !packet.d || !packet.d?.stream_key) {
|
if (
|
||||||
|
typeof packet !== 'object' ||
|
||||||
|
!packet.t ||
|
||||||
|
!packet.d ||
|
||||||
|
!packet.d?.stream_key
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { t: event, d: data } = packet;
|
const { t: event, d: data } = packet;
|
||||||
@@ -697,12 +733,17 @@ class VoiceConnection extends EventEmitter {
|
|||||||
// Current user stream
|
// Current user stream
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case 'STREAM_CREATE': {
|
case 'STREAM_CREATE': {
|
||||||
this.streamConnection.setSessionId(this.authentication.sessionId);
|
this.streamConnection.setSessionId(
|
||||||
|
this.authentication.sessionId,
|
||||||
|
);
|
||||||
this.streamConnection.serverId = data.rtc_server_id;
|
this.streamConnection.serverId = data.rtc_server_id;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'STREAM_SERVER_UPDATE': {
|
case 'STREAM_SERVER_UPDATE': {
|
||||||
this.streamConnection.setTokenAndEndpoint(data.token, data.endpoint);
|
this.streamConnection.setTokenAndEndpoint(
|
||||||
|
data.token,
|
||||||
|
data.endpoint,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'STREAM_DELETE': {
|
case 'STREAM_DELETE': {
|
||||||
@@ -715,8 +756,13 @@ class VoiceConnection extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.streamWatchConnection.has(StreamKey.userId) && this.channel.id == StreamKey.channelId) {
|
if (
|
||||||
const streamConnection = this.streamWatchConnection.get(StreamKey.userId);
|
this.streamWatchConnection.has(StreamKey.userId) &&
|
||||||
|
this.channel.id == StreamKey.channelId
|
||||||
|
) {
|
||||||
|
const streamConnection = this.streamWatchConnection.get(
|
||||||
|
StreamKey.userId,
|
||||||
|
);
|
||||||
// Watch user stream
|
// Watch user stream
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case 'STREAM_CREATE': {
|
case 'STREAM_CREATE': {
|
||||||
@@ -725,7 +771,10 @@ class VoiceConnection extends EventEmitter {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'STREAM_SERVER_UPDATE': {
|
case 'STREAM_SERVER_UPDATE': {
|
||||||
streamConnection.setTokenAndEndpoint(data.token, data.endpoint);
|
streamConnection.setTokenAndEndpoint(
|
||||||
|
data.token,
|
||||||
|
data.endpoint,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'STREAM_DELETE': {
|
case 'STREAM_DELETE': {
|
||||||
@@ -745,13 +794,13 @@ class VoiceConnection extends EventEmitter {
|
|||||||
connection.sendSignalScreenshare();
|
connection.sendSignalScreenshare();
|
||||||
connection.sendScreenshareState(true);
|
connection.sendScreenshareState(true);
|
||||||
|
|
||||||
connection.on('debug', msg =>
|
connection.on('debug', (msg) =>
|
||||||
this.channel.client.emit(
|
this.channel.client.emit(
|
||||||
'debug',
|
'debug',
|
||||||
`[VOICE STREAM (${this.channel.guild?.id || this.channel.id}:${connection.status})]: ${msg}`,
|
`[VOICE STREAM (${this.channel.guild?.id || this.channel.id}:${connection.status})]: ${msg}`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
connection.once('failed', reason => {
|
connection.once('failed', (reason) => {
|
||||||
this.streamConnection = null;
|
this.streamConnection = null;
|
||||||
reject(reason);
|
reject(reason);
|
||||||
});
|
});
|
||||||
@@ -782,7 +831,9 @@ class VoiceConnection extends EventEmitter {
|
|||||||
if (!userId) {
|
if (!userId) {
|
||||||
throw new Error('VOICE_USER_MISSING');
|
throw new Error('VOICE_USER_MISSING');
|
||||||
}
|
}
|
||||||
const voiceState = this.channel.guild?.voiceStates.cache.get(userId) || this.client.voiceStates.cache.get(userId);
|
const voiceState =
|
||||||
|
this.channel.guild?.voiceStates.cache.get(userId) ||
|
||||||
|
this.client.voiceStates.cache.get(userId);
|
||||||
if (!voiceState || !voiceState.streaming) {
|
if (!voiceState || !voiceState.streaming) {
|
||||||
throw new Error('VOICE_USER_NOT_STREAMING');
|
throw new Error('VOICE_USER_NOT_STREAMING');
|
||||||
}
|
}
|
||||||
@@ -791,14 +842,24 @@ class VoiceConnection extends EventEmitter {
|
|||||||
if (this.streamWatchConnection.has(userId)) {
|
if (this.streamWatchConnection.has(userId)) {
|
||||||
return resolve(this.streamWatchConnection.get(userId));
|
return resolve(this.streamWatchConnection.get(userId));
|
||||||
} else {
|
} else {
|
||||||
const connection = new StreamConnectionReadonly(this.voiceManager, this.channel, this, userId);
|
const connection = new StreamConnectionReadonly(
|
||||||
|
this.voiceManager,
|
||||||
|
this.channel,
|
||||||
|
this,
|
||||||
|
userId,
|
||||||
|
);
|
||||||
this.streamWatchConnection.set(userId, connection);
|
this.streamWatchConnection.set(userId, connection);
|
||||||
connection.setVideoCodec(this.videoCodec);
|
connection.setVideoCodec(this.videoCodec);
|
||||||
// Setup event...
|
// Setup event...
|
||||||
if (!this.eventHook) {
|
if (!this.eventHook) {
|
||||||
this.eventHook = true; // Dont listen this event two times :/
|
this.eventHook = true; // Dont listen this event two times :/
|
||||||
this.channel.client.on('raw', packet => {
|
this.channel.client.on('raw', (packet) => {
|
||||||
if (typeof packet !== 'object' || !packet.t || !packet.d || !packet.d?.stream_key) {
|
if (
|
||||||
|
typeof packet !== 'object' ||
|
||||||
|
!packet.t ||
|
||||||
|
!packet.d ||
|
||||||
|
!packet.d?.stream_key
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { t: event, d: data } = packet;
|
const { t: event, d: data } = packet;
|
||||||
@@ -811,12 +872,17 @@ class VoiceConnection extends EventEmitter {
|
|||||||
// Current user stream
|
// Current user stream
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case 'STREAM_CREATE': {
|
case 'STREAM_CREATE': {
|
||||||
this.streamConnection.setSessionId(this.authentication.sessionId);
|
this.streamConnection.setSessionId(
|
||||||
|
this.authentication.sessionId,
|
||||||
|
);
|
||||||
this.streamConnection.serverId = data.rtc_server_id;
|
this.streamConnection.serverId = data.rtc_server_id;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'STREAM_SERVER_UPDATE': {
|
case 'STREAM_SERVER_UPDATE': {
|
||||||
this.streamConnection.setTokenAndEndpoint(data.token, data.endpoint);
|
this.streamConnection.setTokenAndEndpoint(
|
||||||
|
data.token,
|
||||||
|
data.endpoint,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'STREAM_DELETE': {
|
case 'STREAM_DELETE': {
|
||||||
@@ -829,8 +895,13 @@ class VoiceConnection extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.streamWatchConnection.has(StreamKey.userId) && this.channel.id == StreamKey.channelId) {
|
if (
|
||||||
const streamConnection = this.streamWatchConnection.get(StreamKey.userId);
|
this.streamWatchConnection.has(StreamKey.userId) &&
|
||||||
|
this.channel.id == StreamKey.channelId
|
||||||
|
) {
|
||||||
|
const streamConnection = this.streamWatchConnection.get(
|
||||||
|
StreamKey.userId,
|
||||||
|
);
|
||||||
// Watch user stream
|
// Watch user stream
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case 'STREAM_CREATE': {
|
case 'STREAM_CREATE': {
|
||||||
@@ -839,7 +910,10 @@ class VoiceConnection extends EventEmitter {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'STREAM_SERVER_UPDATE': {
|
case 'STREAM_SERVER_UPDATE': {
|
||||||
streamConnection.setTokenAndEndpoint(data.token, data.endpoint);
|
streamConnection.setTokenAndEndpoint(
|
||||||
|
data.token,
|
||||||
|
data.endpoint,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'STREAM_DELETE': {
|
case 'STREAM_DELETE': {
|
||||||
@@ -858,7 +932,7 @@ class VoiceConnection extends EventEmitter {
|
|||||||
|
|
||||||
connection.sendSignalScreenshare();
|
connection.sendSignalScreenshare();
|
||||||
|
|
||||||
connection.on('debug', msg =>
|
connection.on('debug', (msg) =>
|
||||||
this.channel.client.emit(
|
this.channel.client.emit(
|
||||||
'debug',
|
'debug',
|
||||||
`[VOICE STREAM WATCH (${userId}>${this.channel.guild?.id || this.channel.id}:${
|
`[VOICE STREAM WATCH (${userId}>${this.channel.guild?.id || this.channel.id}:${
|
||||||
@@ -866,7 +940,7 @@ class VoiceConnection extends EventEmitter {
|
|||||||
})]: ${msg}`,
|
})]: ${msg}`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
connection.once('failed', reason => {
|
connection.once('failed', (reason) => {
|
||||||
this.streamWatchConnection.delete(userId);
|
this.streamWatchConnection.delete(userId);
|
||||||
reject(reason);
|
reject(reason);
|
||||||
});
|
});
|
||||||
@@ -988,7 +1062,8 @@ class StreamConnection extends VoiceConnection {
|
|||||||
this.emit('closing');
|
this.emit('closing');
|
||||||
this.emit('debug', 'Stream: disconnect() triggered');
|
this.emit('debug', 'Stream: disconnect() triggered');
|
||||||
clearTimeout(this.connectTimeout);
|
clearTimeout(this.connectTimeout);
|
||||||
if (this.voiceConnection.streamConnection === this) this.voiceConnection.streamConnection = null;
|
if (this.voiceConnection.streamConnection === this)
|
||||||
|
this.voiceConnection.streamConnection = null;
|
||||||
this.sendStopScreenshare();
|
this.sendStopScreenshare();
|
||||||
this._disconnect();
|
this._disconnect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,20 @@ Please use the @dank074/discord-video-stream library for the best support.
|
|||||||
const { Buffer } = require('buffer');
|
const { Buffer } = require('buffer');
|
||||||
const VideoDispatcher = require('./VideoDispatcher');
|
const VideoDispatcher = require('./VideoDispatcher');
|
||||||
const Util = require('../../../util/Util');
|
const Util = require('../../../util/Util');
|
||||||
const { H264Helpers, H265Helpers } = require('../player/processing/AnnexBNalSplitter');
|
const {
|
||||||
|
H264Helpers,
|
||||||
|
H265Helpers,
|
||||||
|
} = require('../player/processing/AnnexBNalSplitter');
|
||||||
|
|
||||||
class AnnexBDispatcher extends VideoDispatcher {
|
class AnnexBDispatcher extends VideoDispatcher {
|
||||||
constructor(player, highWaterMark = 12, streams, fps, nalFunctions, payloadType) {
|
constructor(
|
||||||
|
player,
|
||||||
|
highWaterMark = 12,
|
||||||
|
streams,
|
||||||
|
fps,
|
||||||
|
nalFunctions,
|
||||||
|
payloadType,
|
||||||
|
) {
|
||||||
super(player, highWaterMark, streams, fps, payloadType);
|
super(player, highWaterMark, streams, fps, payloadType);
|
||||||
this._nalFunctions = nalFunctions;
|
this._nalFunctions = nalFunctions;
|
||||||
}
|
}
|
||||||
@@ -40,12 +50,19 @@ class AnnexBDispatcher extends VideoDispatcher {
|
|||||||
const isLastNal = offset + naluSize >= accessUnit.length;
|
const isLastNal = offset + naluSize >= accessUnit.length;
|
||||||
if (nalu.length <= this.mtu) {
|
if (nalu.length <= this.mtu) {
|
||||||
// Send as Single NAL Unit Packet.
|
// Send as Single NAL Unit Packet.
|
||||||
this._playChunk(Buffer.concat([this.createPayloadExtension(), nalu]), isLastNal);
|
this._playChunk(
|
||||||
|
Buffer.concat([this.createPayloadExtension(), nalu]),
|
||||||
|
isLastNal,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
const [naluHeader, naluData] = this._nalFunctions.splitHeader(nalu);
|
const [naluHeader, naluData] = this._nalFunctions.splitHeader(nalu);
|
||||||
const dataFragments = this.partitionMtu(naluData);
|
const dataFragments = this.partitionMtu(naluData);
|
||||||
// Send as Fragmentation Unit A (FU-A):
|
// Send as Fragmentation Unit A (FU-A):
|
||||||
for (let fragmentIndex = 0; fragmentIndex < dataFragments.length; fragmentIndex++) {
|
for (
|
||||||
|
let fragmentIndex = 0;
|
||||||
|
fragmentIndex < dataFragments.length;
|
||||||
|
fragmentIndex++
|
||||||
|
) {
|
||||||
const data = dataFragments[fragmentIndex];
|
const data = dataFragments[fragmentIndex];
|
||||||
const isFirstPacket = fragmentIndex === 0;
|
const isFirstPacket = fragmentIndex === 0;
|
||||||
const isFinalPacket = fragmentIndex === dataFragments.length - 1;
|
const isFinalPacket = fragmentIndex === dataFragments.length - 1;
|
||||||
@@ -53,7 +70,11 @@ class AnnexBDispatcher extends VideoDispatcher {
|
|||||||
this._playChunk(
|
this._playChunk(
|
||||||
Buffer.concat([
|
Buffer.concat([
|
||||||
this.createPayloadExtension(),
|
this.createPayloadExtension(),
|
||||||
this.makeFragmentationUnitHeader(isFirstPacket, isFinalPacket, naluHeader),
|
this.makeFragmentationUnitHeader(
|
||||||
|
isFirstPacket,
|
||||||
|
isFinalPacket,
|
||||||
|
naluHeader,
|
||||||
|
),
|
||||||
data,
|
data,
|
||||||
]),
|
]),
|
||||||
isLastNal && isFinalPacket,
|
isLastNal && isFinalPacket,
|
||||||
@@ -67,7 +88,14 @@ class AnnexBDispatcher extends VideoDispatcher {
|
|||||||
|
|
||||||
class H264Dispatcher extends AnnexBDispatcher {
|
class H264Dispatcher extends AnnexBDispatcher {
|
||||||
constructor(player, highWaterMark = 12, streams, fps) {
|
constructor(player, highWaterMark = 12, streams, fps) {
|
||||||
super(player, highWaterMark, streams, fps, H264Helpers, Util.getPayloadType('H264'));
|
super(
|
||||||
|
player,
|
||||||
|
highWaterMark,
|
||||||
|
streams,
|
||||||
|
fps,
|
||||||
|
H264Helpers,
|
||||||
|
Util.getPayloadType('H264'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
makeFragmentationUnitHeader(isFirstPacket, isLastPacket, naluHeader) {
|
makeFragmentationUnitHeader(isFirstPacket, isLastPacket, naluHeader) {
|
||||||
@@ -92,7 +120,14 @@ class H264Dispatcher extends AnnexBDispatcher {
|
|||||||
|
|
||||||
class H265Dispatcher extends AnnexBDispatcher {
|
class H265Dispatcher extends AnnexBDispatcher {
|
||||||
constructor(player, highWaterMark = 12, streams, fps) {
|
constructor(player, highWaterMark = 12, streams, fps) {
|
||||||
super(player, highWaterMark, streams, fps, H265Helpers, Util.getPayloadType('H265'));
|
super(
|
||||||
|
player,
|
||||||
|
highWaterMark,
|
||||||
|
streams,
|
||||||
|
fps,
|
||||||
|
H265Helpers,
|
||||||
|
Util.getPayloadType('H265'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
makeFragmentationUnitHeader(isFirstPacket, isLastPacket, naluHeader) {
|
makeFragmentationUnitHeader(isFirstPacket, isLastPacket, naluHeader) {
|
||||||
|
|||||||
@@ -25,7 +25,11 @@ const CHANNELS = 2;
|
|||||||
* @extends {BaseDispatcher}
|
* @extends {BaseDispatcher}
|
||||||
*/
|
*/
|
||||||
class AudioDispatcher extends BaseDispatcher {
|
class AudioDispatcher extends BaseDispatcher {
|
||||||
constructor(player, { seek = 0, volume = 1, fec, plp, bitrate = 96, highWaterMark = 12 } = {}, streams) {
|
constructor(
|
||||||
|
player,
|
||||||
|
{ seek = 0, volume = 1, fec, plp, bitrate = 96, highWaterMark = 12 } = {},
|
||||||
|
streams,
|
||||||
|
) {
|
||||||
const streamOptions = { seek, volume, fec, plp, bitrate, highWaterMark };
|
const streamOptions = { seek, volume, fec, plp, bitrate, highWaterMark };
|
||||||
super(player, highWaterMark, Util.getPayloadType('opus'), false, streams);
|
super(player, highWaterMark, Util.getPayloadType('opus'), false, streams);
|
||||||
|
|
||||||
@@ -63,7 +67,8 @@ class AudioDispatcher extends BaseDispatcher {
|
|||||||
*/
|
*/
|
||||||
setBitrate(value) {
|
setBitrate(value) {
|
||||||
if (!value || !this.bitrateEditable) return false;
|
if (!value || !this.bitrateEditable) return false;
|
||||||
const bitrate = value === 'auto' ? this.player.voiceConnection.channel.bitrate : value;
|
const bitrate =
|
||||||
|
value === 'auto' ? this.player.voiceConnection.channel.bitrate : value;
|
||||||
this.streams.opus.setBitrate(bitrate * 1000);
|
this.streams.opus.setBitrate(bitrate * 1000);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,13 @@ const extensions = [{ id: 5, length: 2, value: 0 }];
|
|||||||
* @extends {Writable}
|
* @extends {Writable}
|
||||||
*/
|
*/
|
||||||
class BaseDispatcher extends Writable {
|
class BaseDispatcher extends Writable {
|
||||||
constructor(player, highWaterMark = 12, payloadType, extensionEnabled, streams = {}) {
|
constructor(
|
||||||
|
player,
|
||||||
|
highWaterMark = 12,
|
||||||
|
payloadType,
|
||||||
|
extensionEnabled,
|
||||||
|
streams = {},
|
||||||
|
) {
|
||||||
super({
|
super({
|
||||||
highWaterMark,
|
highWaterMark,
|
||||||
});
|
});
|
||||||
@@ -63,10 +69,14 @@ class BaseDispatcher extends Writable {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.on('error', () => streamError());
|
this.on('error', () => streamError());
|
||||||
if (this.streams.input) this.streams.input.on('error', err => streamError('input', err));
|
if (this.streams.input)
|
||||||
if (this.streams.ffmpeg) this.streams.ffmpeg.on('error', err => streamError('ffmpeg', err));
|
this.streams.input.on('error', (err) => streamError('input', err));
|
||||||
if (this.streams.opus) this.streams.opus.on('error', err => streamError('opus', err));
|
if (this.streams.ffmpeg)
|
||||||
if (this.streams.volume) this.streams.volume.on('error', err => streamError('volume', err));
|
this.streams.ffmpeg.on('error', (err) => streamError('ffmpeg', err));
|
||||||
|
if (this.streams.opus)
|
||||||
|
this.streams.opus.on('error', (err) => streamError('opus', err));
|
||||||
|
if (this.streams.volume)
|
||||||
|
this.streams.volume.on('error', (err) => streamError('volume', err));
|
||||||
|
|
||||||
this.on('finish', () => {
|
this.on('finish', () => {
|
||||||
this._cleanup();
|
this._cleanup();
|
||||||
@@ -82,7 +92,8 @@ class BaseDispatcher extends Writable {
|
|||||||
|
|
||||||
resetNonceBuffer() {
|
resetNonceBuffer() {
|
||||||
this._nonceBuffer =
|
this._nonceBuffer =
|
||||||
this.player.voiceConnection.authentication.mode === 'aead_aes256_gcm_rtpsize'
|
this.player.voiceConnection.authentication.mode ===
|
||||||
|
'aead_aes256_gcm_rtpsize'
|
||||||
? Buffer.alloc(12)
|
? Buffer.alloc(12)
|
||||||
: Buffer.alloc(24);
|
: Buffer.alloc(24);
|
||||||
}
|
}
|
||||||
@@ -179,7 +190,11 @@ class BaseDispatcher extends Writable {
|
|||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get pausedTime() {
|
get pausedTime() {
|
||||||
return this._silentPausedTime + this._pausedTime + (this.paused ? performance.now() - this.pausedSince : 0);
|
return (
|
||||||
|
this._silentPausedTime +
|
||||||
|
this._pausedTime +
|
||||||
|
(this.paused ? performance.now() - this.pausedSince : 0)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -217,9 +232,12 @@ class BaseDispatcher extends Writable {
|
|||||||
this._writeCallback = null;
|
this._writeCallback = null;
|
||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
const next = (this.count + 1) * this.FRAME_LENGTH - (performance.now() - this.startTime - this._pausedTime);
|
const next =
|
||||||
|
(this.count + 1) * this.FRAME_LENGTH -
|
||||||
|
(performance.now() - this.startTime - this._pausedTime);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if ((!this.pausedSince || this._silence) && this._writeCallback) this._writeCallback();
|
if ((!this.pausedSince || this._silence) && this._writeCallback)
|
||||||
|
this._writeCallback();
|
||||||
}, next).unref();
|
}, next).unref();
|
||||||
this.timestamp += this.TIMESTAMP_INC;
|
this.timestamp += this.TIMESTAMP_INC;
|
||||||
if (this.timestamp > MAX_UINT_32) this.timestamp = 0;
|
if (this.timestamp > MAX_UINT_32) this.timestamp = 0;
|
||||||
@@ -234,7 +252,8 @@ class BaseDispatcher extends Writable {
|
|||||||
|
|
||||||
_playChunk(chunk, isLastPacket = false) {
|
_playChunk(chunk, isLastPacket = false) {
|
||||||
if (
|
if (
|
||||||
(this.player.dispatcher !== this && this.player.videoDispatcher !== this) ||
|
(this.player.dispatcher !== this &&
|
||||||
|
this.player.videoDispatcher !== this) ||
|
||||||
!this.player.voiceConnection.authentication.secret_key
|
!this.player.voiceConnection.authentication.secret_key
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@@ -321,15 +340,24 @@ class BaseDispatcher extends Writable {
|
|||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 'aead_aes256_gcm_rtpsize': {
|
case 'aead_aes256_gcm_rtpsize': {
|
||||||
const cipher = crypto.createCipheriv('aes-256-gcm', secret_key, this._nonceBuffer);
|
const cipher = crypto.createCipheriv(
|
||||||
|
'aes-256-gcm',
|
||||||
|
secret_key,
|
||||||
|
this._nonceBuffer,
|
||||||
|
);
|
||||||
cipher.setAAD(additionalData);
|
cipher.setAAD(additionalData);
|
||||||
|
|
||||||
encrypted = Buffer.concat([cipher.update(buffer), cipher.final(), cipher.getAuthTag()]);
|
encrypted = Buffer.concat([
|
||||||
|
cipher.update(buffer),
|
||||||
|
cipher.final(),
|
||||||
|
cipher.getAuthTag(),
|
||||||
|
]);
|
||||||
|
|
||||||
return [encrypted, noncePadding];
|
return [encrypted, noncePadding];
|
||||||
}
|
}
|
||||||
case 'aead_xchacha20_poly1305_rtpsize': {
|
case 'aead_xchacha20_poly1305_rtpsize': {
|
||||||
encrypted = secretbox.methods.crypto_aead_xchacha20poly1305_ietf_encrypt(
|
encrypted =
|
||||||
|
secretbox.methods.crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||||
buffer,
|
buffer,
|
||||||
additionalData,
|
additionalData,
|
||||||
this._nonceBuffer,
|
this._nonceBuffer,
|
||||||
@@ -385,7 +413,8 @@ class BaseDispatcher extends Writable {
|
|||||||
rtpHeader.writeUIntBE(this.getNewSequence(), 2, 2);
|
rtpHeader.writeUIntBE(this.getNewSequence(), 2, 2);
|
||||||
rtpHeader.writeUIntBE(this.timestamp, 4, 4);
|
rtpHeader.writeUIntBE(this.timestamp, 4, 4);
|
||||||
rtpHeader.writeUIntBE(
|
rtpHeader.writeUIntBE(
|
||||||
this.player.voiceConnection.authentication.ssrc + Number(this.getTypeDispatcher() === 'video'),
|
this.player.voiceConnection.authentication.ssrc +
|
||||||
|
Number(this.getTypeDispatcher() === 'video'),
|
||||||
8,
|
8,
|
||||||
4,
|
4,
|
||||||
);
|
);
|
||||||
@@ -408,7 +437,7 @@ class BaseDispatcher extends Writable {
|
|||||||
this.emit('debug', 'Failed to send a packet - no UDP socket');
|
this.emit('debug', 'Failed to send a packet - no UDP socket');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.player.voiceConnection.sockets.udp.send(packet).catch(e => {
|
this.player.voiceConnection.sockets.udp.send(packet).catch((e) => {
|
||||||
if (this.getTypeDispatcher() === 'audio') {
|
if (this.getTypeDispatcher() === 'audio') {
|
||||||
this._setSpeaking(this._setSpeaking(0));
|
this._setSpeaking(this._setSpeaking(0));
|
||||||
} else if (this.getTypeDispatcher() === 'video') {
|
} else if (this.getTypeDispatcher() === 'video') {
|
||||||
@@ -444,7 +473,9 @@ class BaseDispatcher extends Writable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_setStreamStatus(value) {
|
_setStreamStatus(value) {
|
||||||
if (typeof this.player.voiceConnection?.sendScreenshareState !== 'undefined') {
|
if (
|
||||||
|
typeof this.player.voiceConnection?.sendScreenshareState !== 'undefined'
|
||||||
|
) {
|
||||||
this.player.voiceConnection.sendScreenshareState(value);
|
this.player.voiceConnection.sendScreenshareState(value);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -38,11 +38,18 @@ class VP8Dispatcher extends VideoDispatcher {
|
|||||||
const pictureIdBuf = Buffer.alloc(2);
|
const pictureIdBuf = Buffer.alloc(2);
|
||||||
pictureIdBuf.writeUintBE(this.count, 0, 2);
|
pictureIdBuf.writeUintBE(this.count, 0, 2);
|
||||||
pictureIdBuf[0] |= 0x80;
|
pictureIdBuf[0] |= 0x80;
|
||||||
return Buffer.concat([this.createPayloadExtension(), payloadDescriptorBuf, pictureIdBuf, buffer]);
|
return Buffer.concat([
|
||||||
|
this.createPayloadExtension(),
|
||||||
|
payloadDescriptorBuf,
|
||||||
|
pictureIdBuf,
|
||||||
|
buffer,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
_codecCallback(chunk) {
|
_codecCallback(chunk) {
|
||||||
const chunkSplit = this.partitionMtu(chunk).map((c, i) => this.makeChunk(c, i === 0));
|
const chunkSplit = this.partitionMtu(chunk).map((c, i) =>
|
||||||
|
this.makeChunk(c, i === 0),
|
||||||
|
);
|
||||||
for (let i = 0; i < chunkSplit.length; i++) {
|
for (let i = 0; i < chunkSplit.length; i++) {
|
||||||
this._playChunk(chunkSplit[i], i + 1 === chunkSplit.length);
|
this._playChunk(chunkSplit[i], i + 1 === chunkSplit.length);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,22 +79,30 @@ class VoiceConnectionUDPClient extends EventEmitter {
|
|||||||
send(packet) {
|
send(packet) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!this.socket) throw new Error('UDP_SEND_FAIL');
|
if (!this.socket) throw new Error('UDP_SEND_FAIL');
|
||||||
if (!this.discordAddress || !this.discordPort) throw new Error('UDP_ADDRESS_MALFORMED');
|
if (!this.discordAddress || !this.discordPort)
|
||||||
this.socket.send(packet, 0, packet.length, this.discordPort, this.discordAddress, error => {
|
throw new Error('UDP_ADDRESS_MALFORMED');
|
||||||
|
this.socket.send(
|
||||||
|
packet,
|
||||||
|
0,
|
||||||
|
packet.length,
|
||||||
|
this.discordPort,
|
||||||
|
this.discordAddress,
|
||||||
|
(error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
this.emit('debug', `[UDP] >> ERROR: ${error}`);
|
this.emit('debug', `[UDP] >> ERROR: ${error}`);
|
||||||
reject(error);
|
reject(error);
|
||||||
} else {
|
} else {
|
||||||
resolve(packet);
|
resolve(packet);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async createUDPSocket(address) {
|
async createUDPSocket(address) {
|
||||||
this.discordAddress = address;
|
this.discordAddress = address;
|
||||||
const socket = (this.socket = udp.createSocket('udp4'));
|
const socket = (this.socket = udp.createSocket('udp4'));
|
||||||
socket.on('error', e => {
|
socket.on('error', (e) => {
|
||||||
this.emit('debug', `[UDP] Error: ${e}`);
|
this.emit('debug', `[UDP] Error: ${e}`);
|
||||||
this.emit('error', e);
|
this.emit('error', e);
|
||||||
});
|
});
|
||||||
@@ -102,7 +110,7 @@ class VoiceConnectionUDPClient extends EventEmitter {
|
|||||||
this.emit('debug', '[UDP] socket closed');
|
this.emit('debug', '[UDP] socket closed');
|
||||||
});
|
});
|
||||||
this.emit('debug', `[UDP] created socket`);
|
this.emit('debug', `[UDP] created socket`);
|
||||||
socket.once('message', message => {
|
socket.once('message', (message) => {
|
||||||
this.emit('debug', `[UDP] message: [${[...message]}] (${message})`);
|
this.emit('debug', `[UDP] message: [${[...message]}] (${message})`);
|
||||||
if (message.readUInt16BE(0) !== 2) {
|
if (message.readUInt16BE(0) !== 2) {
|
||||||
throw new Error('UDP_WRONG_HANDSHAKE');
|
throw new Error('UDP_WRONG_HANDSHAKE');
|
||||||
@@ -117,7 +125,10 @@ class VoiceConnectionUDPClient extends EventEmitter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit('debug', `[UDP] Parse local packet: ${packet.address}:${packet.port}`);
|
this.emit(
|
||||||
|
'debug',
|
||||||
|
`[UDP] Parse local packet: ${packet.address}:${packet.port}`,
|
||||||
|
);
|
||||||
|
|
||||||
this.localAddress = packet.address;
|
this.localAddress = packet.address;
|
||||||
this.localPort = packet.port;
|
this.localPort = packet.port;
|
||||||
@@ -143,7 +154,9 @@ class VoiceConnectionUDPClient extends EventEmitter {
|
|||||||
|
|
||||||
this.emit('debug', `[UDP] << ${JSON.stringify(packet)}`);
|
this.emit('debug', `[UDP] << ${JSON.stringify(packet)}`);
|
||||||
|
|
||||||
socket.on('message', buffer => this.voiceConnection.receiver.packets.push(buffer));
|
socket.on('message', (buffer) =>
|
||||||
|
this.voiceConnection.receiver.packets.push(buffer),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const blankMessage = Buffer.alloc(74);
|
const blankMessage = Buffer.alloc(74);
|
||||||
|
|||||||
@@ -67,7 +67,10 @@ class VoiceWebSocket extends EventEmitter {
|
|||||||
if (this.dead) return;
|
if (this.dead) return;
|
||||||
if (this.ws) this.reset();
|
if (this.ws) this.reset();
|
||||||
if (this.attempts >= 5) {
|
if (this.attempts >= 5) {
|
||||||
this.emit('debug', new Error('VOICE_CONNECTION_ATTEMPTS_EXCEEDED', this.attempts));
|
this.emit(
|
||||||
|
'debug',
|
||||||
|
new Error('VOICE_CONNECTION_ATTEMPTS_EXCEEDED', this.attempts),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,8 +80,14 @@ class VoiceWebSocket extends EventEmitter {
|
|||||||
* The actual WebSocket used to connect to the Voice WebSocket Server.
|
* The actual WebSocket used to connect to the Voice WebSocket Server.
|
||||||
* @type {WebSocket}
|
* @type {WebSocket}
|
||||||
*/
|
*/
|
||||||
this.ws = WebSocket.create(`wss://${this.connection.authentication.endpoint}/`, { v: 8 });
|
this.ws = WebSocket.create(
|
||||||
this.emit('debug', `[WS] connecting, ${this.attempts} attempts, ${this.ws.url}`);
|
`wss://${this.connection.authentication.endpoint}/`,
|
||||||
|
{ v: 8 },
|
||||||
|
);
|
||||||
|
this.emit(
|
||||||
|
'debug',
|
||||||
|
`[WS] connecting, ${this.attempts} attempts, ${this.ws.url}`,
|
||||||
|
);
|
||||||
this.ws.onopen = this.onOpen.bind(this);
|
this.ws.onopen = this.onOpen.bind(this);
|
||||||
this.ws.onmessage = this.onMessage.bind(this);
|
this.ws.onmessage = this.onMessage.bind(this);
|
||||||
this.ws.onclose = this.onClose.bind(this);
|
this.ws.onclose = this.onClose.bind(this);
|
||||||
@@ -93,8 +102,9 @@ class VoiceWebSocket extends EventEmitter {
|
|||||||
send(data) {
|
send(data) {
|
||||||
this.emit('debug', `[WS] >> ${data}`);
|
this.emit('debug', `[WS] >> ${data}`);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) throw new Error('WS_NOT_OPEN', data);
|
if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
|
||||||
this.ws.send(data, null, error => {
|
throw new Error('WS_NOT_OPEN', data);
|
||||||
|
this.ws.send(data, null, (error) => {
|
||||||
if (error) reject(error);
|
if (error) reject(error);
|
||||||
else resolve(data);
|
else resolve(data);
|
||||||
});
|
});
|
||||||
@@ -115,11 +125,17 @@ class VoiceWebSocket extends EventEmitter {
|
|||||||
* Called whenever the WebSocket opens.
|
* Called whenever the WebSocket opens.
|
||||||
*/
|
*/
|
||||||
onOpen() {
|
onOpen() {
|
||||||
this.emit('debug', `[WS] opened at gateway ${this.connection.authentication.endpoint}`);
|
this.emit(
|
||||||
|
'debug',
|
||||||
|
`[WS] opened at gateway ${this.connection.authentication.endpoint}`,
|
||||||
|
);
|
||||||
this.sendPacket({
|
this.sendPacket({
|
||||||
op: Opcodes.DISPATCH,
|
op: Opcodes.DISPATCH,
|
||||||
d: {
|
d: {
|
||||||
server_id: this.connection.serverId || this.connection.channel.guild?.id || this.connection.channel.id,
|
server_id:
|
||||||
|
this.connection.serverId ||
|
||||||
|
this.connection.channel.guild?.id ||
|
||||||
|
this.connection.channel.id,
|
||||||
user_id: this.client.user.id,
|
user_id: this.client.user.id,
|
||||||
token: this.connection.authentication.token,
|
token: this.connection.authentication.token,
|
||||||
session_id: this.connection.authentication.sessionId,
|
session_id: this.connection.authentication.sessionId,
|
||||||
@@ -149,8 +165,12 @@ class VoiceWebSocket extends EventEmitter {
|
|||||||
* @param {CloseEvent} event The WebSocket close event
|
* @param {CloseEvent} event The WebSocket close event
|
||||||
*/
|
*/
|
||||||
onClose(event) {
|
onClose(event) {
|
||||||
this.emit('debug', `[WS] closed with code ${event.code} and reason: ${event.reason}`);
|
this.emit(
|
||||||
if (!this.dead) setTimeout(this.connect.bind(this), this.attempts * 1000).unref();
|
'debug',
|
||||||
|
`[WS] closed with code ${event.code} and reason: ${event.reason}`,
|
||||||
|
);
|
||||||
|
if (!this.dead)
|
||||||
|
setTimeout(this.connect.bind(this), this.attempts * 1000).unref();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -200,7 +220,9 @@ class VoiceWebSocket extends EventEmitter {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case VoiceOpcodes.CLIENT_DISCONNECT:
|
case VoiceOpcodes.CLIENT_DISCONNECT:
|
||||||
const streamInfo = this.connection.receiver && this.connection.receiver.packets.streams.get(packet.d.user_id);
|
const streamInfo =
|
||||||
|
this.connection.receiver &&
|
||||||
|
this.connection.receiver.packets.streams.get(packet.d.user_id);
|
||||||
if (streamInfo) {
|
if (streamInfo) {
|
||||||
this.connection.receiver.packets.streams.delete(packet.d.user_id);
|
this.connection.receiver.packets.streams.delete(packet.d.user_id);
|
||||||
streamInfo.stream.push(null);
|
streamInfo.stream.push(null);
|
||||||
@@ -251,7 +273,10 @@ class VoiceWebSocket extends EventEmitter {
|
|||||||
this.emit('warn', 'A voice heartbeat interval is being overwritten');
|
this.emit('warn', 'A voice heartbeat interval is being overwritten');
|
||||||
clearInterval(this.heartbeatInterval);
|
clearInterval(this.heartbeatInterval);
|
||||||
}
|
}
|
||||||
this.heartbeatInterval = setInterval(this.sendHeartbeat.bind(this), interval).unref();
|
this.heartbeatInterval = setInterval(
|
||||||
|
this.sendHeartbeat.bind(this),
|
||||||
|
interval,
|
||||||
|
).unref();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -259,7 +284,10 @@ class VoiceWebSocket extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
clearHeartbeat() {
|
clearHeartbeat() {
|
||||||
if (!this.heartbeatInterval) {
|
if (!this.heartbeatInterval) {
|
||||||
this.emit('warn', 'Tried to clear a heartbeat interval that does not exist');
|
this.emit(
|
||||||
|
'warn',
|
||||||
|
'Tried to clear a heartbeat interval that does not exist',
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
clearInterval(this.heartbeatInterval);
|
clearInterval(this.heartbeatInterval);
|
||||||
|
|||||||
@@ -20,13 +20,22 @@ Please use the @dank074/discord-video-stream library for the best support.
|
|||||||
const EventEmitter = require('events');
|
const EventEmitter = require('events');
|
||||||
const { Readable: ReadableStream } = require('stream');
|
const { Readable: ReadableStream } = require('stream');
|
||||||
const prism = require('prism-media');
|
const prism = require('prism-media');
|
||||||
const { H264NalSplitter, H265NalSplitter } = require('./processing/AnnexBNalSplitter');
|
const {
|
||||||
|
H264NalSplitter,
|
||||||
|
H265NalSplitter,
|
||||||
|
} = require('./processing/AnnexBNalSplitter');
|
||||||
const { IvfTransformer } = require('./processing/IvfSplitter');
|
const { IvfTransformer } = require('./processing/IvfSplitter');
|
||||||
const { H264Dispatcher } = require('../dispatcher/AnnexBDispatcher');
|
const { H264Dispatcher } = require('../dispatcher/AnnexBDispatcher');
|
||||||
const AudioDispatcher = require('../dispatcher/AudioDispatcher');
|
const AudioDispatcher = require('../dispatcher/AudioDispatcher');
|
||||||
const { VP8Dispatcher } = require('../dispatcher/VPxDispatcher');
|
const { VP8Dispatcher } = require('../dispatcher/VPxDispatcher');
|
||||||
|
|
||||||
const FFMPEG_OUTPUT_PREFIX = ['-use_wallclock_as_timestamps', '1', '-copyts', '-analyzeduration', '0'];
|
const FFMPEG_OUTPUT_PREFIX = [
|
||||||
|
'-use_wallclock_as_timestamps',
|
||||||
|
'1',
|
||||||
|
'-copyts',
|
||||||
|
'-analyzeduration',
|
||||||
|
'0',
|
||||||
|
];
|
||||||
const FFMPEG_INPUT_PREFIX = [
|
const FFMPEG_INPUT_PREFIX = [
|
||||||
'-reconnect',
|
'-reconnect',
|
||||||
'1',
|
'1',
|
||||||
@@ -38,8 +47,15 @@ const FFMPEG_INPUT_PREFIX = [
|
|||||||
'4294',
|
'4294',
|
||||||
];
|
];
|
||||||
const FFMPEG_PCM_ARGUMENTS = ['-f', 's16le', '-ar', '48000', '-ac', '2'];
|
const FFMPEG_PCM_ARGUMENTS = ['-f', 's16le', '-ar', '48000', '-ac', '2'];
|
||||||
const FFMPEG_VP8_ARGUMENTS = ['-f', 'ivf', '-deadline', 'realtime', '-c:v', 'libvpx'];
|
const FFMPEG_VP8_ARGUMENTS = [
|
||||||
const FFMPEG_H264_ARGUMENTS = options => [
|
'-f',
|
||||||
|
'ivf',
|
||||||
|
'-deadline',
|
||||||
|
'realtime',
|
||||||
|
'-c:v',
|
||||||
|
'libvpx',
|
||||||
|
];
|
||||||
|
const FFMPEG_H264_ARGUMENTS = (options) => [
|
||||||
'-c:v',
|
'-c:v',
|
||||||
'libx264',
|
'libx264',
|
||||||
'-f',
|
'-f',
|
||||||
@@ -62,7 +78,7 @@ const FFMPEG_H264_ARGUMENTS = options => [
|
|||||||
'h264_metadata=aud=insert',
|
'h264_metadata=aud=insert',
|
||||||
];
|
];
|
||||||
|
|
||||||
const FFMPEG_H265_ARGUMENTS = options => [
|
const FFMPEG_H265_ARGUMENTS = (options) => [
|
||||||
'-c:v',
|
'-c:v',
|
||||||
'libx265',
|
'libx265',
|
||||||
'-f',
|
'-f',
|
||||||
@@ -127,9 +143,12 @@ class MediaPlayer extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ffmpeg = new prism.FFmpeg({ args });
|
const ffmpeg = new prism.FFmpeg({ args });
|
||||||
this.emit('debug', `[ffmpeg-audio_process] Spawn process with args:\n${args.join(' ')}`);
|
this.emit(
|
||||||
|
'debug',
|
||||||
|
`[ffmpeg-audio_process] Spawn process with args:\n${args.join(' ')}`,
|
||||||
|
);
|
||||||
|
|
||||||
ffmpeg.process.stderr.on('data', data => {
|
ffmpeg.process.stderr.on('data', (data) => {
|
||||||
this.emit('debug', `[ffmpeg-audio_process]: ${data.toString()}`);
|
this.emit('debug', `[ffmpeg-audio_process]: ${data.toString()}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -143,12 +162,19 @@ class MediaPlayer extends EventEmitter {
|
|||||||
|
|
||||||
playPCMStream(stream, options, streams = {}) {
|
playPCMStream(stream, options, streams = {}) {
|
||||||
this.destroyDispatcher();
|
this.destroyDispatcher();
|
||||||
const opus = (streams.opus = new prism.opus.Encoder({ channels: 2, rate: 48000, frameSize: 960 }));
|
const opus = (streams.opus = new prism.opus.Encoder({
|
||||||
|
channels: 2,
|
||||||
|
rate: 48000,
|
||||||
|
frameSize: 960,
|
||||||
|
}));
|
||||||
if (options && options.volume === false) {
|
if (options && options.volume === false) {
|
||||||
stream.pipe(opus);
|
stream.pipe(opus);
|
||||||
return this.playOpusStream(opus, options, streams);
|
return this.playOpusStream(opus, options, streams);
|
||||||
}
|
}
|
||||||
streams.volume = new prism.VolumeTransformer({ type: 's16le', volume: options ? options.volume : 1 });
|
streams.volume = new prism.VolumeTransformer({
|
||||||
|
type: 's16le',
|
||||||
|
volume: options ? options.volume : 1,
|
||||||
|
});
|
||||||
stream.pipe(streams.volume).pipe(opus);
|
stream.pipe(streams.volume).pipe(opus);
|
||||||
return this.playOpusStream(opus, options, streams);
|
return this.playOpusStream(opus, options, streams);
|
||||||
}
|
}
|
||||||
@@ -158,12 +184,21 @@ class MediaPlayer extends EventEmitter {
|
|||||||
streams.opus = stream;
|
streams.opus = stream;
|
||||||
if (options.volume !== false && !streams.input) {
|
if (options.volume !== false && !streams.input) {
|
||||||
streams.input = stream;
|
streams.input = stream;
|
||||||
const decoder = new prism.opus.Decoder({ channels: 2, rate: 48000, frameSize: 960 });
|
const decoder = new prism.opus.Decoder({
|
||||||
streams.volume = new prism.VolumeTransformer({ type: 's16le', volume: options ? options.volume : 1 });
|
channels: 2,
|
||||||
|
rate: 48000,
|
||||||
|
frameSize: 960,
|
||||||
|
});
|
||||||
|
streams.volume = new prism.VolumeTransformer({
|
||||||
|
type: 's16le',
|
||||||
|
volume: options ? options.volume : 1,
|
||||||
|
});
|
||||||
streams.opus = stream
|
streams.opus = stream
|
||||||
.pipe(decoder)
|
.pipe(decoder)
|
||||||
.pipe(streams.volume)
|
.pipe(streams.volume)
|
||||||
.pipe(new prism.opus.Encoder({ channels: 2, rate: 48000, frameSize: 960 }));
|
.pipe(
|
||||||
|
new prism.opus.Encoder({ channels: 2, rate: 48000, frameSize: 960 }),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const dispatcher = this.createDispatcher(options, streams);
|
const dispatcher = this.createDispatcher(options, streams);
|
||||||
streams.opus.pipe(dispatcher);
|
streams.opus.pipe(dispatcher);
|
||||||
@@ -232,9 +267,12 @@ class MediaPlayer extends EventEmitter {
|
|||||||
input.pipe(ffmpeg);
|
input.pipe(ffmpeg);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit('debug', `[ffmpeg-video_process] Spawn process with args:\n${args.join(' ')}`);
|
this.emit(
|
||||||
|
'debug',
|
||||||
|
`[ffmpeg-video_process] Spawn process with args:\n${args.join(' ')}`,
|
||||||
|
);
|
||||||
|
|
||||||
ffmpeg.process.stderr.on('data', data => {
|
ffmpeg.process.stderr.on('data', (data) => {
|
||||||
this.emit('debug', `[ffmpeg-video_process]: ${data.toString()}`);
|
this.emit('debug', `[ffmpeg-video_process]: ${data.toString()}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -279,7 +317,11 @@ class MediaPlayer extends EventEmitter {
|
|||||||
|
|
||||||
createDispatcher(options, streams) {
|
createDispatcher(options, streams) {
|
||||||
this.destroyDispatcher();
|
this.destroyDispatcher();
|
||||||
const dispatcher = (this.dispatcher = new AudioDispatcher(this, options, streams));
|
const dispatcher = (this.dispatcher = new AudioDispatcher(
|
||||||
|
this,
|
||||||
|
options,
|
||||||
|
streams,
|
||||||
|
));
|
||||||
return dispatcher;
|
return dispatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -166,7 +166,9 @@ class AnnexBNalSplitter extends Transform {
|
|||||||
findNalStart(buf) {
|
findNalStart(buf) {
|
||||||
const pos = buf.indexOf(nalSuffix);
|
const pos = buf.indexOf(nalSuffix);
|
||||||
if (pos === -1) return null;
|
if (pos === -1) return null;
|
||||||
return pos > 0 && buf[pos - 1] === 0 ? { index: pos - 1, length: 4 } : { index: pos, length: 3 };
|
return pos > 0 && buf[pos - 1] === 0
|
||||||
|
? { index: pos - 1, length: 4 }
|
||||||
|
: { index: pos, length: 3 };
|
||||||
}
|
}
|
||||||
|
|
||||||
processFrame(frame) {
|
processFrame(frame) {
|
||||||
@@ -174,11 +176,14 @@ class AnnexBNalSplitter extends Transform {
|
|||||||
|
|
||||||
const unitType = this._nalFunctions.getUnitType(frame);
|
const unitType = this._nalFunctions.getUnitType(frame);
|
||||||
if (this._nalFunctions.isAUD(unitType) && this._accessUnit.length > 0) {
|
if (this._nalFunctions.isAUD(unitType) && this._accessUnit.length > 0) {
|
||||||
const sizeOfAccessUnit = this._accessUnit.reduce((acc, nalu) => acc + nalu.length + 4, 0);
|
const sizeOfAccessUnit = this._accessUnit.reduce(
|
||||||
|
(acc, nalu) => acc + nalu.length + 4,
|
||||||
|
0,
|
||||||
|
);
|
||||||
const accessUnitBuf = Buffer.allocUnsafe(sizeOfAccessUnit);
|
const accessUnitBuf = Buffer.allocUnsafe(sizeOfAccessUnit);
|
||||||
|
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
this._accessUnit.forEach(nalu => {
|
this._accessUnit.forEach((nalu) => {
|
||||||
accessUnitBuf.writeUint32BE(nalu.length, offset);
|
accessUnitBuf.writeUint32BE(nalu.length, offset);
|
||||||
offset += 4;
|
offset += 4;
|
||||||
nalu.copy(accessUnitBuf, offset);
|
nalu.copy(accessUnitBuf, offset);
|
||||||
@@ -220,7 +225,10 @@ class H264NalSplitter extends AnnexBNalSplitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeEpbs(frame, unitType) {
|
removeEpbs(frame, unitType) {
|
||||||
return unitType === H264NalUnitTypes.SPS || unitType === H264NalUnitTypes.SEI ? this.rbsp(frame) : frame;
|
return unitType === H264NalUnitTypes.SPS ||
|
||||||
|
unitType === H264NalUnitTypes.SEI
|
||||||
|
? this.rbsp(frame)
|
||||||
|
: frame;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ class IvfTransformer extends Transform {
|
|||||||
|
|
||||||
this.header = null;
|
this.header = null;
|
||||||
this.buf = null;
|
this.buf = null;
|
||||||
this.retFullFrame = options && options.fullframe ? options.fullframe : false;
|
this.retFullFrame =
|
||||||
|
options && options.fullframe ? options.fullframe : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_parseHeader(header) {
|
_parseHeader(header) {
|
||||||
@@ -69,7 +70,8 @@ class IvfTransformer extends Transform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_updateBufLen(size) {
|
_updateBufLen(size) {
|
||||||
if (this.buf.length > size) this.buf = this.buf.subarray(size, this.buf.length);
|
if (this.buf.length > size)
|
||||||
|
this.buf = this.buf.subarray(size, this.buf.length);
|
||||||
else this.buf = null;
|
else this.buf = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ class PacketHandler extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getNonceBuffer() {
|
getNonceBuffer() {
|
||||||
return this.receiver.connection.authentication.mode === 'aead_aes256_gcm_rtpsize'
|
return this.receiver.connection.authentication.mode ===
|
||||||
|
'aead_aes256_gcm_rtpsize'
|
||||||
? Buffer.alloc(12)
|
? Buffer.alloc(12)
|
||||||
: Buffer.alloc(24);
|
: Buffer.alloc(24);
|
||||||
}
|
}
|
||||||
@@ -88,7 +89,10 @@ class PacketHandler extends EventEmitter {
|
|||||||
const header = buffer.slice(0, headerSize);
|
const header = buffer.slice(0, headerSize);
|
||||||
|
|
||||||
// Encrypted contains the extension, if any, the opus packet, and the auth tag
|
// Encrypted contains the extension, if any, the opus packet, and the auth tag
|
||||||
const encrypted = buffer.slice(headerSize, buffer.length - AUTH_TAG_LENGTH - UNPADDED_NONCE_LENGTH);
|
const encrypted = buffer.slice(
|
||||||
|
headerSize,
|
||||||
|
buffer.length - AUTH_TAG_LENGTH - UNPADDED_NONCE_LENGTH,
|
||||||
|
);
|
||||||
const authTag = buffer.slice(
|
const authTag = buffer.slice(
|
||||||
buffer.length - AUTH_TAG_LENGTH - UNPADDED_NONCE_LENGTH,
|
buffer.length - AUTH_TAG_LENGTH - UNPADDED_NONCE_LENGTH,
|
||||||
buffer.length - UNPADDED_NONCE_LENGTH,
|
buffer.length - UNPADDED_NONCE_LENGTH,
|
||||||
@@ -97,11 +101,18 @@ class PacketHandler extends EventEmitter {
|
|||||||
let packet;
|
let packet;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 'aead_aes256_gcm_rtpsize': {
|
case 'aead_aes256_gcm_rtpsize': {
|
||||||
const decipheriv = crypto.createDecipheriv('aes-256-gcm', secret_key, nonce);
|
const decipheriv = crypto.createDecipheriv(
|
||||||
|
'aes-256-gcm',
|
||||||
|
secret_key,
|
||||||
|
nonce,
|
||||||
|
);
|
||||||
decipheriv.setAAD(header);
|
decipheriv.setAAD(header);
|
||||||
decipheriv.setAuthTag(authTag);
|
decipheriv.setAuthTag(authTag);
|
||||||
|
|
||||||
packet = Buffer.concat([decipheriv.update(encrypted), decipheriv.final()]);
|
packet = Buffer.concat([
|
||||||
|
decipheriv.update(encrypted),
|
||||||
|
decipheriv.final(),
|
||||||
|
]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'aead_xchacha20_poly1305_rtpsize': {
|
case 'aead_xchacha20_poly1305_rtpsize': {
|
||||||
@@ -163,10 +174,18 @@ class PacketHandler extends EventEmitter {
|
|||||||
if (userStat.speaking === 0) {
|
if (userStat.speaking === 0) {
|
||||||
userStat.speaking = Speaking.FLAGS.SPEAKING;
|
userStat.speaking = Speaking.FLAGS.SPEAKING;
|
||||||
}
|
}
|
||||||
this.connection.onSpeaking({ user_id: userStat.userId, ssrc: ssrc, speaking: userStat.speaking });
|
this.connection.onSpeaking({
|
||||||
|
user_id: userStat.userId,
|
||||||
|
ssrc: ssrc,
|
||||||
|
speaking: userStat.speaking,
|
||||||
|
});
|
||||||
speakingTimeout = setTimeout(() => {
|
speakingTimeout = setTimeout(() => {
|
||||||
try {
|
try {
|
||||||
this.connection.onSpeaking({ user_id: userStat.userId, ssrc: ssrc, speaking: 0 });
|
this.connection.onSpeaking({
|
||||||
|
user_id: userStat.userId,
|
||||||
|
ssrc: ssrc,
|
||||||
|
speaking: 0,
|
||||||
|
});
|
||||||
clearTimeout(speakingTimeout);
|
clearTimeout(speakingTimeout);
|
||||||
this.speakingTimeouts.delete(ssrc);
|
this.speakingTimeouts.delete(ssrc);
|
||||||
} catch {
|
} catch {
|
||||||
@@ -241,7 +260,8 @@ class PacketHandler extends EventEmitter {
|
|||||||
packet = this.parseBuffer(buffer);
|
packet = this.parseBuffer(buffer);
|
||||||
this.videoReceiver(ssrc, userStat, packet);
|
this.videoReceiver(ssrc, userStat, packet);
|
||||||
}
|
}
|
||||||
if (userStat && !(packet instanceof Error)) this.receiver.emit('receiverData', userStat, packet);
|
if (userStat && !(packet instanceof Error))
|
||||||
|
this.receiver.emit('receiverData', userStat, packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When udp connection is closed (STREAM_DELETE), destroy all streams (Memory leak)
|
// When udp connection is closed (STREAM_DELETE), destroy all streams (Memory leak)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class VoiceReceiver extends EventEmitter {
|
|||||||
* @event VoiceReceiver#debug
|
* @event VoiceReceiver#debug
|
||||||
* @param {Error|string} error The error or message to debug
|
* @param {Error|string} error The error or message to debug
|
||||||
*/
|
*/
|
||||||
this.packets.on('error', err => this.emit('debug', err));
|
this.packets.on('error', (err) => this.emit('debug', err));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,24 +45,39 @@ class VoiceReceiver extends EventEmitter {
|
|||||||
* @param {ReceiveStreamOptions} options Options.
|
* @param {ReceiveStreamOptions} options Options.
|
||||||
* @returns {ReadableStream}
|
* @returns {ReadableStream}
|
||||||
*/
|
*/
|
||||||
createStream(user, { mode = 'opus', end = 'silence', paddingSilence = false } = {}) {
|
createStream(
|
||||||
|
user,
|
||||||
|
{ mode = 'opus', end = 'silence', paddingSilence = false } = {},
|
||||||
|
) {
|
||||||
user = this.connection.client.users.resolve(user);
|
user = this.connection.client.users.resolve(user);
|
||||||
if (end === 'silence') paddingSilence = false;
|
if (end === 'silence') paddingSilence = false;
|
||||||
if (!user) throw new Error('VOICE_USER_MISSING');
|
if (!user) throw new Error('VOICE_USER_MISSING');
|
||||||
const stream = this.packets.makeStream(user.id, end); // Opus stream
|
const stream = this.packets.makeStream(user.id, end); // Opus stream
|
||||||
if (paddingSilence) {
|
if (paddingSilence) {
|
||||||
const decoder = new prism.opus.Decoder({ channels: 2, rate: 48000, frameSize: 960 });
|
const decoder = new prism.opus.Decoder({
|
||||||
|
channels: 2,
|
||||||
|
rate: 48000,
|
||||||
|
frameSize: 960,
|
||||||
|
});
|
||||||
const pcmTransformer = new PCMInsertSilence();
|
const pcmTransformer = new PCMInsertSilence();
|
||||||
stream.pipe(decoder).pipe(pcmTransformer);
|
stream.pipe(decoder).pipe(pcmTransformer);
|
||||||
if (mode === 'opus') {
|
if (mode === 'opus') {
|
||||||
const encoder = new prism.opus.Encoder({ channels: 2, rate: 48000, frameSize: 960 });
|
const encoder = new prism.opus.Encoder({
|
||||||
|
channels: 2,
|
||||||
|
rate: 48000,
|
||||||
|
frameSize: 960,
|
||||||
|
});
|
||||||
pcmTransformer.pipe(encoder);
|
pcmTransformer.pipe(encoder);
|
||||||
return encoder;
|
return encoder;
|
||||||
}
|
}
|
||||||
return pcmTransformer;
|
return pcmTransformer;
|
||||||
} else {
|
} else {
|
||||||
if (mode === 'pcm') {
|
if (mode === 'pcm') {
|
||||||
const decoder = new prism.opus.Decoder({ channels: 2, rate: 48000, frameSize: 960 });
|
const decoder = new prism.opus.Decoder({
|
||||||
|
channels: 2,
|
||||||
|
rate: 48000,
|
||||||
|
frameSize: 960,
|
||||||
|
});
|
||||||
stream.pipe(decoder);
|
stream.pipe(decoder);
|
||||||
return decoder;
|
return decoder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ class Recorder extends EventEmitter {
|
|||||||
this.promise = null;
|
this.promise = null;
|
||||||
|
|
||||||
if (!portUdpH264 || !portUdpOpus) {
|
if (!portUdpH264 || !portUdpOpus) {
|
||||||
this.promise = randomPorts(6, 'udp4').then(ports => {
|
this.promise = randomPorts(6, 'udp4').then((ports) => {
|
||||||
ports = ports.filter(port => port % 2 === 0);
|
ports = ports.filter((port) => port % 2 === 0);
|
||||||
this.portUdpH264 ??= ports[0];
|
this.portUdpH264 ??= ports[0];
|
||||||
this.portUdpOpus ??= ports[1];
|
this.portUdpOpus ??= ports[1];
|
||||||
});
|
});
|
||||||
@@ -60,7 +60,11 @@ class Recorder extends EventEmitter {
|
|||||||
}
|
}
|
||||||
async init(output) {
|
async init(output) {
|
||||||
await this.promise;
|
await this.promise;
|
||||||
const sdpData = Util.getSDPCodecName(this.portUdpH264, this.portUdpH265, this.portUdpOpus);
|
const sdpData = Util.getSDPCodecName(
|
||||||
|
this.portUdpH264,
|
||||||
|
this.portUdpH265,
|
||||||
|
this.portUdpOpus,
|
||||||
|
);
|
||||||
const isStream = output instanceof Writable;
|
const isStream = output instanceof Writable;
|
||||||
if (isStream) {
|
if (isStream) {
|
||||||
this.outputStream = StreamOutput(output);
|
this.outputStream = StreamOutput(output);
|
||||||
@@ -109,7 +113,7 @@ class Recorder extends EventEmitter {
|
|||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.stream.stdin.write(sdpData);
|
this.stream.stdin.write(sdpData);
|
||||||
this.stream.stdin.end();
|
this.stream.stdin.end();
|
||||||
this.stream.stderr.once('data', data => {
|
this.stream.stderr.once('data', (data) => {
|
||||||
this.emit('debug', `stderr: ${data}`);
|
this.emit('debug', `stderr: ${data}`);
|
||||||
this.ready = true;
|
this.ready = true;
|
||||||
this.emit('ready');
|
this.emit('ready');
|
||||||
@@ -122,14 +126,16 @@ class Recorder extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
feed(
|
feed(
|
||||||
payload,
|
payload,
|
||||||
callback = e => {
|
callback = (e) => {
|
||||||
if (e) {
|
if (e) {
|
||||||
console.error('Error sending packet:', e);
|
console.error('Error sending packet:', e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
if (!(payload instanceof RtpPacket)) {
|
if (!(payload instanceof RtpPacket)) {
|
||||||
payload = RtpPacket.deSerialize(Buffer.isBuffer(payload) ? payload : Buffer.from(payload));
|
payload = RtpPacket.deSerialize(
|
||||||
|
Buffer.isBuffer(payload) ? payload : Buffer.from(payload),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const message = payload.serialize();
|
const message = payload.serialize();
|
||||||
// Get port from payloadType
|
// Get port from payloadType
|
||||||
@@ -149,8 +155,11 @@ class Recorder extends EventEmitter {
|
|||||||
destroy() {
|
destroy() {
|
||||||
const ffmpegPid = this.stream.pid; // But it is ppid ;-;
|
const ffmpegPid = this.stream.pid; // But it is ppid ;-;
|
||||||
const args = this.stream.spawnargs.slice(1).join(' '); // Skip ffmpeg
|
const args = this.stream.spawnargs.slice(1).join(' '); // Skip ffmpeg
|
||||||
find('name', 'ffmpeg', true).then(list => {
|
find('name', 'ffmpeg', true).then((list) => {
|
||||||
let process = list.find(o => o.pid === ffmpegPid || o.ppid === ffmpegPid || o.cmd.includes(args));
|
let process = list.find(
|
||||||
|
(o) =>
|
||||||
|
o.pid === ffmpegPid || o.ppid === ffmpegPid || o.cmd.includes(args),
|
||||||
|
);
|
||||||
if (process) {
|
if (process) {
|
||||||
kill(process.pid);
|
kill(process.pid);
|
||||||
this.receiver?.videoStreams?.delete(this.userId);
|
this.receiver?.videoStreams?.delete(this.userId);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ async function randomPort(protocol = 'udp4', interfaceAddresses) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const port = socket.address()?.port;
|
const port = socket.address()?.port;
|
||||||
await new Promise(resolve => socket.close(resolve));
|
await new Promise((resolve) => socket.close(resolve));
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +53,11 @@ async function randomPort(protocol = 'udp4', interfaceAddresses) {
|
|||||||
* @returns {Promise<number[]>} An array of assigned random ports.
|
* @returns {Promise<number[]>} An array of assigned random ports.
|
||||||
*/
|
*/
|
||||||
async function randomPorts(num, protocol = 'udp4', interfaceAddresses) {
|
async function randomPorts(num, protocol = 'udp4', interfaceAddresses) {
|
||||||
return Promise.all(Array.from({ length: num }).map(() => randomPort(protocol, interfaceAddresses)));
|
return Promise.all(
|
||||||
|
Array.from({ length: num }).map(() =>
|
||||||
|
randomPort(protocol, interfaceAddresses),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,12 +82,12 @@ async function findPort(min, max, protocol = 'udp4', interfaceAddresses) {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const error = await new Promise(resolve => {
|
const error = await new Promise((resolve) => {
|
||||||
socket.once('error', resolve);
|
socket.once('error', resolve);
|
||||||
socket.once('listening', () => resolve(null));
|
socket.once('listening', () => resolve(null));
|
||||||
});
|
});
|
||||||
|
|
||||||
await new Promise(resolve => socket.close(resolve));
|
await new Promise((resolve) => socket.close(resolve));
|
||||||
|
|
||||||
if (error) continue;
|
if (error) continue;
|
||||||
|
|
||||||
|
|||||||
@@ -64,11 +64,19 @@ class PlayInterface {
|
|||||||
} else if (type === 'opus') {
|
} else if (type === 'opus') {
|
||||||
return this.player.playOpusStream(resource, options);
|
return this.player.playOpusStream(resource, options);
|
||||||
} else if (type === 'ogg/opus') {
|
} else if (type === 'ogg/opus') {
|
||||||
if (!(resource instanceof Readable)) throw new Error('VOICE_PRISM_DEMUXERS_NEED_STREAM');
|
if (!(resource instanceof Readable))
|
||||||
return this.player.playOpusStream(resource.pipe(new prism.opus.OggDemuxer()), options);
|
throw new Error('VOICE_PRISM_DEMUXERS_NEED_STREAM');
|
||||||
|
return this.player.playOpusStream(
|
||||||
|
resource.pipe(new prism.opus.OggDemuxer()),
|
||||||
|
options,
|
||||||
|
);
|
||||||
} else if (type === 'webm/opus') {
|
} else if (type === 'webm/opus') {
|
||||||
if (!(resource instanceof Readable)) throw new Error('VOICE_PRISM_DEMUXERS_NEED_STREAM');
|
if (!(resource instanceof Readable))
|
||||||
return this.player.playOpusStream(resource.pipe(new prism.opus.WebmDemuxer()), options);
|
throw new Error('VOICE_PRISM_DEMUXERS_NEED_STREAM');
|
||||||
|
return this.player.playOpusStream(
|
||||||
|
resource.pipe(new prism.opus.WebmDemuxer()),
|
||||||
|
options,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error('VOICE_PLAY_INTERFACE_BAD_TYPE');
|
throw new Error('VOICE_PLAY_INTERFACE_BAD_TYPE');
|
||||||
@@ -114,7 +122,11 @@ class PlayInterface {
|
|||||||
|
|
||||||
static applyToClass(structure) {
|
static applyToClass(structure) {
|
||||||
for (const prop of ['playAudio', 'playVideo']) {
|
for (const prop of ['playAudio', 'playVideo']) {
|
||||||
Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(PlayInterface.prototype, prop));
|
Object.defineProperty(
|
||||||
|
structure.prototype,
|
||||||
|
prop,
|
||||||
|
Object.getOwnPropertyDescriptor(PlayInterface.prototype, prop),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,78 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const libs = {
|
const libs = {
|
||||||
sodium: sodium => ({
|
sodium: (sodium) => ({
|
||||||
crypto_aead_xchacha20poly1305_ietf_encrypt: (plaintext, additionalData, nonce, key) =>
|
crypto_aead_xchacha20poly1305_ietf_encrypt: (
|
||||||
sodium.api.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, additionalData, null, nonce, key),
|
plaintext,
|
||||||
crypto_aead_xchacha20poly1305_ietf_decrypt: (plaintext, additionalData, nonce, key) =>
|
additionalData,
|
||||||
sodium.api.crypto_aead_xchacha20poly1305_ietf_decrypt(plaintext, additionalData, null, nonce, key),
|
nonce,
|
||||||
|
key,
|
||||||
|
) =>
|
||||||
|
sodium.api.crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||||
|
plaintext,
|
||||||
|
additionalData,
|
||||||
|
null,
|
||||||
|
nonce,
|
||||||
|
key,
|
||||||
|
),
|
||||||
|
crypto_aead_xchacha20poly1305_ietf_decrypt: (
|
||||||
|
plaintext,
|
||||||
|
additionalData,
|
||||||
|
nonce,
|
||||||
|
key,
|
||||||
|
) =>
|
||||||
|
sodium.api.crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||||
|
plaintext,
|
||||||
|
additionalData,
|
||||||
|
null,
|
||||||
|
nonce,
|
||||||
|
key,
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
'libsodium-wrappers': sodium => ({
|
'libsodium-wrappers': (sodium) => ({
|
||||||
crypto_aead_xchacha20poly1305_ietf_encrypt: (plaintext, additionalData, nonce, key) =>
|
crypto_aead_xchacha20poly1305_ietf_encrypt: (
|
||||||
sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, additionalData, null, nonce, key),
|
plaintext,
|
||||||
crypto_aead_xchacha20poly1305_ietf_decrypt: (plaintext, additionalData, nonce, key) =>
|
additionalData,
|
||||||
sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null, plaintext, additionalData, nonce, key),
|
nonce,
|
||||||
|
key,
|
||||||
|
) =>
|
||||||
|
sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||||
|
plaintext,
|
||||||
|
additionalData,
|
||||||
|
null,
|
||||||
|
nonce,
|
||||||
|
key,
|
||||||
|
),
|
||||||
|
crypto_aead_xchacha20poly1305_ietf_decrypt: (
|
||||||
|
plaintext,
|
||||||
|
additionalData,
|
||||||
|
nonce,
|
||||||
|
key,
|
||||||
|
) =>
|
||||||
|
sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||||
|
null,
|
||||||
|
plaintext,
|
||||||
|
additionalData,
|
||||||
|
nonce,
|
||||||
|
key,
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
'@stablelib/xchacha20poly1305': stablelib => ({
|
'@stablelib/xchacha20poly1305': (stablelib) => ({
|
||||||
crypto_aead_xchacha20poly1305_ietf_encrypt(cipherText, additionalData, nonce, key) {
|
crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||||
|
cipherText,
|
||||||
|
additionalData,
|
||||||
|
nonce,
|
||||||
|
key,
|
||||||
|
) {
|
||||||
const crypto = new stablelib.XChaCha20Poly1305(key);
|
const crypto = new stablelib.XChaCha20Poly1305(key);
|
||||||
return crypto.seal(nonce, cipherText, additionalData);
|
return crypto.seal(nonce, cipherText, additionalData);
|
||||||
},
|
},
|
||||||
crypto_aead_xchacha20poly1305_ietf_decrypt(plaintext, additionalData, nonce, key) {
|
crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||||
|
plaintext,
|
||||||
|
additionalData,
|
||||||
|
nonce,
|
||||||
|
key,
|
||||||
|
) {
|
||||||
const crypto = new stablelib.XChaCha20Poly1305(key);
|
const crypto = new stablelib.XChaCha20Poly1305(key);
|
||||||
return crypto.open(nonce, plaintext, additionalData);
|
return crypto.open(nonce, plaintext, additionalData);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -52,11 +52,11 @@ class UnixStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function StreamInput(stream) {
|
function StreamInput(stream) {
|
||||||
return new UnixStream(stream, socket => stream.pipe(socket));
|
return new UnixStream(stream, (socket) => stream.pipe(socket));
|
||||||
}
|
}
|
||||||
|
|
||||||
function StreamOutput(stream) {
|
function StreamOutput(stream) {
|
||||||
return new UnixStream(stream, socket => socket.pipe(stream));
|
return new UnixStream(stream, (socket) => socket.pipe(stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { StreamOutput, StreamInput, UnixStream };
|
module.exports = { StreamOutput, StreamInput, UnixStream };
|
||||||
|
|||||||
@@ -56,7 +56,10 @@ class VolumeInterface extends EventEmitter {
|
|||||||
const out = Buffer.alloc(buffer.length);
|
const out = Buffer.alloc(buffer.length);
|
||||||
for (let i = 0; i < buffer.length; i += 2) {
|
for (let i = 0; i < buffer.length; i += 2) {
|
||||||
if (i >= buffer.length - 1) break;
|
if (i >= buffer.length - 1) break;
|
||||||
const uint = Math.min(32767, Math.max(-32767, Math.floor(volume * buffer.readInt16LE(i))));
|
const uint = Math.min(
|
||||||
|
32767,
|
||||||
|
Math.max(-32767, Math.floor(volume * buffer.readInt16LE(i))),
|
||||||
|
);
|
||||||
out.writeInt16LE(uint, i);
|
out.writeInt16LE(uint, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,10 +98,19 @@ class VolumeInterface extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = ['volumeDecibels', 'volumeLogarithmic', 'setVolumeDecibels', 'setVolumeLogarithmic'];
|
const props = [
|
||||||
|
'volumeDecibels',
|
||||||
|
'volumeLogarithmic',
|
||||||
|
'setVolumeDecibels',
|
||||||
|
'setVolumeLogarithmic',
|
||||||
|
];
|
||||||
|
|
||||||
exports.applyToClass = function applyToClass(structure) {
|
exports.applyToClass = function applyToClass(structure) {
|
||||||
for (const prop of props) {
|
for (const prop of props) {
|
||||||
Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(VolumeInterface.prototype, prop));
|
Object.defineProperty(
|
||||||
|
structure.prototype,
|
||||||
|
prop,
|
||||||
|
Object.getOwnPropertyDescriptor(VolumeInterface.prototype, prop),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,13 @@ const { RPCErrorCodes } = require('discord-api-types/v10');
|
|||||||
const WebSocketShard = require('./WebSocketShard');
|
const WebSocketShard = require('./WebSocketShard');
|
||||||
const PacketHandlers = require('./handlers');
|
const PacketHandlers = require('./handlers');
|
||||||
const { Error } = require('../../errors');
|
const { Error } = require('../../errors');
|
||||||
const { Events, ShardEvents, Status, WSCodes, WSEvents } = require('../../util/Constants');
|
const {
|
||||||
|
Events,
|
||||||
|
ShardEvents,
|
||||||
|
Status,
|
||||||
|
WSCodes,
|
||||||
|
WSEvents,
|
||||||
|
} = require('../../util/Constants');
|
||||||
|
|
||||||
const BeforeReadyWhitelist = [
|
const BeforeReadyWhitelist = [
|
||||||
WSEvents.READY,
|
WSEvents.READY,
|
||||||
@@ -70,7 +76,10 @@ class WebSocketManager extends EventEmitter {
|
|||||||
* @private
|
* @private
|
||||||
* @name WebSocketManager#shardQueue
|
* @name WebSocketManager#shardQueue
|
||||||
*/
|
*/
|
||||||
Object.defineProperty(this, 'shardQueue', { value: new Set(), writable: true });
|
Object.defineProperty(this, 'shardQueue', {
|
||||||
|
value: new Set(),
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of queued events before this WebSocketManager became ready
|
* An array of queued events before this WebSocketManager became ready
|
||||||
@@ -118,7 +127,10 @@ class WebSocketManager extends EventEmitter {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
debug(message, shard) {
|
debug(message, shard) {
|
||||||
this.client.emit(Events.DEBUG, `[WS => ${shard ? `Shard ${shard.id}` : 'Manager'}] ${message}`);
|
this.client.emit(
|
||||||
|
Events.DEBUG,
|
||||||
|
`[WS => ${shard ? `Shard ${shard.id}` : 'Manager'}] ${message}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,7 +141,7 @@ class WebSocketManager extends EventEmitter {
|
|||||||
let gatewayURL = 'wss://gateway.discord.gg';
|
let gatewayURL = 'wss://gateway.discord.gg';
|
||||||
await this.client.api.gateway
|
await this.client.api.gateway
|
||||||
.get({ auth: false })
|
.get({ auth: false })
|
||||||
.then(r => (gatewayURL = r.url))
|
.then((r) => (gatewayURL = r.url))
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
|
|
||||||
const total = Infinity;
|
const total = Infinity;
|
||||||
@@ -149,14 +161,19 @@ class WebSocketManager extends EventEmitter {
|
|||||||
let { shards } = this.client.options;
|
let { shards } = this.client.options;
|
||||||
|
|
||||||
if (shards === 'auto') {
|
if (shards === 'auto') {
|
||||||
this.debug(`Using the recommended shard count provided by Discord: ${recommendedShards}`);
|
this.debug(
|
||||||
|
`Using the recommended shard count provided by Discord: ${recommendedShards}`,
|
||||||
|
);
|
||||||
this.totalShards = this.client.options.shardCount = recommendedShards;
|
this.totalShards = this.client.options.shardCount = recommendedShards;
|
||||||
shards = this.client.options.shards = Array.from({ length: recommendedShards }, (_, i) => i);
|
shards = this.client.options.shards = Array.from(
|
||||||
|
{ length: recommendedShards },
|
||||||
|
(_, i) => i,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.totalShards = shards.length;
|
this.totalShards = shards.length;
|
||||||
this.debug(`Spawning shards: ${shards.join(', ')}`);
|
this.debug(`Spawning shards: ${shards.join(', ')}`);
|
||||||
this.shardQueue = new Set(shards.map(id => new WebSocketShard(this, id)));
|
this.shardQueue = new Set(shards.map((id) => new WebSocketShard(this, id)));
|
||||||
|
|
||||||
return this.createShards();
|
return this.createShards();
|
||||||
}
|
}
|
||||||
@@ -175,7 +192,7 @@ class WebSocketManager extends EventEmitter {
|
|||||||
this.shardQueue.delete(shard);
|
this.shardQueue.delete(shard);
|
||||||
|
|
||||||
if (!shard.eventsAttached) {
|
if (!shard.eventsAttached) {
|
||||||
shard.on(ShardEvents.ALL_READY, unavailableGuilds => {
|
shard.on(ShardEvents.ALL_READY, (unavailableGuilds) => {
|
||||||
/**
|
/**
|
||||||
* Emitted when a shard turns ready.
|
* Emitted when a shard turns ready.
|
||||||
* @event Client#shardReady
|
* @event Client#shardReady
|
||||||
@@ -188,8 +205,12 @@ class WebSocketManager extends EventEmitter {
|
|||||||
this.checkShardsReady();
|
this.checkShardsReady();
|
||||||
});
|
});
|
||||||
|
|
||||||
shard.on(ShardEvents.CLOSE, event => {
|
shard.on(ShardEvents.CLOSE, (event) => {
|
||||||
if (event.code === 1_000 ? this.destroyed : UNRECOVERABLE_CLOSE_CODES.includes(event.code)) {
|
if (
|
||||||
|
event.code === 1_000
|
||||||
|
? this.destroyed
|
||||||
|
: UNRECOVERABLE_CLOSE_CODES.includes(event.code)
|
||||||
|
) {
|
||||||
/**
|
/**
|
||||||
* Emitted when a shard's WebSocket disconnects and will no longer reconnect.
|
* Emitted when a shard's WebSocket disconnects and will no longer reconnect.
|
||||||
* @event Client#shardDisconnect
|
* @event Client#shardDisconnect
|
||||||
@@ -215,7 +236,11 @@ class WebSocketManager extends EventEmitter {
|
|||||||
|
|
||||||
this.shardQueue.add(shard);
|
this.shardQueue.add(shard);
|
||||||
|
|
||||||
if (shard.sessionId) this.debug(`Session id is present, attempting an immediate reconnect...`, shard);
|
if (shard.sessionId)
|
||||||
|
this.debug(
|
||||||
|
`Session id is present, attempting an immediate reconnect...`,
|
||||||
|
shard,
|
||||||
|
);
|
||||||
this.reconnect();
|
this.reconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -224,7 +249,10 @@ class WebSocketManager extends EventEmitter {
|
|||||||
});
|
});
|
||||||
|
|
||||||
shard.on(ShardEvents.DESTROYED, () => {
|
shard.on(ShardEvents.DESTROYED, () => {
|
||||||
this.debug('Shard was destroyed but no WebSocket connection was present! Reconnecting...', shard);
|
this.debug(
|
||||||
|
'Shard was destroyed but no WebSocket connection was present! Reconnecting...',
|
||||||
|
shard,
|
||||||
|
);
|
||||||
|
|
||||||
this.client.emit(Events.SHARD_RECONNECTING, shard.id);
|
this.client.emit(Events.SHARD_RECONNECTING, shard.id);
|
||||||
|
|
||||||
@@ -252,7 +280,9 @@ class WebSocketManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
// If we have more shards, add a 5s delay
|
// If we have more shards, add a 5s delay
|
||||||
if (this.shardQueue.size) {
|
if (this.shardQueue.size) {
|
||||||
this.debug(`Shard Queue Size: ${this.shardQueue.size}; continuing in 5 seconds...`);
|
this.debug(
|
||||||
|
`Shard Queue Size: ${this.shardQueue.size}; continuing in 5 seconds...`,
|
||||||
|
);
|
||||||
await sleep(5_000);
|
await sleep(5_000);
|
||||||
return this.createShards();
|
return this.createShards();
|
||||||
}
|
}
|
||||||
@@ -271,7 +301,9 @@ class WebSocketManager extends EventEmitter {
|
|||||||
try {
|
try {
|
||||||
await this.createShards();
|
await this.createShards();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.debug(`Couldn't reconnect or fetch information about the gateway. ${error}`);
|
this.debug(
|
||||||
|
`Couldn't reconnect or fetch information about the gateway. ${error}`,
|
||||||
|
);
|
||||||
if (error.httpStatus !== 401) {
|
if (error.httpStatus !== 401) {
|
||||||
this.debug(`Possible network error occurred. Retrying in 5s...`);
|
this.debug(`Possible network error occurred. Retrying in 5s...`);
|
||||||
await sleep(5_000);
|
await sleep(5_000);
|
||||||
@@ -313,10 +345,13 @@ class WebSocketManager extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
destroy() {
|
destroy() {
|
||||||
if (this.destroyed) return;
|
if (this.destroyed) return;
|
||||||
this.debug(`Manager was destroyed. Called by:\n${new Error('MANAGER_DESTROYED').stack}`);
|
this.debug(
|
||||||
|
`Manager was destroyed. Called by:\n${new Error('MANAGER_DESTROYED').stack}`,
|
||||||
|
);
|
||||||
this.destroyed = true;
|
this.destroyed = true;
|
||||||
this.shardQueue.clear();
|
this.shardQueue.clear();
|
||||||
for (const shard of this.shards.values()) shard.destroy({ closeCode: 1_000, reset: true, emit: false, log: false });
|
for (const shard of this.shards.values())
|
||||||
|
shard.destroy({ closeCode: 1_000, reset: true, emit: false, log: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -362,7 +397,10 @@ class WebSocketManager extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
checkShardsReady() {
|
checkShardsReady() {
|
||||||
if (this.status === Status.READY) return;
|
if (this.status === Status.READY) return;
|
||||||
if (this.shards.size !== this.totalShards || this.shards.some(s => s.status !== Status.READY)) {
|
if (
|
||||||
|
this.shards.size !== this.totalShards ||
|
||||||
|
this.shards.some((s) => s.status !== Status.READY)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,14 @@
|
|||||||
const EventEmitter = require('node:events');
|
const EventEmitter = require('node:events');
|
||||||
const { setTimeout, setInterval, clearTimeout } = require('node:timers');
|
const { setTimeout, setInterval, clearTimeout } = require('node:timers');
|
||||||
const WebSocket = require('../../WebSocket');
|
const WebSocket = require('../../WebSocket');
|
||||||
const { Status, Events, ShardEvents, Opcodes, WSEvents, WSCodes } = require('../../util/Constants');
|
const {
|
||||||
|
Status,
|
||||||
|
Events,
|
||||||
|
ShardEvents,
|
||||||
|
Opcodes,
|
||||||
|
WSEvents,
|
||||||
|
WSCodes,
|
||||||
|
} = require('../../util/Constants');
|
||||||
const Intents = require('../../util/Intents');
|
const Intents = require('../../util/Intents');
|
||||||
const Util = require('../../util/Util');
|
const Util = require('../../util/Util');
|
||||||
|
|
||||||
@@ -140,7 +147,10 @@ class WebSocketShard extends EventEmitter {
|
|||||||
* @type {?NodeJS.Timeout}
|
* @type {?NodeJS.Timeout}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
Object.defineProperty(this, 'helloTimeout', { value: null, writable: true });
|
Object.defineProperty(this, 'helloTimeout', {
|
||||||
|
value: null,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The WebSocket timeout.
|
* The WebSocket timeout.
|
||||||
@@ -148,7 +158,10 @@ class WebSocketShard extends EventEmitter {
|
|||||||
* @type {?NodeJS.Timeout}
|
* @type {?NodeJS.Timeout}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
Object.defineProperty(this, 'wsCloseTimeout', { value: null, writable: true });
|
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
|
||||||
@@ -156,7 +169,10 @@ class WebSocketShard extends EventEmitter {
|
|||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
Object.defineProperty(this, 'eventsAttached', { value: false, writable: true });
|
Object.defineProperty(this, 'eventsAttached', {
|
||||||
|
value: false,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A set of guild ids this shard expects to receive
|
* A set of guild ids this shard expects to receive
|
||||||
@@ -164,7 +180,10 @@ class WebSocketShard extends EventEmitter {
|
|||||||
* @type {?Set<string>}
|
* @type {?Set<string>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
Object.defineProperty(this, 'expectedGuilds', { value: null, writable: true });
|
Object.defineProperty(this, 'expectedGuilds', {
|
||||||
|
value: null,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ready timeout
|
* The ready timeout
|
||||||
@@ -172,7 +191,10 @@ class WebSocketShard extends EventEmitter {
|
|||||||
* @type {?NodeJS.Timeout}
|
* @type {?NodeJS.Timeout}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
Object.defineProperty(this, 'readyTimeout', { value: null, writable: true });
|
Object.defineProperty(this, 'readyTimeout', {
|
||||||
|
value: null,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time when the WebSocket connection was opened
|
* Time when the WebSocket connection was opened
|
||||||
@@ -201,7 +223,10 @@ class WebSocketShard extends EventEmitter {
|
|||||||
connect() {
|
connect() {
|
||||||
const { client } = this.manager;
|
const { client } = this.manager;
|
||||||
|
|
||||||
if (this.connection?.readyState === WebSocket.OPEN && this.status === Status.READY) {
|
if (
|
||||||
|
this.connection?.readyState === WebSocket.OPEN &&
|
||||||
|
this.status === Status.READY
|
||||||
|
) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +251,7 @@ class WebSocketShard extends EventEmitter {
|
|||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClose = event => {
|
const onClose = (event) => {
|
||||||
cleanup();
|
cleanup();
|
||||||
reject(event);
|
reject(event);
|
||||||
};
|
};
|
||||||
@@ -244,7 +269,9 @@ class WebSocketShard extends EventEmitter {
|
|||||||
this.once(ShardEvents.DESTROYED, onInvalidOrDestroyed);
|
this.once(ShardEvents.DESTROYED, onInvalidOrDestroyed);
|
||||||
|
|
||||||
if (this.connection?.readyState === WebSocket.OPEN) {
|
if (this.connection?.readyState === WebSocket.OPEN) {
|
||||||
this.debug('An open connection was found, attempting an immediate identify.');
|
this.debug(
|
||||||
|
'An open connection was found, attempting an immediate identify.',
|
||||||
|
);
|
||||||
this.identify();
|
this.identify();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -275,7 +302,10 @@ class WebSocketShard extends EventEmitter {
|
|||||||
Agent : ${Util.verifyProxyAgent(client.options.ws.agent)}`,
|
Agent : ${Util.verifyProxyAgent(client.options.ws.agent)}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
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.setWsCloseTimeout(-1);
|
||||||
this.connectedAt = Date.now();
|
this.connectedAt = Date.now();
|
||||||
@@ -283,7 +313,9 @@ class WebSocketShard extends EventEmitter {
|
|||||||
// Adding a handshake timeout to just make sure no zombie connection appears.
|
// Adding a handshake timeout to just make sure no zombie connection appears.
|
||||||
const ws = (this.connection = WebSocket.create(gateway, wsQuery, {
|
const ws = (this.connection = WebSocket.create(gateway, wsQuery, {
|
||||||
handshakeTimeout: 30_000,
|
handshakeTimeout: 30_000,
|
||||||
agent: Util.verifyProxyAgent(client.options.ws.agent) ? client.options.ws.agent : undefined,
|
agent: Util.verifyProxyAgent(client.options.ws.agent)
|
||||||
|
? client.options.ws.agent
|
||||||
|
: undefined,
|
||||||
}));
|
}));
|
||||||
ws.onopen = this.onOpen.bind(this);
|
ws.onopen = this.onOpen.bind(this);
|
||||||
ws.onmessage = this.onMessage.bind(this);
|
ws.onmessage = this.onMessage.bind(this);
|
||||||
@@ -312,7 +344,11 @@ class WebSocketShard extends EventEmitter {
|
|||||||
if (zlib) {
|
if (zlib) {
|
||||||
const l = data.length;
|
const l = data.length;
|
||||||
const flush =
|
const flush =
|
||||||
l >= 4 && data[l - 4] === 0x00 && data[l - 3] === 0x00 && data[l - 2] === 0xff && data[l - 1] === 0xff;
|
l >= 4 &&
|
||||||
|
data[l - 4] === 0x00 &&
|
||||||
|
data[l - 3] === 0x00 &&
|
||||||
|
data[l - 2] === 0xff &&
|
||||||
|
data[l - 1] === 0xff;
|
||||||
|
|
||||||
this.inflate.push(data, flush && zlib.Z_SYNC_FLUSH);
|
this.inflate.push(data, flush && zlib.Z_SYNC_FLUSH);
|
||||||
if (!flush) return;
|
if (!flush) return;
|
||||||
@@ -328,7 +364,8 @@ class WebSocketShard extends EventEmitter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.manager.client.emit(Events.RAW, packet, this.id);
|
this.manager.client.emit(Events.RAW, packet, this.id);
|
||||||
if (packet.op === Opcodes.DISPATCH) this.manager.emit(packet.t, packet.d, this.id);
|
if (packet.op === Opcodes.DISPATCH)
|
||||||
|
this.manager.emit(packet.t, packet.d, this.id);
|
||||||
this.onPacket(packet);
|
this.onPacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,9 +471,15 @@ class WebSocketShard extends EventEmitter {
|
|||||||
|
|
||||||
this.resumeURL = packet.d.resume_gateway_url;
|
this.resumeURL = packet.d.resume_gateway_url;
|
||||||
this.sessionId = packet.d.session_id;
|
this.sessionId = packet.d.session_id;
|
||||||
this.expectedGuilds = new Set(packet.d.guilds.filter(d => d?.unavailable == true).map(d => d.id));
|
this.expectedGuilds = new Set(
|
||||||
|
packet.d.guilds
|
||||||
|
.filter((d) => d?.unavailable == true)
|
||||||
|
.map((d) => d.id),
|
||||||
|
);
|
||||||
this.status = Status.WAITING_FOR_GUILDS;
|
this.status = Status.WAITING_FOR_GUILDS;
|
||||||
this.debug(`[READY] Session ${this.sessionId} | Resume url ${this.resumeURL}.`);
|
this.debug(
|
||||||
|
`[READY] Session ${this.sessionId} | Resume url ${this.resumeURL}.`,
|
||||||
|
);
|
||||||
this.lastHeartbeatAcked = true;
|
this.lastHeartbeatAcked = true;
|
||||||
this.sendHeartbeat('ReadyHeartbeat');
|
this.sendHeartbeat('ReadyHeartbeat');
|
||||||
break;
|
break;
|
||||||
@@ -449,7 +492,9 @@ class WebSocketShard extends EventEmitter {
|
|||||||
|
|
||||||
this.status = Status.READY;
|
this.status = Status.READY;
|
||||||
const replayed = packet.s - this.closeSequence;
|
const replayed = packet.s - this.closeSequence;
|
||||||
this.debug(`[RESUMED] Session ${this.sessionId} | Replayed ${replayed} events.`);
|
this.debug(
|
||||||
|
`[RESUMED] Session ${this.sessionId} | Replayed ${replayed} events.`,
|
||||||
|
);
|
||||||
this.lastHeartbeatAcked = true;
|
this.lastHeartbeatAcked = true;
|
||||||
this.sendHeartbeat('ResumeHeartbeat');
|
this.sendHeartbeat('ResumeHeartbeat');
|
||||||
break;
|
break;
|
||||||
@@ -496,7 +541,10 @@ class WebSocketShard extends EventEmitter {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.manager.handlePacket(packet, this);
|
this.manager.handlePacket(packet, this);
|
||||||
if (this.status === Status.WAITING_FOR_GUILDS && packet.t === WSEvents.GUILD_CREATE) {
|
if (
|
||||||
|
this.status === Status.WAITING_FOR_GUILDS &&
|
||||||
|
packet.t === WSEvents.GUILD_CREATE
|
||||||
|
) {
|
||||||
this.expectedGuilds.delete(packet.d.id);
|
this.expectedGuilds.delete(packet.d.id);
|
||||||
this.checkReady();
|
this.checkReady();
|
||||||
}
|
}
|
||||||
@@ -529,7 +577,9 @@ class WebSocketShard extends EventEmitter {
|
|||||||
this.emit(ShardEvents.ALL_READY);
|
this.emit(ShardEvents.ALL_READY);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const hasGuildsIntent = new Intents(this.manager.client.options.intents).has(Intents.FLAGS.GUILDS);
|
const hasGuildsIntent = new Intents(
|
||||||
|
this.manager.client.options.intents,
|
||||||
|
).has(Intents.FLAGS.GUILDS);
|
||||||
// Step 2. Create a timeout that will mark the shard as ready if there are still unavailable guilds
|
// Step 2. Create a timeout that will mark the shard as ready if there are still unavailable guilds
|
||||||
// * The timeout is 15 seconds by default
|
// * The timeout is 15 seconds by default
|
||||||
// * This can be optionally changed in the client options via the `waitGuildTimeout` option
|
// * This can be optionally changed in the client options via the `waitGuildTimeout` option
|
||||||
@@ -572,7 +622,9 @@ class WebSocketShard extends EventEmitter {
|
|||||||
}
|
}
|
||||||
this.debug('Setting a HELLO timeout for 20s.');
|
this.debug('Setting a HELLO timeout for 20s.');
|
||||||
this.helloTimeout = setTimeout(() => {
|
this.helloTimeout = setTimeout(() => {
|
||||||
this.debug('Did not receive HELLO in time. Destroying and connecting again.');
|
this.debug(
|
||||||
|
'Did not receive HELLO in time. Destroying and connecting again.',
|
||||||
|
);
|
||||||
this.destroy({ reset: true, closeCode: 4009 });
|
this.destroy({ reset: true, closeCode: 4009 });
|
||||||
}, 20_000).unref();
|
}, 20_000).unref();
|
||||||
}
|
}
|
||||||
@@ -597,7 +649,9 @@ class WebSocketShard extends EventEmitter {
|
|||||||
|
|
||||||
// Check if close event was emitted.
|
// Check if close event was emitted.
|
||||||
if (this.closeEmitted) {
|
if (this.closeEmitted) {
|
||||||
this.debug(`[WebSocket] close was already emitted, assuming the connection was closed properly.`);
|
this.debug(
|
||||||
|
`[WebSocket] close was already emitted, assuming the connection was closed properly.`,
|
||||||
|
);
|
||||||
// Setting the variable false to check for zombie connections.
|
// Setting the variable false to check for zombie connections.
|
||||||
this.closeEmitted = false;
|
this.closeEmitted = false;
|
||||||
return;
|
return;
|
||||||
@@ -635,7 +689,10 @@ class WebSocketShard extends EventEmitter {
|
|||||||
this.debug(`Setting a heartbeat interval for ${time}ms.`);
|
this.debug(`Setting a heartbeat interval for ${time}ms.`);
|
||||||
// Sanity checks
|
// Sanity checks
|
||||||
if (this.heartbeatInterval) clearInterval(this.heartbeatInterval);
|
if (this.heartbeatInterval) clearInterval(this.heartbeatInterval);
|
||||||
this.heartbeatInterval = setInterval(() => this.sendHeartbeat(), time).unref();
|
this.heartbeatInterval = setInterval(
|
||||||
|
() => this.sendHeartbeat(),
|
||||||
|
time,
|
||||||
|
).unref();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -647,10 +704,16 @@ class WebSocketShard extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
sendHeartbeat(
|
sendHeartbeat(
|
||||||
tag = 'HeartbeatTimer',
|
tag = 'HeartbeatTimer',
|
||||||
ignoreHeartbeatAck = [Status.WAITING_FOR_GUILDS, Status.IDENTIFYING, Status.RESUMING].includes(this.status),
|
ignoreHeartbeatAck = [
|
||||||
|
Status.WAITING_FOR_GUILDS,
|
||||||
|
Status.IDENTIFYING,
|
||||||
|
Status.RESUMING,
|
||||||
|
].includes(this.status),
|
||||||
) {
|
) {
|
||||||
if (ignoreHeartbeatAck && !this.lastHeartbeatAcked) {
|
if (ignoreHeartbeatAck && !this.lastHeartbeatAcked) {
|
||||||
this.debug(`[${tag}] Didn't process heartbeat ack yet but we are still connected. Sending one now.`);
|
this.debug(
|
||||||
|
`[${tag}] Didn't process heartbeat ack yet but we are still connected. Sending one now.`,
|
||||||
|
);
|
||||||
} else if (!this.lastHeartbeatAcked) {
|
} else if (!this.lastHeartbeatAcked) {
|
||||||
this.debug(
|
this.debug(
|
||||||
`[${tag}] Didn't receive a heartbeat ack last time, assuming zombie connection. Destroying and reconnecting.
|
`[${tag}] Didn't receive a heartbeat ack last time, assuming zombie connection. Destroying and reconnecting.
|
||||||
@@ -703,9 +766,10 @@ class WebSocketShard extends EventEmitter {
|
|||||||
|
|
||||||
// Patch something
|
// Patch something
|
||||||
Object.keys(client.options.ws.properties)
|
Object.keys(client.options.ws.properties)
|
||||||
.filter(k => k.startsWith('$'))
|
.filter((k) => k.startsWith('$'))
|
||||||
.forEach(k => {
|
.forEach((k) => {
|
||||||
client.options.ws.properties[k.slice(1)] = client.options.ws.properties[k];
|
client.options.ws.properties[k.slice(1)] =
|
||||||
|
client.options.ws.properties[k];
|
||||||
delete client.options.ws.properties[k];
|
delete client.options.ws.properties[k];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -728,14 +792,18 @@ class WebSocketShard extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
identifyResume() {
|
identifyResume() {
|
||||||
if (!this.sessionId) {
|
if (!this.sessionId) {
|
||||||
this.debug('[RESUME] No session id was present; identifying as a new session.');
|
this.debug(
|
||||||
|
'[RESUME] No session id was present; identifying as a new session.',
|
||||||
|
);
|
||||||
this.identifyNew();
|
this.identifyNew();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.status = Status.RESUMING;
|
this.status = Status.RESUMING;
|
||||||
|
|
||||||
this.debug(`[RESUME] Session ${this.sessionId}, sequence ${this.closeSequence}`);
|
this.debug(
|
||||||
|
`[RESUME] Session ${this.sessionId}, sequence ${this.closeSequence}`,
|
||||||
|
);
|
||||||
|
|
||||||
const d = {
|
const d = {
|
||||||
token: this.manager.client.token,
|
token: this.manager.client.token,
|
||||||
@@ -767,13 +835,15 @@ class WebSocketShard extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
_send(data) {
|
_send(data) {
|
||||||
if (this.connection?.readyState !== WebSocket.OPEN) {
|
if (this.connection?.readyState !== WebSocket.OPEN) {
|
||||||
this.debug(`Tried to send packet '${JSON.stringify(data)}' but no WebSocket is available!`);
|
this.debug(
|
||||||
|
`Tried to send packet '${JSON.stringify(data)}' but no WebSocket is available!`,
|
||||||
|
);
|
||||||
this.destroy({ closeCode: 4_000 });
|
this.destroy({ closeCode: 4_000 });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.debug(`[WebSocketShard] send packet '${JSON.stringify(data)}'`);
|
this.debug(`[WebSocketShard] send packet '${JSON.stringify(data)}'`);
|
||||||
this.connection.send(WebSocket.pack(data), err => {
|
this.connection.send(WebSocket.pack(data), (err) => {
|
||||||
if (err) this.manager.client.emit(Events.SHARD_ERROR, err, this.id);
|
if (err) this.manager.client.emit(Events.SHARD_ERROR, err, this.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -827,7 +897,9 @@ class WebSocketShard extends EventEmitter {
|
|||||||
// 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]}`);
|
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]}`);
|
||||||
@@ -886,7 +958,10 @@ class WebSocketShard extends EventEmitter {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_cleanupConnection() {
|
_cleanupConnection() {
|
||||||
this.connection.onopen = this.connection.onclose = this.connection.onmessage = null;
|
this.connection.onopen =
|
||||||
|
this.connection.onclose =
|
||||||
|
this.connection.onmessage =
|
||||||
|
null;
|
||||||
this.connection.onerror = () => null;
|
this.connection.onerror = () => null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,15 @@
|
|||||||
const { Events } = require('../../../util/Constants');
|
const { Events } = require('../../../util/Constants');
|
||||||
|
|
||||||
module.exports = (client, { d: data }) => {
|
module.exports = (client, { d: data }) => {
|
||||||
const commandManager = data.guild_id ? client.guilds.cache.get(data.guild_id)?.commands : client.application.commands;
|
const commandManager = data.guild_id
|
||||||
|
? client.guilds.cache.get(data.guild_id)?.commands
|
||||||
|
: client.application.commands;
|
||||||
if (!commandManager) return;
|
if (!commandManager) return;
|
||||||
|
|
||||||
const command = commandManager._add(data, data.application_id === client.application.id);
|
const command = commandManager._add(
|
||||||
|
data,
|
||||||
|
data.application_id === client.application.id,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emitted when a guild application command is created.
|
* Emitted when a guild application command is created.
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
const { Events } = require('../../../util/Constants');
|
const { Events } = require('../../../util/Constants');
|
||||||
|
|
||||||
module.exports = (client, { d: data }) => {
|
module.exports = (client, { d: data }) => {
|
||||||
const commandManager = data.guild_id ? client.guilds.cache.get(data.guild_id)?.commands : client.application.commands;
|
const commandManager = data.guild_id
|
||||||
|
? client.guilds.cache.get(data.guild_id)?.commands
|
||||||
|
: client.application.commands;
|
||||||
if (!commandManager) return;
|
if (!commandManager) return;
|
||||||
|
|
||||||
const isOwn = data.application_id === client.application.id;
|
const isOwn = data.application_id === client.application.id;
|
||||||
|
|||||||
@@ -3,11 +3,16 @@
|
|||||||
const { Events } = require('../../../util/Constants');
|
const { Events } = require('../../../util/Constants');
|
||||||
|
|
||||||
module.exports = (client, { d: data }) => {
|
module.exports = (client, { d: data }) => {
|
||||||
const commandManager = data.guild_id ? client.guilds.cache.get(data.guild_id)?.commands : client.application.commands;
|
const commandManager = data.guild_id
|
||||||
|
? client.guilds.cache.get(data.guild_id)?.commands
|
||||||
|
: client.application.commands;
|
||||||
if (!commandManager) return;
|
if (!commandManager) return;
|
||||||
|
|
||||||
const oldCommand = commandManager.cache.get(data.id)?._clone() ?? null;
|
const oldCommand = commandManager.cache.get(data.id)?._clone() ?? null;
|
||||||
const newCommand = commandManager._add(data, data.application_id === client.application.id);
|
const newCommand = commandManager._add(
|
||||||
|
data,
|
||||||
|
data.application_id === client.application.id,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emitted when a guild application command is updated.
|
* Emitted when a guild application command is updated.
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ const { Events } = require('../../../util/Constants');
|
|||||||
|
|
||||||
module.exports = (client, { d: data }) => {
|
module.exports = (client, { d: data }) => {
|
||||||
const channel = client.channels.cache.get(data.channel_id);
|
const channel = client.channels.cache.get(data.channel_id);
|
||||||
const time = data.last_pin_timestamp ? new Date(data.last_pin_timestamp).getTime() : null;
|
const time = data.last_pin_timestamp
|
||||||
|
? new Date(data.last_pin_timestamp).getTime()
|
||||||
|
: null;
|
||||||
|
|
||||||
if (channel) {
|
if (channel) {
|
||||||
// Discord sends null for last_pin_timestamp if the last pinned message was removed
|
// Discord sends null for last_pin_timestamp if the last pinned message was removed
|
||||||
|
|||||||
@@ -4,13 +4,19 @@ module.exports = (client, packet) => {
|
|||||||
const channel = client.channels.cache.get(packet.d.channel_id);
|
const channel = client.channels.cache.get(packet.d.channel_id);
|
||||||
if (channel) {
|
if (channel) {
|
||||||
if (!channel._recipients) channel._recipients = [];
|
if (!channel._recipients) channel._recipients = [];
|
||||||
channel._recipients = channel._recipients.filter(u => u.id !== packet.d.user.id);
|
channel._recipients = channel._recipients.filter(
|
||||||
|
(u) => u.id !== packet.d.user.id,
|
||||||
|
);
|
||||||
/**
|
/**
|
||||||
* Emitted whenever a recipient is removed from a group DM.
|
* Emitted whenever a recipient is removed from a group DM.
|
||||||
* @event Client#channelRecipientRemove
|
* @event Client#channelRecipientRemove
|
||||||
* @param {GroupDMChannel} channel Group DM channel
|
* @param {GroupDMChannel} channel Group DM channel
|
||||||
* @param {User} user User
|
* @param {User} user User
|
||||||
*/
|
*/
|
||||||
client.emit(Events.CHANNEL_RECIPIENT_REMOVE, channel, client.users._add(packet.d.user));
|
client.emit(
|
||||||
|
Events.CHANNEL_RECIPIENT_REMOVE,
|
||||||
|
channel,
|
||||||
|
client.users._add(packet.d.user),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ module.exports = (client, { d: data }) => {
|
|||||||
if (!guild) return;
|
if (!guild) return;
|
||||||
const members = new Collection();
|
const members = new Collection();
|
||||||
|
|
||||||
for (const member of data.members) members.set(member.user.id, guild.members._add(member));
|
for (const member of data.members)
|
||||||
|
members.set(member.user.id, guild.members._add(member));
|
||||||
if (data.presences) {
|
if (data.presences) {
|
||||||
for (const presence of data.presences) guild.presences._add(Object.assign(presence, { guild }));
|
for (const presence of data.presences)
|
||||||
|
guild.presences._add(Object.assign(presence, { guild }));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ module.exports = (client, { d: data }, shard) => {
|
|||||||
client.settings._patch(data.user_settings);
|
client.settings._patch(data.user_settings);
|
||||||
|
|
||||||
// GuildSetting
|
// GuildSetting
|
||||||
for (const gSetting of Array.isArray(data.user_guild_settings) ? data.user_guild_settings : []) {
|
for (const gSetting of Array.isArray(data.user_guild_settings)
|
||||||
|
? data.user_guild_settings
|
||||||
|
: []) {
|
||||||
const guild = client.guilds.cache.get(gSetting.guild_id);
|
const guild = client.guilds.cache.get(gSetting.guild_id);
|
||||||
if (guild) guild.settings._patch(gSetting);
|
if (guild) guild.settings._patch(gSetting);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ module.exports = (client, { d: data }) => {
|
|||||||
const oldNickname = client.relationships.friendNicknames.get(data.id);
|
const oldNickname = client.relationships.friendNicknames.get(data.id);
|
||||||
// Update
|
// Update
|
||||||
if (data.type) client.relationships.cache.set(data.id, data.type);
|
if (data.type) client.relationships.cache.set(data.id, data.type);
|
||||||
if (data.nickname) client.relationships.friendNicknames.set(data.id, data.nickname);
|
if (data.nickname)
|
||||||
if (data.since) client.relationships.sinceCache.set(data.id, new Date(data.since || 0));
|
client.relationships.friendNicknames.set(data.id, data.nickname);
|
||||||
|
if (data.since)
|
||||||
|
client.relationships.sinceCache.set(data.id, new Date(data.since || 0));
|
||||||
client.emit(
|
client.emit(
|
||||||
Events.RELATIONSHIP_UPDATE,
|
Events.RELATIONSHIP_UPDATE,
|
||||||
data.id,
|
data.id,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ module.exports = (client, { d: data }) => {
|
|||||||
'[USER_REQUIRED_ACTION] Successfully accepted the new Terms of Service and Privacy Policy.',
|
'[USER_REQUIRED_ACTION] Successfully accepted the new Terms of Service and Privacy Policy.',
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch((e) => {
|
||||||
client.emit(
|
client.emit(
|
||||||
'debug',
|
'debug',
|
||||||
`[USER_REQUIRED_ACTION] Failed to accept the new Terms of Service and Privacy Policy: ${e}`,
|
`[USER_REQUIRED_ACTION] Failed to accept the new Terms of Service and Privacy Policy: ${e}`,
|
||||||
|
|||||||
@@ -12,5 +12,8 @@ module.exports = (client, { d: data }) => {
|
|||||||
* @event Client#voiceChannelEffectSend
|
* @event Client#voiceChannelEffectSend
|
||||||
* @param {VoiceChannelEffect} voiceChannelEffect The sent voice channel effect
|
* @param {VoiceChannelEffect} voiceChannelEffect The sent voice channel effect
|
||||||
*/
|
*/
|
||||||
client.emit(Events.VOICE_CHANNEL_EFFECT_SEND, new VoiceChannelEffect(data, guild));
|
client.emit(
|
||||||
|
Events.VOICE_CHANNEL_EFFECT_SEND,
|
||||||
|
new VoiceChannelEffect(data, guild),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.exports = (client, packet) => {
|
module.exports = (client, packet) => {
|
||||||
client.emit('debug', `[VOICE] received voice server: ${JSON.stringify(packet)}`);
|
client.emit(
|
||||||
|
'debug',
|
||||||
|
`[VOICE] received voice server: ${JSON.stringify(packet)}`,
|
||||||
|
);
|
||||||
client.voice.onVoiceServer(packet.d);
|
client.voice.onVoiceServer(packet.d);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,8 +6,14 @@ const handlers = Object.fromEntries([
|
|||||||
['APPLICATION_COMMAND_CREATE', require('./APPLICATION_COMMAND_CREATE')],
|
['APPLICATION_COMMAND_CREATE', require('./APPLICATION_COMMAND_CREATE')],
|
||||||
['APPLICATION_COMMAND_DELETE', require('./APPLICATION_COMMAND_DELETE')],
|
['APPLICATION_COMMAND_DELETE', require('./APPLICATION_COMMAND_DELETE')],
|
||||||
['APPLICATION_COMMAND_UPDATE', require('./APPLICATION_COMMAND_UPDATE')],
|
['APPLICATION_COMMAND_UPDATE', require('./APPLICATION_COMMAND_UPDATE')],
|
||||||
['APPLICATION_COMMAND_PERMISSIONS_UPDATE', require('./APPLICATION_COMMAND_PERMISSIONS_UPDATE')],
|
[
|
||||||
['AUTO_MODERATION_ACTION_EXECUTION', require('./AUTO_MODERATION_ACTION_EXECUTION')],
|
'APPLICATION_COMMAND_PERMISSIONS_UPDATE',
|
||||||
|
require('./APPLICATION_COMMAND_PERMISSIONS_UPDATE'),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'AUTO_MODERATION_ACTION_EXECUTION',
|
||||||
|
require('./AUTO_MODERATION_ACTION_EXECUTION'),
|
||||||
|
],
|
||||||
['AUTO_MODERATION_RULE_CREATE', require('./AUTO_MODERATION_RULE_CREATE')],
|
['AUTO_MODERATION_RULE_CREATE', require('./AUTO_MODERATION_RULE_CREATE')],
|
||||||
['AUTO_MODERATION_RULE_DELETE', require('./AUTO_MODERATION_RULE_DELETE')],
|
['AUTO_MODERATION_RULE_DELETE', require('./AUTO_MODERATION_RULE_DELETE')],
|
||||||
['AUTO_MODERATION_RULE_UPDATE', require('./AUTO_MODERATION_RULE_UPDATE')],
|
['AUTO_MODERATION_RULE_UPDATE', require('./AUTO_MODERATION_RULE_UPDATE')],
|
||||||
@@ -59,8 +65,14 @@ const handlers = Object.fromEntries([
|
|||||||
['GUILD_SCHEDULED_EVENT_CREATE', require('./GUILD_SCHEDULED_EVENT_CREATE')],
|
['GUILD_SCHEDULED_EVENT_CREATE', require('./GUILD_SCHEDULED_EVENT_CREATE')],
|
||||||
['GUILD_SCHEDULED_EVENT_UPDATE', require('./GUILD_SCHEDULED_EVENT_UPDATE')],
|
['GUILD_SCHEDULED_EVENT_UPDATE', require('./GUILD_SCHEDULED_EVENT_UPDATE')],
|
||||||
['GUILD_SCHEDULED_EVENT_DELETE', require('./GUILD_SCHEDULED_EVENT_DELETE')],
|
['GUILD_SCHEDULED_EVENT_DELETE', require('./GUILD_SCHEDULED_EVENT_DELETE')],
|
||||||
['GUILD_SCHEDULED_EVENT_USER_ADD', require('./GUILD_SCHEDULED_EVENT_USER_ADD')],
|
[
|
||||||
['GUILD_SCHEDULED_EVENT_USER_REMOVE', require('./GUILD_SCHEDULED_EVENT_USER_REMOVE')],
|
'GUILD_SCHEDULED_EVENT_USER_ADD',
|
||||||
|
require('./GUILD_SCHEDULED_EVENT_USER_ADD'),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'GUILD_SCHEDULED_EVENT_USER_REMOVE',
|
||||||
|
require('./GUILD_SCHEDULED_EVENT_USER_REMOVE'),
|
||||||
|
],
|
||||||
['GUILD_AUDIT_LOG_ENTRY_CREATE', require('./GUILD_AUDIT_LOG_ENTRY_CREATE')],
|
['GUILD_AUDIT_LOG_ENTRY_CREATE', require('./GUILD_AUDIT_LOG_ENTRY_CREATE')],
|
||||||
// Selfbot
|
// Selfbot
|
||||||
['RELATIONSHIP_ADD', require('./RELATIONSHIP_ADD')],
|
['RELATIONSHIP_ADD', require('./RELATIONSHIP_ADD')],
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ function makeDiscordjsError(Base) {
|
|||||||
constructor(key, ...args) {
|
constructor(key, ...args) {
|
||||||
super(message(key, args));
|
super(message(key, args));
|
||||||
this[kCode] = key;
|
this[kCode] = key;
|
||||||
if (Error.captureStackTrace) Error.captureStackTrace(this, DiscordjsError);
|
if (Error.captureStackTrace)
|
||||||
|
Error.captureStackTrace(this, DiscordjsError);
|
||||||
}
|
}
|
||||||
|
|
||||||
get name() {
|
get name() {
|
||||||
@@ -35,7 +36,8 @@ function makeDiscordjsError(Base) {
|
|||||||
* @returns {string} Formatted string
|
* @returns {string} Formatted string
|
||||||
*/
|
*/
|
||||||
function message(key, args) {
|
function message(key, args) {
|
||||||
if (typeof key !== 'string') throw new Error('Error message key must be a string');
|
if (typeof key !== 'string')
|
||||||
|
throw new Error('Error message key must be a string');
|
||||||
const msg = messages.get(key);
|
const msg = messages.get(key);
|
||||||
if (!msg) throw new Error(`An invalid error message key was used: ${key}.`);
|
if (!msg) throw new Error(`An invalid error message key was used: ${key}.`);
|
||||||
if (typeof msg === 'function') return msg(...args);
|
if (typeof msg === 'function') return msg(...args);
|
||||||
|
|||||||
@@ -6,41 +6,51 @@ const Messages = {
|
|||||||
CLIENT_INVALID_OPTION: (prop, must) => `The ${prop} option must be ${must}`,
|
CLIENT_INVALID_OPTION: (prop, must) => `The ${prop} option must be ${must}`,
|
||||||
CLIENT_INVALID_PROVIDED_SHARDS: 'None of the provided shards were valid.',
|
CLIENT_INVALID_PROVIDED_SHARDS: 'None of the provided shards were valid.',
|
||||||
CLIENT_MISSING_INTENTS: 'Valid intents must be provided for the Client.',
|
CLIENT_MISSING_INTENTS: 'Valid intents must be provided for the Client.',
|
||||||
CLIENT_NOT_READY: action => `The client needs to be logged in to ${action}.`,
|
CLIENT_NOT_READY: (action) =>
|
||||||
|
`The client needs to be logged in to ${action}.`,
|
||||||
|
|
||||||
TOKEN_INVALID: 'An invalid token was provided.',
|
TOKEN_INVALID: 'An invalid token was provided.',
|
||||||
TOKEN_MISSING: 'Request to use token, but token was unavailable to the client.',
|
TOKEN_MISSING:
|
||||||
TOTPKEY_MISSING: 'Request to use mfa, but TOTPKey was not set in client options.',
|
'Request to use token, but token was unavailable to the client.',
|
||||||
|
TOTPKEY_MISSING:
|
||||||
|
'Request to use mfa, but TOTPKey was not set in client options.',
|
||||||
|
|
||||||
WS_CLOSE_REQUESTED: 'WebSocket closed due to user request.',
|
WS_CLOSE_REQUESTED: 'WebSocket closed due to user request.',
|
||||||
WS_CONNECTION_EXISTS: 'There is already an existing WebSocket connection.',
|
WS_CONNECTION_EXISTS: 'There is already an existing WebSocket connection.',
|
||||||
WS_NOT_OPEN: (data = 'data') => `WebSocket not open to send ${data}`,
|
WS_NOT_OPEN: (data = 'data') => `WebSocket not open to send ${data}`,
|
||||||
MANAGER_DESTROYED: 'Manager was destroyed.',
|
MANAGER_DESTROYED: 'Manager was destroyed.',
|
||||||
|
|
||||||
BITFIELD_INVALID: bit => `Invalid bitfield flag or number: ${bit}.`,
|
BITFIELD_INVALID: (bit) => `Invalid bitfield flag or number: ${bit}.`,
|
||||||
|
|
||||||
SHARDING_INVALID: '[Bot Token] Invalid shard settings were provided.',
|
SHARDING_INVALID: '[Bot Token] Invalid shard settings were provided.',
|
||||||
SHARDING_REQUIRED: '[Bot Token] This session would have handled too many guilds - Sharding is required.',
|
SHARDING_REQUIRED:
|
||||||
|
'[Bot Token] This session would have handled too many guilds - Sharding is required.',
|
||||||
INVALID_INTENTS: '[Bot Token] Invalid intent provided for WebSocket intents.',
|
INVALID_INTENTS: '[Bot Token] Invalid intent provided for WebSocket intents.',
|
||||||
DISALLOWED_INTENTS: '[Bot Token] Privileged intent provided is not enabled or whitelisted.',
|
DISALLOWED_INTENTS:
|
||||||
|
'[Bot Token] Privileged intent provided is not enabled or whitelisted.',
|
||||||
SHARDING_NO_SHARDS: 'No shards have been spawned.',
|
SHARDING_NO_SHARDS: 'No shards have been spawned.',
|
||||||
SHARDING_IN_PROCESS: 'Shards are still being spawned.',
|
SHARDING_IN_PROCESS: 'Shards are still being spawned.',
|
||||||
SHARDING_INVALID_EVAL_BROADCAST: 'Script to evaluate must be a function',
|
SHARDING_INVALID_EVAL_BROADCAST: 'Script to evaluate must be a function',
|
||||||
SHARDING_SHARD_NOT_FOUND: id => `Shard ${id} could not be found.`,
|
SHARDING_SHARD_NOT_FOUND: (id) => `Shard ${id} could not be found.`,
|
||||||
SHARDING_ALREADY_SPAWNED: count => `Already spawned ${count} shards.`,
|
SHARDING_ALREADY_SPAWNED: (count) => `Already spawned ${count} shards.`,
|
||||||
SHARDING_PROCESS_EXISTS: id => `Shard ${id} already has an active process.`,
|
SHARDING_PROCESS_EXISTS: (id) => `Shard ${id} already has an active process.`,
|
||||||
SHARDING_WORKER_EXISTS: id => `Shard ${id} already has an active worker.`,
|
SHARDING_WORKER_EXISTS: (id) => `Shard ${id} already has an active worker.`,
|
||||||
SHARDING_READY_TIMEOUT: id => `Shard ${id}'s Client took too long to become ready.`,
|
SHARDING_READY_TIMEOUT: (id) =>
|
||||||
SHARDING_READY_DISCONNECTED: id => `Shard ${id}'s Client disconnected before becoming ready.`,
|
`Shard ${id}'s Client took too long to become ready.`,
|
||||||
SHARDING_READY_DIED: id => `Shard ${id}'s process exited before its Client became ready.`,
|
SHARDING_READY_DISCONNECTED: (id) =>
|
||||||
SHARDING_NO_CHILD_EXISTS: id => `Shard ${id} has no active process or worker.`,
|
`Shard ${id}'s Client disconnected before becoming ready.`,
|
||||||
|
SHARDING_READY_DIED: (id) =>
|
||||||
|
`Shard ${id}'s process exited before its Client became ready.`,
|
||||||
|
SHARDING_NO_CHILD_EXISTS: (id) =>
|
||||||
|
`Shard ${id} has no active process or worker.`,
|
||||||
SHARDING_SHARD_MISCALCULATION: (shard, guild, count) =>
|
SHARDING_SHARD_MISCALCULATION: (shard, guild, count) =>
|
||||||
`Calculated invalid shard ${shard} for guild ${guild} with ${count} shards.`,
|
`Calculated invalid shard ${shard} for guild ${guild} with ${count} shards.`,
|
||||||
|
|
||||||
COLOR_RANGE: 'Color must be within the range 0 - 16777215 (0xFFFFFF).',
|
COLOR_RANGE: 'Color must be within the range 0 - 16777215 (0xFFFFFF).',
|
||||||
COLOR_CONVERT: 'Unable to convert color to a number.',
|
COLOR_CONVERT: 'Unable to convert color to a number.',
|
||||||
|
|
||||||
INVITE_OPTIONS_MISSING_CHANNEL: 'A valid guild channel must be provided when GuildScheduledEvent is EXTERNAL.',
|
INVITE_OPTIONS_MISSING_CHANNEL:
|
||||||
|
'A valid guild channel must be provided when GuildScheduledEvent is EXTERNAL.',
|
||||||
|
|
||||||
EMBED_TITLE: 'MessageEmbed title must be a string.',
|
EMBED_TITLE: 'MessageEmbed title must be a string.',
|
||||||
EMBED_FIELD_NAME: 'MessageEmbed field names must be non-empty strings.',
|
EMBED_FIELD_NAME: 'MessageEmbed field names must be non-empty strings.',
|
||||||
@@ -67,66 +77,82 @@ const Messages = {
|
|||||||
MODAL_CUSTOM_ID: 'Modal customId must be a string',
|
MODAL_CUSTOM_ID: 'Modal customId must be a string',
|
||||||
MODAL_TITLE: 'Modal title must be a string',
|
MODAL_TITLE: 'Modal title must be a string',
|
||||||
|
|
||||||
INTERACTION_COLLECTOR_ERROR: reason => `Collector received no interactions before ending with reason: ${reason}`,
|
INTERACTION_COLLECTOR_ERROR: (reason) =>
|
||||||
|
`Collector received no interactions before ending with reason: ${reason}`,
|
||||||
|
|
||||||
FILE_NOT_FOUND: file => `File could not be found: ${file}`,
|
FILE_NOT_FOUND: (file) => `File could not be found: ${file}`,
|
||||||
|
|
||||||
USER_BANNER_NOT_FETCHED: "You must fetch this user's banner before trying to generate its URL!",
|
USER_BANNER_NOT_FETCHED:
|
||||||
|
"You must fetch this user's banner before trying to generate its URL!",
|
||||||
USER_NO_DM_CHANNEL: 'No DM Channel exists!',
|
USER_NO_DM_CHANNEL: 'No DM Channel exists!',
|
||||||
|
|
||||||
VOICE_NOT_STAGE_CHANNEL: 'You are only allowed to do this in stage channels.',
|
VOICE_NOT_STAGE_CHANNEL: 'You are only allowed to do this in stage channels.',
|
||||||
|
|
||||||
VOICE_STATE_NOT_OWN:
|
VOICE_STATE_NOT_OWN:
|
||||||
'You cannot self-deafen/mute/request to speak on VoiceStates that do not belong to the ClientUser.',
|
'You cannot self-deafen/mute/request to speak on VoiceStates that do not belong to the ClientUser.',
|
||||||
VOICE_STATE_INVALID_TYPE: name => `${name} must be a boolean.`,
|
VOICE_STATE_INVALID_TYPE: (name) => `${name} must be a boolean.`,
|
||||||
|
|
||||||
REQ_RESOURCE_TYPE: 'The resource must be a string, Buffer or a valid file stream.',
|
REQ_RESOURCE_TYPE:
|
||||||
|
'The resource must be a string, Buffer or a valid file stream.',
|
||||||
|
|
||||||
IMAGE_FORMAT: format => `Invalid image format: ${format}`,
|
IMAGE_FORMAT: (format) => `Invalid image format: ${format}`,
|
||||||
IMAGE_SIZE: size => `Invalid image size: ${size}`,
|
IMAGE_SIZE: (size) => `Invalid image size: ${size}`,
|
||||||
|
|
||||||
MESSAGE_BULK_DELETE_TYPE: 'The messages must be an Array, Collection, or number.',
|
MESSAGE_BULK_DELETE_TYPE:
|
||||||
|
'The messages must be an Array, Collection, or number.',
|
||||||
MESSAGE_NONCE_TYPE: 'Message nonce must be an integer or a string.',
|
MESSAGE_NONCE_TYPE: 'Message nonce must be an integer or a string.',
|
||||||
MESSAGE_CONTENT_TYPE: 'Message content must be a non-empty string.',
|
MESSAGE_CONTENT_TYPE: 'Message content must be a non-empty string.',
|
||||||
|
|
||||||
SPLIT_MAX_LEN: 'Chunk exceeds the max length and contains no split characters.',
|
SPLIT_MAX_LEN:
|
||||||
|
'Chunk exceeds the max length and contains no split characters.',
|
||||||
|
|
||||||
BAN_RESOLVE_ID: (ban = false) => `Couldn't resolve the user id to ${ban ? 'ban' : 'unban'}.`,
|
BAN_RESOLVE_ID: (ban = false) =>
|
||||||
|
`Couldn't resolve the user id to ${ban ? 'ban' : 'unban'}.`,
|
||||||
FETCH_BAN_RESOLVE_ID: "Couldn't resolve the user id to fetch the ban.",
|
FETCH_BAN_RESOLVE_ID: "Couldn't resolve the user id to fetch the ban.",
|
||||||
|
|
||||||
PRUNE_DAYS_TYPE: 'Days must be a number',
|
PRUNE_DAYS_TYPE: 'Days must be a number',
|
||||||
|
|
||||||
GUILD_CHANNEL_RESOLVE: 'Could not resolve channel to a guild channel.',
|
GUILD_CHANNEL_RESOLVE: 'Could not resolve channel to a guild channel.',
|
||||||
GUILD_VOICE_CHANNEL_RESOLVE: 'Could not resolve channel to a guild voice channel.',
|
GUILD_VOICE_CHANNEL_RESOLVE:
|
||||||
|
'Could not resolve channel to a guild voice channel.',
|
||||||
GUILD_CHANNEL_ORPHAN: 'Could not find a parent to this guild channel.',
|
GUILD_CHANNEL_ORPHAN: 'Could not find a parent to this guild channel.',
|
||||||
GUILD_CHANNEL_UNOWNED: "The fetched channel does not belong to this manager's guild.",
|
GUILD_CHANNEL_UNOWNED:
|
||||||
|
"The fetched channel does not belong to this manager's guild.",
|
||||||
GUILD_OWNED: 'Guild is owned by the client.',
|
GUILD_OWNED: 'Guild is owned by the client.',
|
||||||
GUILD_MEMBERS_TIMEOUT: "Members didn't arrive in time.",
|
GUILD_MEMBERS_TIMEOUT: "Members didn't arrive in time.",
|
||||||
GUILD_UNCACHED_ME: 'The client user as a member of this guild is uncached.',
|
GUILD_UNCACHED_ME: 'The client user as a member of this guild is uncached.',
|
||||||
CHANNEL_NOT_CACHED: 'Could not find the channel where this message came from in the cache!',
|
CHANNEL_NOT_CACHED:
|
||||||
|
'Could not find the channel where this message came from in the cache!',
|
||||||
STAGE_CHANNEL_RESOLVE: 'Could not resolve channel to a stage channel.',
|
STAGE_CHANNEL_RESOLVE: 'Could not resolve channel to a stage channel.',
|
||||||
GUILD_SCHEDULED_EVENT_RESOLVE: 'Could not resolve the guild scheduled event.',
|
GUILD_SCHEDULED_EVENT_RESOLVE: 'Could not resolve the guild scheduled event.',
|
||||||
|
|
||||||
INVALID_TYPE: (name, expected, an = false) => `Supplied ${name} is not a${an ? 'n' : ''} ${expected}.`,
|
INVALID_TYPE: (name, expected, an = false) =>
|
||||||
INVALID_ELEMENT: (type, name, elem) => `Supplied ${type} ${name} includes an invalid element: ${elem}`,
|
`Supplied ${name} is not a${an ? 'n' : ''} ${expected}.`,
|
||||||
|
INVALID_ELEMENT: (type, name, elem) =>
|
||||||
|
`Supplied ${type} ${name} includes an invalid element: ${elem}`,
|
||||||
|
|
||||||
MESSAGE_THREAD_PARENT: 'The message was not sent in a guild text or news channel',
|
MESSAGE_THREAD_PARENT:
|
||||||
|
'The message was not sent in a guild text or news channel',
|
||||||
MESSAGE_EXISTING_THREAD: 'The message already has a thread',
|
MESSAGE_EXISTING_THREAD: 'The message already has a thread',
|
||||||
THREAD_INVITABLE_TYPE: type => `Invitable cannot be edited on ${type}`,
|
THREAD_INVITABLE_TYPE: (type) => `Invitable cannot be edited on ${type}`,
|
||||||
|
|
||||||
WEBHOOK_MESSAGE: 'The message was not sent by a webhook.',
|
WEBHOOK_MESSAGE: 'The message was not sent by a webhook.',
|
||||||
WEBHOOK_TOKEN_UNAVAILABLE: 'This action requires a webhook token, but none is available.',
|
WEBHOOK_TOKEN_UNAVAILABLE:
|
||||||
|
'This action requires a webhook token, but none is available.',
|
||||||
WEBHOOK_URL_INVALID: 'The provided webhook URL is not valid.',
|
WEBHOOK_URL_INVALID: 'The provided webhook URL is not valid.',
|
||||||
WEBHOOK_APPLICATION: 'This message webhook belongs to an application and cannot be fetched.',
|
WEBHOOK_APPLICATION:
|
||||||
|
'This message webhook belongs to an application and cannot be fetched.',
|
||||||
MESSAGE_REFERENCE_MISSING: 'The message does not reference another message',
|
MESSAGE_REFERENCE_MISSING: 'The message does not reference another message',
|
||||||
|
|
||||||
EMOJI_TYPE: 'Emoji must be a string or GuildEmoji/ReactionEmoji',
|
EMOJI_TYPE: 'Emoji must be a string or GuildEmoji/ReactionEmoji',
|
||||||
EMOJI_MANAGED: 'Emoji is managed and has no Author.',
|
EMOJI_MANAGED: 'Emoji is managed and has no Author.',
|
||||||
MISSING_MANAGE_EMOJIS_AND_STICKERS_PERMISSION: guild =>
|
MISSING_MANAGE_EMOJIS_AND_STICKERS_PERMISSION: (guild) =>
|
||||||
`Client must have Manage Emojis and Stickers permission in guild ${guild} to see emoji authors.`,
|
`Client must have Manage Emojis and Stickers permission in guild ${guild} to see emoji authors.`,
|
||||||
NOT_GUILD_STICKER: 'Sticker is a standard (non-guild) sticker and has no author.',
|
NOT_GUILD_STICKER:
|
||||||
|
'Sticker is a standard (non-guild) sticker and has no author.',
|
||||||
|
|
||||||
REACTION_RESOLVE_USER: "Couldn't resolve the user id to remove from the reaction.",
|
REACTION_RESOLVE_USER:
|
||||||
|
"Couldn't resolve the user id to remove from the reaction.",
|
||||||
|
|
||||||
VANITY_URL: 'This guild does not have the VANITY_URL feature enabled.',
|
VANITY_URL: 'This guild does not have the VANITY_URL feature enabled.',
|
||||||
|
|
||||||
@@ -134,55 +160,73 @@ const Messages = {
|
|||||||
|
|
||||||
INVITE_NOT_FOUND: 'Could not find the requested invite.',
|
INVITE_NOT_FOUND: 'Could not find the requested invite.',
|
||||||
|
|
||||||
DELETE_GROUP_DM_CHANNEL: "Bots don't have access to Group DM Channels and cannot delete them",
|
DELETE_GROUP_DM_CHANNEL:
|
||||||
FETCH_GROUP_DM_CHANNEL: "Bots don't have access to Group DM Channels and cannot fetch them",
|
"Bots don't have access to Group DM Channels and cannot delete them",
|
||||||
|
FETCH_GROUP_DM_CHANNEL:
|
||||||
|
"Bots don't have access to Group DM Channels and cannot fetch them",
|
||||||
|
|
||||||
MEMBER_FETCH_NONCE_LENGTH: 'Nonce length must not exceed 32 characters.',
|
MEMBER_FETCH_NONCE_LENGTH: 'Nonce length must not exceed 32 characters.',
|
||||||
|
|
||||||
GLOBAL_COMMAND_PERMISSIONS:
|
GLOBAL_COMMAND_PERMISSIONS:
|
||||||
'Permissions for global commands may only be fetched or modified by providing a GuildResolvable ' +
|
'Permissions for global commands may only be fetched or modified by providing a GuildResolvable ' +
|
||||||
"or from a guild's application command manager.",
|
"or from a guild's application command manager.",
|
||||||
GUILD_UNCACHED_ROLE_RESOLVE: 'Cannot resolve roles from an arbitrary guild, provide an id instead',
|
GUILD_UNCACHED_ROLE_RESOLVE:
|
||||||
|
'Cannot resolve roles from an arbitrary guild, provide an id instead',
|
||||||
|
|
||||||
INTERACTION_ALREADY_REPLIED: 'The reply to this interaction has already been sent or deferred.',
|
INTERACTION_ALREADY_REPLIED:
|
||||||
INTERACTION_NOT_REPLIED: 'The reply to this interaction has not been sent or deferred.',
|
'The reply to this interaction has already been sent or deferred.',
|
||||||
|
INTERACTION_NOT_REPLIED:
|
||||||
|
'The reply to this interaction has not been sent or deferred.',
|
||||||
|
|
||||||
COMMAND_INTERACTION_OPTION_NOT_FOUND: name => `Required option "${name}" not found.`,
|
COMMAND_INTERACTION_OPTION_NOT_FOUND: (name) =>
|
||||||
|
`Required option "${name}" not found.`,
|
||||||
COMMAND_INTERACTION_OPTION_TYPE: (name, type, expected) =>
|
COMMAND_INTERACTION_OPTION_TYPE: (name, type, expected) =>
|
||||||
`Option "${name}" is of type: ${type}; expected ${expected}.`,
|
`Option "${name}" is of type: ${type}; expected ${expected}.`,
|
||||||
COMMAND_INTERACTION_OPTION_EMPTY: (name, type) =>
|
COMMAND_INTERACTION_OPTION_EMPTY: (name, type) =>
|
||||||
`Required option "${name}" is of type: ${type}; expected a non-empty value.`,
|
`Required option "${name}" is of type: ${type}; expected a non-empty value.`,
|
||||||
COMMAND_INTERACTION_OPTION_NO_SUB_COMMAND: 'No subcommand specified for interaction.',
|
COMMAND_INTERACTION_OPTION_NO_SUB_COMMAND:
|
||||||
COMMAND_INTERACTION_OPTION_NO_SUB_COMMAND_GROUP: 'No subcommand group specified for interaction.',
|
'No subcommand specified for interaction.',
|
||||||
AUTOCOMPLETE_INTERACTION_OPTION_NO_FOCUSED_OPTION: 'No focused option for autocomplete interaction.',
|
COMMAND_INTERACTION_OPTION_NO_SUB_COMMAND_GROUP:
|
||||||
|
'No subcommand group specified for interaction.',
|
||||||
|
AUTOCOMPLETE_INTERACTION_OPTION_NO_FOCUSED_OPTION:
|
||||||
|
'No focused option for autocomplete interaction.',
|
||||||
|
|
||||||
MODAL_SUBMIT_INTERACTION_FIELD_NOT_FOUND: customId => `Required field with custom id "${customId}" not found.`,
|
MODAL_SUBMIT_INTERACTION_FIELD_NOT_FOUND: (customId) =>
|
||||||
|
`Required field with custom id "${customId}" not found.`,
|
||||||
MODAL_SUBMIT_INTERACTION_FIELD_TYPE: (customId, type, expected) =>
|
MODAL_SUBMIT_INTERACTION_FIELD_TYPE: (customId, type, expected) =>
|
||||||
`Field with custom id "${customId}" is of type: ${type}; expected ${expected}.`,
|
`Field with custom id "${customId}" is of type: ${type}; expected ${expected}.`,
|
||||||
|
|
||||||
INVITE_MISSING_SCOPES: 'At least one valid scope must be provided for the invite',
|
INVITE_MISSING_SCOPES:
|
||||||
|
'At least one valid scope must be provided for the invite',
|
||||||
|
|
||||||
NOT_IMPLEMENTED: (what, name) => `Method ${what} not implemented on ${name}.`,
|
NOT_IMPLEMENTED: (what, name) => `Method ${what} not implemented on ${name}.`,
|
||||||
|
|
||||||
SWEEP_FILTER_RETURN: 'The return value of the sweepFilter function was not false or a Function',
|
SWEEP_FILTER_RETURN:
|
||||||
|
'The return value of the sweepFilter function was not false or a Function',
|
||||||
|
|
||||||
GUILD_FORUM_MESSAGE_REQUIRED: 'You must provide a message to create a guild forum thread',
|
GUILD_FORUM_MESSAGE_REQUIRED:
|
||||||
|
'You must provide a message to create a guild forum thread',
|
||||||
|
|
||||||
// Selfbot
|
// Selfbot
|
||||||
INVALID_USER_API: 'User accounts cannot use this endpoint',
|
INVALID_USER_API: 'User accounts cannot use this endpoint',
|
||||||
INVALID_APPLICATION_COMMAND: id => `Could not find a valid command for this bot: ${id}`,
|
INVALID_APPLICATION_COMMAND: (id) =>
|
||||||
INVALID_COMMAND_NAME: allCMD => `Could not parse subGroupCommand and subCommand due to too long: ${allCMD.join(' ')}`,
|
`Could not find a valid command for this bot: ${id}`,
|
||||||
|
INVALID_COMMAND_NAME: (allCMD) =>
|
||||||
|
`Could not parse subGroupCommand and subCommand due to too long: ${allCMD.join(' ')}`,
|
||||||
INVALID_SLASH_COMMAND_CHOICES: (parentOptions, value) =>
|
INVALID_SLASH_COMMAND_CHOICES: (parentOptions, value) =>
|
||||||
`${value} is not a valid choice for this option (${parentOptions})`,
|
`${value} is not a valid choice for this option (${parentOptions})`,
|
||||||
SLASH_COMMAND_REQUIRED_OPTIONS_MISSING: (req, opt) => `Value required (${req}) missing (Options: ${opt})`,
|
SLASH_COMMAND_REQUIRED_OPTIONS_MISSING: (req, opt) =>
|
||||||
SLASH_COMMAND_SUB_COMMAND_GROUP_INVALID: n => `${n} is not a valid sub command group`,
|
`Value required (${req}) missing (Options: ${opt})`,
|
||||||
SLASH_COMMAND_SUB_COMMAND_INVALID: n => `${n} is not a valid sub command`,
|
SLASH_COMMAND_SUB_COMMAND_GROUP_INVALID: (n) =>
|
||||||
|
`${n} is not a valid sub command group`,
|
||||||
|
SLASH_COMMAND_SUB_COMMAND_INVALID: (n) => `${n} is not a valid sub command`,
|
||||||
INTERACTION_FAILED: 'No responsed from Application',
|
INTERACTION_FAILED: 'No responsed from Application',
|
||||||
USER_NOT_STREAMING: 'User is not streaming',
|
USER_NOT_STREAMING: 'User is not streaming',
|
||||||
BULK_BAN_USERS_OPTION_EMPTY: 'Option "users" array or collection is empty',
|
BULK_BAN_USERS_OPTION_EMPTY: 'Option "users" array or collection is empty',
|
||||||
|
|
||||||
// Djs v12
|
// Djs v12
|
||||||
VOICE_INVALID_HEARTBEAT: 'Tried to set voice heartbeat but no valid interval was specified.',
|
VOICE_INVALID_HEARTBEAT:
|
||||||
|
'Tried to set voice heartbeat but no valid interval was specified.',
|
||||||
VOICE_USER_MISSING: "Couldn't resolve the user to create stream.",
|
VOICE_USER_MISSING: "Couldn't resolve the user to create stream.",
|
||||||
VOICE_JOIN_CHANNEL: (full = false) =>
|
VOICE_JOIN_CHANNEL: (full = false) =>
|
||||||
`You do not have permission to join this voice channel${full ? '; it is full.' : '.'}`,
|
`You do not have permission to join this voice channel${full ? '; it is full.' : '.'}`,
|
||||||
@@ -191,20 +235,26 @@ const Messages = {
|
|||||||
VOICE_SESSION_ABSENT: 'Session ID not supplied.',
|
VOICE_SESSION_ABSENT: 'Session ID not supplied.',
|
||||||
VOICE_INVALID_ENDPOINT: 'Invalid endpoint received.',
|
VOICE_INVALID_ENDPOINT: 'Invalid endpoint received.',
|
||||||
VOICE_NO_BROWSER: 'Voice connections are not available in browsers.',
|
VOICE_NO_BROWSER: 'Voice connections are not available in browsers.',
|
||||||
VOICE_CONNECTION_ATTEMPTS_EXCEEDED: attempts => `Too many connection attempts (${attempts}).`,
|
VOICE_CONNECTION_ATTEMPTS_EXCEEDED: (attempts) =>
|
||||||
VOICE_JOIN_SOCKET_CLOSED: 'Tried to send join packet, but the WebSocket is not open.',
|
`Too many connection attempts (${attempts}).`,
|
||||||
VOICE_PLAY_INTERFACE_NO_BROADCAST: 'A broadcast cannot be played in this context.',
|
VOICE_JOIN_SOCKET_CLOSED:
|
||||||
|
'Tried to send join packet, but the WebSocket is not open.',
|
||||||
|
VOICE_PLAY_INTERFACE_NO_BROADCAST:
|
||||||
|
'A broadcast cannot be played in this context.',
|
||||||
VOICE_PLAY_INTERFACE_BAD_TYPE: 'Unknown stream type',
|
VOICE_PLAY_INTERFACE_BAD_TYPE: 'Unknown stream type',
|
||||||
VOICE_PRISM_DEMUXERS_NEED_STREAM: 'To play a webm/ogg stream, you need to pass a ReadableStream.',
|
VOICE_PRISM_DEMUXERS_NEED_STREAM:
|
||||||
|
'To play a webm/ogg stream, you need to pass a ReadableStream.',
|
||||||
|
|
||||||
VOICE_STATE_UNCACHED_MEMBER: 'The member of this voice state is uncached.',
|
VOICE_STATE_UNCACHED_MEMBER: 'The member of this voice state is uncached.',
|
||||||
|
|
||||||
UDP_SEND_FAIL: 'Tried to send a UDP packet, but there is no socket available.',
|
UDP_SEND_FAIL:
|
||||||
|
'Tried to send a UDP packet, but there is no socket available.',
|
||||||
UDP_ADDRESS_MALFORMED: 'Malformed UDP address or port.',
|
UDP_ADDRESS_MALFORMED: 'Malformed UDP address or port.',
|
||||||
UDP_CONNECTION_EXISTS: 'There is already an existing UDP connection.',
|
UDP_CONNECTION_EXISTS: 'There is already an existing UDP connection.',
|
||||||
UDP_WRONG_HANDSHAKE: 'Wrong handshake packet for UDP',
|
UDP_WRONG_HANDSHAKE: 'Wrong handshake packet for UDP',
|
||||||
|
|
||||||
INVALID_VIDEO_CODEC: codecs => `Only these codecs are supported: ${codecs.join(', ')}`,
|
INVALID_VIDEO_CODEC: (codecs) =>
|
||||||
|
`Only these codecs are supported: ${codecs.join(', ')}`,
|
||||||
|
|
||||||
STREAM_CONNECTION_READONLY: 'Cannot send data to a read-only stream',
|
STREAM_CONNECTION_READONLY: 'Cannot send data to a read-only stream',
|
||||||
STREAM_CANNOT_JOIN: 'Cannot join a stream to itself',
|
STREAM_CANNOT_JOIN: 'Cannot join a stream to itself',
|
||||||
|
|||||||
@@ -107,7 +107,8 @@ exports.GuildEmoji = require('./structures/GuildEmoji');
|
|||||||
exports.GuildMember = require('./structures/GuildMember').GuildMember;
|
exports.GuildMember = require('./structures/GuildMember').GuildMember;
|
||||||
exports.GuildPreview = require('./structures/GuildPreview');
|
exports.GuildPreview = require('./structures/GuildPreview');
|
||||||
exports.GuildPreviewEmoji = require('./structures/GuildPreviewEmoji');
|
exports.GuildPreviewEmoji = require('./structures/GuildPreviewEmoji');
|
||||||
exports.GuildScheduledEvent = require('./structures/GuildScheduledEvent').GuildScheduledEvent;
|
exports.GuildScheduledEvent =
|
||||||
|
require('./structures/GuildScheduledEvent').GuildScheduledEvent;
|
||||||
exports.GuildTemplate = require('./structures/GuildTemplate');
|
exports.GuildTemplate = require('./structures/GuildTemplate');
|
||||||
exports.Integration = require('./structures/Integration');
|
exports.Integration = require('./structures/Integration');
|
||||||
exports.IntegrationApplication = require('./structures/IntegrationApplication');
|
exports.IntegrationApplication = require('./structures/IntegrationApplication');
|
||||||
@@ -132,7 +133,8 @@ exports.PermissionOverwrites = require('./structures/PermissionOverwrites');
|
|||||||
exports.Presence = require('./structures/Presence').Presence;
|
exports.Presence = require('./structures/Presence').Presence;
|
||||||
exports.ReactionCollector = require('./structures/ReactionCollector');
|
exports.ReactionCollector = require('./structures/ReactionCollector');
|
||||||
exports.ReactionEmoji = require('./structures/ReactionEmoji');
|
exports.ReactionEmoji = require('./structures/ReactionEmoji');
|
||||||
exports.RichPresenceAssets = require('./structures/Presence').RichPresenceAssets;
|
exports.RichPresenceAssets =
|
||||||
|
require('./structures/Presence').RichPresenceAssets;
|
||||||
exports.Role = require('./structures/Role').Role;
|
exports.Role = require('./structures/Role').Role;
|
||||||
exports.Session = require('./structures/Session');
|
exports.Session = require('./structures/Session');
|
||||||
exports.StageChannel = require('./structures/StageChannel');
|
exports.StageChannel = require('./structures/StageChannel');
|
||||||
|
|||||||
@@ -93,7 +93,10 @@ class ApplicationCommandManager extends CachedManager {
|
|||||||
* .then(commands => console.log(`Fetched ${commands.size} commands`))
|
* .then(commands => console.log(`Fetched ${commands.size} commands`))
|
||||||
* .catch(console.error);
|
* .catch(console.error);
|
||||||
*/
|
*/
|
||||||
async fetch(id, { guildId, cache = true, force = false, locale, withLocalizations } = {}) {
|
async fetch(
|
||||||
|
id,
|
||||||
|
{ guildId, cache = true, force = false, locale, withLocalizations } = {},
|
||||||
|
) {
|
||||||
if (typeof id === 'object') {
|
if (typeof id === 'object') {
|
||||||
({ guildId, cache = true, locale, withLocalizations } = id);
|
({ guildId, cache = true, locale, withLocalizations } = id);
|
||||||
} else if (id) {
|
} else if (id) {
|
||||||
@@ -109,9 +112,16 @@ class ApplicationCommandManager extends CachedManager {
|
|||||||
headers: {
|
headers: {
|
||||||
'X-Discord-Locale': locale,
|
'X-Discord-Locale': locale,
|
||||||
},
|
},
|
||||||
query: typeof withLocalizations === 'boolean' ? { with_localizations: withLocalizations } : undefined,
|
query:
|
||||||
|
typeof withLocalizations === 'boolean'
|
||||||
|
? { with_localizations: withLocalizations }
|
||||||
|
: undefined,
|
||||||
});
|
});
|
||||||
return data.reduce((coll, command) => coll.set(command.id, this._add(command, cache, guildId)), new Collection());
|
return data.reduce(
|
||||||
|
(coll, command) =>
|
||||||
|
coll.set(command.id, this._add(command, cache, guildId)),
|
||||||
|
new Collection(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -160,9 +170,13 @@ class ApplicationCommandManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async set(commands, guildId) {
|
async set(commands, guildId) {
|
||||||
const data = await this.commandPath({ guildId }).put({
|
const data = await this.commandPath({ guildId }).put({
|
||||||
data: commands.map(c => this.constructor.transformCommand(c)),
|
data: commands.map((c) => this.constructor.transformCommand(c)),
|
||||||
});
|
});
|
||||||
return data.reduce((coll, command) => coll.set(command.id, this._add(command, true, guildId)), new Collection());
|
return data.reduce(
|
||||||
|
(coll, command) =>
|
||||||
|
coll.set(command.id, this._add(command, true, guildId)),
|
||||||
|
new Collection(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -182,7 +196,12 @@ class ApplicationCommandManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async edit(command, data, guildId) {
|
async edit(command, data, guildId) {
|
||||||
const id = this.resolveId(command);
|
const id = this.resolveId(command);
|
||||||
if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
|
if (!id)
|
||||||
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'command',
|
||||||
|
'ApplicationCommandResolvable',
|
||||||
|
);
|
||||||
|
|
||||||
const patched = await this.commandPath({ id, guildId }).patch({
|
const patched = await this.commandPath({ id, guildId }).patch({
|
||||||
data: this.constructor.transformCommand(data),
|
data: this.constructor.transformCommand(data),
|
||||||
@@ -204,7 +223,12 @@ class ApplicationCommandManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async delete(command, guildId) {
|
async delete(command, guildId) {
|
||||||
const id = this.resolveId(command);
|
const id = this.resolveId(command);
|
||||||
if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
|
if (!id)
|
||||||
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'command',
|
||||||
|
'ApplicationCommandResolvable',
|
||||||
|
);
|
||||||
|
|
||||||
await this.commandPath({ id, guildId }).delete();
|
await this.commandPath({ id, guildId }).delete();
|
||||||
|
|
||||||
@@ -226,25 +250,37 @@ class ApplicationCommandManager extends CachedManager {
|
|||||||
|
|
||||||
if ('default_member_permissions' in command) {
|
if ('default_member_permissions' in command) {
|
||||||
default_member_permissions = command.default_member_permissions
|
default_member_permissions = command.default_member_permissions
|
||||||
? new Permissions(BigInt(command.default_member_permissions)).bitfield.toString()
|
? new Permissions(
|
||||||
|
BigInt(command.default_member_permissions),
|
||||||
|
).bitfield.toString()
|
||||||
: command.default_member_permissions;
|
: command.default_member_permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('defaultMemberPermissions' in command) {
|
if ('defaultMemberPermissions' in command) {
|
||||||
default_member_permissions =
|
default_member_permissions =
|
||||||
command.defaultMemberPermissions !== null
|
command.defaultMemberPermissions !== null
|
||||||
? new Permissions(command.defaultMemberPermissions).bitfield.toString()
|
? new Permissions(
|
||||||
|
command.defaultMemberPermissions,
|
||||||
|
).bitfield.toString()
|
||||||
: command.defaultMemberPermissions;
|
: command.defaultMemberPermissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: command.name,
|
name: command.name,
|
||||||
name_localizations: command.nameLocalizations ?? command.name_localizations,
|
name_localizations:
|
||||||
|
command.nameLocalizations ?? command.name_localizations,
|
||||||
description: command.description,
|
description: command.description,
|
||||||
description_localizations: command.descriptionLocalizations ?? command.description_localizations,
|
description_localizations:
|
||||||
type: typeof command.type === 'number' ? command.type : ApplicationCommandTypes[command.type],
|
command.descriptionLocalizations ?? command.description_localizations,
|
||||||
options: command.options?.map(o => ApplicationCommand.transformOption(o)),
|
type:
|
||||||
default_permission: command.defaultPermission ?? command.default_permission,
|
typeof command.type === 'number'
|
||||||
|
? command.type
|
||||||
|
: ApplicationCommandTypes[command.type],
|
||||||
|
options: command.options?.map((o) =>
|
||||||
|
ApplicationCommand.transformOption(o),
|
||||||
|
),
|
||||||
|
default_permission:
|
||||||
|
command.defaultPermission ?? command.default_permission,
|
||||||
default_member_permissions,
|
default_member_permissions,
|
||||||
dm_permission: command.dmPermission ?? command.dm_permission,
|
dm_permission: command.dmPermission ?? command.dm_permission,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
const { Collection } = require('@discordjs/collection');
|
const { Collection } = require('@discordjs/collection');
|
||||||
const BaseManager = require('./BaseManager');
|
const BaseManager = require('./BaseManager');
|
||||||
const { Error, TypeError } = require('../errors');
|
const { Error, TypeError } = require('../errors');
|
||||||
const { ApplicationCommandPermissionTypes, APIErrors } = require('../util/Constants');
|
const {
|
||||||
|
ApplicationCommandPermissionTypes,
|
||||||
|
APIErrors,
|
||||||
|
} = require('../util/Constants');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages API methods for permissions of Application Commands.
|
* Manages API methods for permissions of Application Commands.
|
||||||
@@ -47,7 +50,10 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
permissionsPath(guildId, commandId) {
|
permissionsPath(guildId, commandId) {
|
||||||
return this.client.api.applications(this.client.application.id).guilds(guildId).commands(commandId).permissions;
|
return this.client.api
|
||||||
|
.applications(this.client.application.id)
|
||||||
|
.guilds(guildId)
|
||||||
|
.commands(commandId).permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,7 +102,9 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
|||||||
const { guildId, commandId } = this._validateOptions(guild, command);
|
const { guildId, commandId } = this._validateOptions(guild, command);
|
||||||
if (commandId) {
|
if (commandId) {
|
||||||
const data = await this.permissionsPath(guildId, commandId).get();
|
const data = await this.permissionsPath(guildId, commandId).get();
|
||||||
return data.permissions.map(perm => this.constructor.transformPermissions(perm, true));
|
return data.permissions.map((perm) =>
|
||||||
|
this.constructor.transformPermissions(perm, true),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await this.permissionsPath(guildId).get();
|
const data = await this.permissionsPath(guildId).get();
|
||||||
@@ -104,7 +112,9 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
|||||||
(coll, perm) =>
|
(coll, perm) =>
|
||||||
coll.set(
|
coll.set(
|
||||||
perm.id,
|
perm.id,
|
||||||
perm.permissions.map(p => this.constructor.transformPermissions(p, true)),
|
perm.permissions.map((p) =>
|
||||||
|
this.constructor.transformPermissions(p, true),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
new Collection(),
|
new Collection(),
|
||||||
);
|
);
|
||||||
@@ -163,24 +173,48 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
|||||||
|
|
||||||
if (commandId) {
|
if (commandId) {
|
||||||
if (!Array.isArray(permissions)) {
|
if (!Array.isArray(permissions)) {
|
||||||
throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissionData', true);
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'permissions',
|
||||||
|
'Array of ApplicationCommandPermissionData',
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const data = await this.permissionsPath(guildId, commandId).put({
|
const data = await this.permissionsPath(guildId, commandId).put({
|
||||||
data: { permissions: permissions.map(perm => this.constructor.transformPermissions(perm)) },
|
data: {
|
||||||
|
permissions: permissions.map((perm) =>
|
||||||
|
this.constructor.transformPermissions(perm),
|
||||||
|
),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
return data.permissions.map(perm => this.constructor.transformPermissions(perm, true));
|
return data.permissions.map((perm) =>
|
||||||
|
this.constructor.transformPermissions(perm, true),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Array.isArray(fullPermissions)) {
|
if (!Array.isArray(fullPermissions)) {
|
||||||
throw new TypeError('INVALID_TYPE', 'fullPermissions', 'Array of GuildApplicationCommandPermissionData', true);
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'fullPermissions',
|
||||||
|
'Array of GuildApplicationCommandPermissionData',
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const APIPermissions = [];
|
const APIPermissions = [];
|
||||||
for (const perm of fullPermissions) {
|
for (const perm of fullPermissions) {
|
||||||
if (!Array.isArray(perm.permissions)) throw new TypeError('INVALID_ELEMENT', 'Array', 'fullPermissions', perm);
|
if (!Array.isArray(perm.permissions))
|
||||||
|
throw new TypeError(
|
||||||
|
'INVALID_ELEMENT',
|
||||||
|
'Array',
|
||||||
|
'fullPermissions',
|
||||||
|
perm,
|
||||||
|
);
|
||||||
APIPermissions.push({
|
APIPermissions.push({
|
||||||
id: perm.id,
|
id: perm.id,
|
||||||
permissions: perm.permissions.map(p => this.constructor.transformPermissions(p)),
|
permissions: perm.permissions.map((p) =>
|
||||||
|
this.constructor.transformPermissions(p),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const data = await this.permissionsPath(guildId).put({
|
const data = await this.permissionsPath(guildId).put({
|
||||||
@@ -190,7 +224,9 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
|||||||
(coll, perm) =>
|
(coll, perm) =>
|
||||||
coll.set(
|
coll.set(
|
||||||
perm.id,
|
perm.id,
|
||||||
perm.permissions.map(p => this.constructor.transformPermissions(p, true)),
|
perm.permissions.map((p) =>
|
||||||
|
this.constructor.transformPermissions(p, true),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
new Collection(),
|
new Collection(),
|
||||||
);
|
);
|
||||||
@@ -221,26 +257,41 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
|||||||
*/
|
*/
|
||||||
async add({ guild, command, permissions }) {
|
async add({ guild, command, permissions }) {
|
||||||
const { guildId, commandId } = this._validateOptions(guild, command);
|
const { guildId, commandId } = this._validateOptions(guild, command);
|
||||||
if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
|
if (!commandId)
|
||||||
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'command',
|
||||||
|
'ApplicationCommandResolvable',
|
||||||
|
);
|
||||||
if (!Array.isArray(permissions)) {
|
if (!Array.isArray(permissions)) {
|
||||||
throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissionData', true);
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'permissions',
|
||||||
|
'Array of ApplicationCommandPermissionData',
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let existing = [];
|
let existing = [];
|
||||||
try {
|
try {
|
||||||
existing = await this.fetch({ guild: guildId, command: commandId });
|
existing = await this.fetch({ guild: guildId, command: commandId });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error;
|
if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS)
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newPermissions = permissions.slice();
|
const newPermissions = permissions.slice();
|
||||||
for (const perm of existing) {
|
for (const perm of existing) {
|
||||||
if (!newPermissions.some(x => x.id === perm.id)) {
|
if (!newPermissions.some((x) => x.id === perm.id)) {
|
||||||
newPermissions.push(perm);
|
newPermissions.push(perm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.set({ guild: guildId, command: commandId, permissions: newPermissions });
|
return this.set({
|
||||||
|
guild: guildId,
|
||||||
|
command: commandId,
|
||||||
|
permissions: newPermissions,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -272,15 +323,27 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
|||||||
*/
|
*/
|
||||||
async remove({ guild, command, users, roles }) {
|
async remove({ guild, command, users, roles }) {
|
||||||
const { guildId, commandId } = this._validateOptions(guild, command);
|
const { guildId, commandId } = this._validateOptions(guild, command);
|
||||||
if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
|
if (!commandId)
|
||||||
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'command',
|
||||||
|
'ApplicationCommandResolvable',
|
||||||
|
);
|
||||||
|
|
||||||
if (!users && !roles) throw new TypeError('INVALID_TYPE', 'users OR roles', 'Array or Resolvable', true);
|
if (!users && !roles)
|
||||||
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'users OR roles',
|
||||||
|
'Array or Resolvable',
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
let resolvedIds = [];
|
let resolvedIds = [];
|
||||||
if (Array.isArray(users)) {
|
if (Array.isArray(users)) {
|
||||||
users.forEach(user => {
|
users.forEach((user) => {
|
||||||
const userId = this.client.users.resolveId(user);
|
const userId = this.client.users.resolveId(user);
|
||||||
if (!userId) throw new TypeError('INVALID_ELEMENT', 'Array', 'users', user);
|
if (!userId)
|
||||||
|
throw new TypeError('INVALID_ELEMENT', 'Array', 'users', user);
|
||||||
resolvedIds.push(userId);
|
resolvedIds.push(userId);
|
||||||
});
|
});
|
||||||
} else if (users) {
|
} else if (users) {
|
||||||
@@ -292,14 +355,15 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(roles)) {
|
if (Array.isArray(roles)) {
|
||||||
roles.forEach(role => {
|
roles.forEach((role) => {
|
||||||
if (typeof role === 'string') {
|
if (typeof role === 'string') {
|
||||||
resolvedIds.push(role);
|
resolvedIds.push(role);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE');
|
if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE');
|
||||||
const roleId = this.guild.roles.resolveId(role);
|
const roleId = this.guild.roles.resolveId(role);
|
||||||
if (!roleId) throw new TypeError('INVALID_ELEMENT', 'Array', 'users', role);
|
if (!roleId)
|
||||||
|
throw new TypeError('INVALID_ELEMENT', 'Array', 'users', role);
|
||||||
resolvedIds.push(roleId);
|
resolvedIds.push(roleId);
|
||||||
});
|
});
|
||||||
} else if (roles) {
|
} else if (roles) {
|
||||||
@@ -309,7 +373,11 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
|||||||
if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE');
|
if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE');
|
||||||
const roleId = this.guild.roles.resolveId(roles);
|
const roleId = this.guild.roles.resolveId(roles);
|
||||||
if (!roleId) {
|
if (!roleId) {
|
||||||
throw new TypeError('INVALID_TYPE', 'users', 'Array or RoleResolvable');
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'users',
|
||||||
|
'Array or RoleResolvable',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
resolvedIds.push(roleId);
|
resolvedIds.push(roleId);
|
||||||
}
|
}
|
||||||
@@ -319,10 +387,13 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
|||||||
try {
|
try {
|
||||||
existing = await this.fetch({ guild: guildId, command: commandId });
|
existing = await this.fetch({ guild: guildId, command: commandId });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error;
|
if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS)
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
const permissions = existing.filter(perm => !resolvedIds.includes(perm.id));
|
const permissions = existing.filter(
|
||||||
|
(perm) => !resolvedIds.includes(perm.id),
|
||||||
|
);
|
||||||
|
|
||||||
return this.set({ guild: guildId, command: commandId, permissions });
|
return this.set({ guild: guildId, command: commandId, permissions });
|
||||||
}
|
}
|
||||||
@@ -347,9 +418,19 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
|||||||
*/
|
*/
|
||||||
async has({ guild, command, permissionId }) {
|
async has({ guild, command, permissionId }) {
|
||||||
const { guildId, commandId } = this._validateOptions(guild, command);
|
const { guildId, commandId } = this._validateOptions(guild, command);
|
||||||
if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
|
if (!commandId)
|
||||||
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'command',
|
||||||
|
'ApplicationCommandResolvable',
|
||||||
|
);
|
||||||
|
|
||||||
if (!permissionId) throw new TypeError('INVALID_TYPE', 'permissionId', 'UserResolvable or RoleResolvable');
|
if (!permissionId)
|
||||||
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'permissionId',
|
||||||
|
'UserResolvable or RoleResolvable',
|
||||||
|
);
|
||||||
let resolvedId = permissionId;
|
let resolvedId = permissionId;
|
||||||
if (typeof permissionId !== 'string') {
|
if (typeof permissionId !== 'string') {
|
||||||
resolvedId = this.client.users.resolveId(permissionId);
|
resolvedId = this.client.users.resolveId(permissionId);
|
||||||
@@ -358,7 +439,11 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
|||||||
resolvedId = this.guild.roles.resolveId(permissionId);
|
resolvedId = this.guild.roles.resolveId(permissionId);
|
||||||
}
|
}
|
||||||
if (!resolvedId) {
|
if (!resolvedId) {
|
||||||
throw new TypeError('INVALID_TYPE', 'permissionId', 'UserResolvable or RoleResolvable');
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'permissionId',
|
||||||
|
'UserResolvable or RoleResolvable',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,10 +451,11 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
|||||||
try {
|
try {
|
||||||
existing = await this.fetch({ guild: guildId, command: commandId });
|
existing = await this.fetch({ guild: guildId, command: commandId });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error;
|
if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS)
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return existing.some(perm => perm.id === resolvedId);
|
return existing.some((perm) => perm.id === resolvedId);
|
||||||
}
|
}
|
||||||
|
|
||||||
_validateOptions(guild, command) {
|
_validateOptions(guild, command) {
|
||||||
@@ -383,7 +469,12 @@ class ApplicationCommandPermissionsManager extends BaseManager {
|
|||||||
}
|
}
|
||||||
commandId ??= this.client.application?.commands.resolveId(command);
|
commandId ??= this.client.application?.commands.resolveId(command);
|
||||||
if (!commandId) {
|
if (!commandId) {
|
||||||
throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable', true);
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'command',
|
||||||
|
'ApplicationCommandResolvable',
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { guildId, commandId };
|
return { guildId, commandId };
|
||||||
|
|||||||
@@ -116,32 +116,52 @@ class AutoModerationRuleManager extends CachedManager {
|
|||||||
exemptChannels,
|
exemptChannels,
|
||||||
reason,
|
reason,
|
||||||
}) {
|
}) {
|
||||||
const data = await this.client.api.guilds(this.guild.id)['auto-moderation'].rules.post({
|
const data = await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
['auto-moderation'].rules.post({
|
||||||
data: {
|
data: {
|
||||||
name,
|
name,
|
||||||
event_type: typeof eventType === 'number' ? eventType : AutoModerationRuleEventTypes[eventType],
|
event_type:
|
||||||
trigger_type: typeof triggerType === 'number' ? triggerType : AutoModerationRuleTriggerTypes[triggerType],
|
typeof eventType === 'number'
|
||||||
|
? eventType
|
||||||
|
: AutoModerationRuleEventTypes[eventType],
|
||||||
|
trigger_type:
|
||||||
|
typeof triggerType === 'number'
|
||||||
|
? triggerType
|
||||||
|
: AutoModerationRuleTriggerTypes[triggerType],
|
||||||
trigger_metadata: triggerMetadata && {
|
trigger_metadata: triggerMetadata && {
|
||||||
keyword_filter: triggerMetadata.keywordFilter,
|
keyword_filter: triggerMetadata.keywordFilter,
|
||||||
regex_patterns: triggerMetadata.regexPatterns,
|
regex_patterns: triggerMetadata.regexPatterns,
|
||||||
presets: triggerMetadata.presets?.map(preset =>
|
presets: triggerMetadata.presets?.map((preset) =>
|
||||||
typeof preset === 'number' ? preset : AutoModerationRuleKeywordPresetTypes[preset],
|
typeof preset === 'number'
|
||||||
|
? preset
|
||||||
|
: AutoModerationRuleKeywordPresetTypes[preset],
|
||||||
),
|
),
|
||||||
allow_list: triggerMetadata.allowList,
|
allow_list: triggerMetadata.allowList,
|
||||||
mention_total_limit: triggerMetadata.mentionTotalLimit,
|
mention_total_limit: triggerMetadata.mentionTotalLimit,
|
||||||
mention_raid_protection_enabled: triggerMetadata.mentionRaidProtectionEnabled,
|
mention_raid_protection_enabled:
|
||||||
|
triggerMetadata.mentionRaidProtectionEnabled,
|
||||||
},
|
},
|
||||||
actions: actions.map(action => ({
|
actions: actions.map((action) => ({
|
||||||
type: typeof action.type === 'number' ? action.type : AutoModerationActionTypes[action.type],
|
type:
|
||||||
|
typeof action.type === 'number'
|
||||||
|
? action.type
|
||||||
|
: AutoModerationActionTypes[action.type],
|
||||||
metadata: {
|
metadata: {
|
||||||
duration_seconds: action.metadata?.durationSeconds,
|
duration_seconds: action.metadata?.durationSeconds,
|
||||||
channel_id: action.metadata?.channel && this.guild.channels.resolveId(action.metadata.channel),
|
channel_id:
|
||||||
|
action.metadata?.channel &&
|
||||||
|
this.guild.channels.resolveId(action.metadata.channel),
|
||||||
custom_message: action.metadata?.customMessage,
|
custom_message: action.metadata?.customMessage,
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
enabled,
|
enabled,
|
||||||
exempt_roles: exemptRoles?.map(exemptRole => this.guild.roles.resolveId(exemptRole)),
|
exempt_roles: exemptRoles?.map((exemptRole) =>
|
||||||
exempt_channels: exemptChannels?.map(exemptChannel => this.guild.channels.resolveId(exemptChannel)),
|
this.guild.roles.resolveId(exemptRole),
|
||||||
|
),
|
||||||
|
exempt_channels: exemptChannels?.map((exemptChannel) =>
|
||||||
|
this.guild.channels.resolveId(exemptChannel),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
reason,
|
reason,
|
||||||
});
|
});
|
||||||
@@ -173,7 +193,16 @@ class AutoModerationRuleManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async edit(
|
async edit(
|
||||||
autoModerationRule,
|
autoModerationRule,
|
||||||
{ name, eventType, triggerMetadata, actions, enabled, exemptRoles, exemptChannels, reason },
|
{
|
||||||
|
name,
|
||||||
|
eventType,
|
||||||
|
triggerMetadata,
|
||||||
|
actions,
|
||||||
|
enabled,
|
||||||
|
exemptRoles,
|
||||||
|
exemptChannels,
|
||||||
|
reason,
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
const autoModerationRuleId = this.resolveId(autoModerationRule);
|
const autoModerationRuleId = this.resolveId(autoModerationRule);
|
||||||
|
|
||||||
@@ -183,28 +212,43 @@ class AutoModerationRuleManager extends CachedManager {
|
|||||||
.patch({
|
.patch({
|
||||||
data: {
|
data: {
|
||||||
name,
|
name,
|
||||||
event_type: typeof eventType === 'number' ? eventType : AutoModerationRuleEventTypes[eventType],
|
event_type:
|
||||||
|
typeof eventType === 'number'
|
||||||
|
? eventType
|
||||||
|
: AutoModerationRuleEventTypes[eventType],
|
||||||
trigger_metadata: triggerMetadata && {
|
trigger_metadata: triggerMetadata && {
|
||||||
keyword_filter: triggerMetadata.keywordFilter,
|
keyword_filter: triggerMetadata.keywordFilter,
|
||||||
regex_patterns: triggerMetadata.regexPatterns,
|
regex_patterns: triggerMetadata.regexPatterns,
|
||||||
presets: triggerMetadata.presets?.map(preset =>
|
presets: triggerMetadata.presets?.map((preset) =>
|
||||||
typeof preset === 'number' ? preset : AutoModerationRuleKeywordPresetTypes[preset],
|
typeof preset === 'number'
|
||||||
|
? preset
|
||||||
|
: AutoModerationRuleKeywordPresetTypes[preset],
|
||||||
),
|
),
|
||||||
allow_list: triggerMetadata.allowList,
|
allow_list: triggerMetadata.allowList,
|
||||||
mention_total_limit: triggerMetadata.mentionTotalLimit,
|
mention_total_limit: triggerMetadata.mentionTotalLimit,
|
||||||
mention_raid_protection_enabled: triggerMetadata.mentionRaidProtectionEnabled,
|
mention_raid_protection_enabled:
|
||||||
|
triggerMetadata.mentionRaidProtectionEnabled,
|
||||||
},
|
},
|
||||||
actions: actions?.map(action => ({
|
actions: actions?.map((action) => ({
|
||||||
type: typeof action.type === 'number' ? action.type : AutoModerationActionTypes[action.type],
|
type:
|
||||||
|
typeof action.type === 'number'
|
||||||
|
? action.type
|
||||||
|
: AutoModerationActionTypes[action.type],
|
||||||
metadata: {
|
metadata: {
|
||||||
duration_seconds: action.metadata?.durationSeconds,
|
duration_seconds: action.metadata?.durationSeconds,
|
||||||
channel_id: action.metadata?.channel && this.guild.channels.resolveId(action.metadata.channel),
|
channel_id:
|
||||||
|
action.metadata?.channel &&
|
||||||
|
this.guild.channels.resolveId(action.metadata.channel),
|
||||||
custom_message: action.metadata?.customMessage,
|
custom_message: action.metadata?.customMessage,
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
enabled,
|
enabled,
|
||||||
exempt_roles: exemptRoles?.map(exemptRole => this.guild.roles.resolveId(exemptRole)),
|
exempt_roles: exemptRoles?.map((exemptRole) =>
|
||||||
exempt_channels: exemptChannels?.map(exemptChannel => this.guild.channels.resolveId(exemptChannel)),
|
this.guild.roles.resolveId(exemptRole),
|
||||||
|
),
|
||||||
|
exempt_channels: exemptChannels?.map((exemptChannel) =>
|
||||||
|
this.guild.channels.resolveId(exemptChannel),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
reason,
|
reason,
|
||||||
});
|
});
|
||||||
@@ -255,9 +299,15 @@ class AutoModerationRuleManager extends CachedManager {
|
|||||||
fetch(options) {
|
fetch(options) {
|
||||||
if (!options) return this._fetchMany();
|
if (!options) return this._fetchMany();
|
||||||
const { autoModerationRule, cache, force } = options;
|
const { autoModerationRule, cache, force } = options;
|
||||||
const resolvedAutoModerationRule = this.resolveId(autoModerationRule ?? options);
|
const resolvedAutoModerationRule = this.resolveId(
|
||||||
|
autoModerationRule ?? options,
|
||||||
|
);
|
||||||
if (resolvedAutoModerationRule) {
|
if (resolvedAutoModerationRule) {
|
||||||
return this._fetchSingle({ autoModerationRule: resolvedAutoModerationRule, cache, force });
|
return this._fetchSingle({
|
||||||
|
autoModerationRule: resolvedAutoModerationRule,
|
||||||
|
cache,
|
||||||
|
force,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return this._fetchMany(options);
|
return this._fetchMany(options);
|
||||||
}
|
}
|
||||||
@@ -268,15 +318,24 @@ class AutoModerationRuleManager extends CachedManager {
|
|||||||
if (existing) return existing;
|
if (existing) return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await this.client.api.guilds(this.guild.id)('auto-moderation').rules(autoModerationRule).get();
|
const data = await this.client.api
|
||||||
|
.guilds(this.guild.id)('auto-moderation')
|
||||||
|
.rules(autoModerationRule)
|
||||||
|
.get();
|
||||||
return this._add(data, cache);
|
return this._add(data, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _fetchMany(options = {}) {
|
async _fetchMany(options = {}) {
|
||||||
const data = await this.client.api.guilds(this.guild.id)('auto-moderation').rules.get();
|
const data = await this.client.api
|
||||||
|
.guilds(this.guild.id)('auto-moderation')
|
||||||
|
.rules.get();
|
||||||
|
|
||||||
return data.reduce(
|
return data.reduce(
|
||||||
(col, autoModerationRule) => col.set(autoModerationRule.id, this._add(autoModerationRule, options.cache)),
|
(col, autoModerationRule) =>
|
||||||
|
col.set(
|
||||||
|
autoModerationRule.id,
|
||||||
|
this._add(autoModerationRule, options.cache),
|
||||||
|
),
|
||||||
new Collection(),
|
new Collection(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -289,7 +348,10 @@ class AutoModerationRuleManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async delete(autoModerationRule, reason) {
|
async delete(autoModerationRule, reason) {
|
||||||
const autoModerationRuleId = this.resolveId(autoModerationRule);
|
const autoModerationRuleId = this.resolveId(autoModerationRule);
|
||||||
await this.client.api.guilds(this.guild.id)('auto-moderation').rules(autoModerationRuleId).delete({ reason });
|
await this.client.api
|
||||||
|
.guilds(this.guild.id)('auto-moderation')
|
||||||
|
.rules(autoModerationRuleId)
|
||||||
|
.delete({ reason });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,9 +34,11 @@ class BillingManager extends BaseManager {
|
|||||||
*/
|
*/
|
||||||
async fetchPaymentSources() {
|
async fetchPaymentSources() {
|
||||||
// https://discord.com/api/v9/users/@me/billing/payment-sources
|
// https://discord.com/api/v9/users/@me/billing/payment-sources
|
||||||
const d = await this.client.api.users('@me').billing['payment-sources'].get();
|
const d = await this.client.api
|
||||||
|
.users('@me')
|
||||||
|
.billing['payment-sources'].get();
|
||||||
// ! TODO: Create a PaymentSource class
|
// ! TODO: Create a PaymentSource class
|
||||||
this.paymentSources = new Collection(d.map(s => [s.id, s]));
|
this.paymentSources = new Collection(d.map((s) => [s.id, s]));
|
||||||
return this.paymentSources;
|
return this.paymentSources;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,8 +48,12 @@ class BillingManager extends BaseManager {
|
|||||||
*/
|
*/
|
||||||
async fetchGuildBoosts() {
|
async fetchGuildBoosts() {
|
||||||
// https://discord.com/api/v9/users/@me/guilds/premium/subscription-slots
|
// https://discord.com/api/v9/users/@me/guilds/premium/subscription-slots
|
||||||
const d = await this.client.api.users('@me').guilds.premium['subscription-slots'].get();
|
const d = await this.client.api
|
||||||
this.guildBoosts = new Collection(d.map(s => [s.id, new GuildBoost(this.client, s)]));
|
.users('@me')
|
||||||
|
.guilds.premium['subscription-slots'].get();
|
||||||
|
this.guildBoosts = new Collection(
|
||||||
|
d.map((s) => [s.id, new GuildBoost(this.client, s)]),
|
||||||
|
);
|
||||||
return this.guildBoosts;
|
return this.guildBoosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +64,7 @@ class BillingManager extends BaseManager {
|
|||||||
async fetchCurrentSubscription() {
|
async fetchCurrentSubscription() {
|
||||||
// https://discord.com/api/v9/users/@me/billing/subscriptions
|
// https://discord.com/api/v9/users/@me/billing/subscriptions
|
||||||
const d = await this.client.api.users('@me').billing.subscriptions.get();
|
const d = await this.client.api.users('@me').billing.subscriptions.get();
|
||||||
this.currentSubscription = new Collection(d.map(s => [s.id, s]));
|
this.currentSubscription = new Collection(d.map((s) => [s.id, s]));
|
||||||
return this.currentSubscription;
|
return this.currentSubscription;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ class CachedManager extends DataManager {
|
|||||||
* @readonly
|
* @readonly
|
||||||
* @name CachedManager#_cache
|
* @name CachedManager#_cache
|
||||||
*/
|
*/
|
||||||
Object.defineProperty(this, '_cache', { value: this.client.options.makeCache(this.constructor, this.holds) });
|
Object.defineProperty(this, '_cache', {
|
||||||
|
value: this.client.options.makeCache(this.constructor, this.holds),
|
||||||
|
});
|
||||||
|
|
||||||
let cleanup = this._cache[_cleanupSymbol]?.();
|
let cleanup = this._cache[_cleanupSymbol]?.();
|
||||||
if (cleanup) {
|
if (cleanup) {
|
||||||
@@ -62,7 +64,9 @@ class CachedManager extends DataManager {
|
|||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
const entry = this.holds ? new this.holds(this.client, data, ...extras) : data;
|
const entry = this.holds
|
||||||
|
? new this.holds(this.client, data, ...extras)
|
||||||
|
: data;
|
||||||
if (cache) this.cache.set(id ?? entry.id, entry);
|
if (cache) this.cache.set(id ?? entry.id, entry);
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,11 @@
|
|||||||
const process = require('node:process');
|
const process = require('node:process');
|
||||||
const CachedManager = require('./CachedManager');
|
const CachedManager = require('./CachedManager');
|
||||||
const { Channel } = require('../structures/Channel');
|
const { Channel } = require('../structures/Channel');
|
||||||
const { Events, ThreadChannelTypes, RelationshipTypes } = require('../util/Constants');
|
const {
|
||||||
|
Events,
|
||||||
|
ThreadChannelTypes,
|
||||||
|
RelationshipTypes,
|
||||||
|
} = require('../util/Constants');
|
||||||
|
|
||||||
let cacheWarningEmitted = false;
|
let cacheWarningEmitted = false;
|
||||||
|
|
||||||
@@ -16,8 +20,10 @@ class ChannelManager extends CachedManager {
|
|||||||
super(client, Channel, iterable);
|
super(client, Channel, iterable);
|
||||||
const defaultCaching =
|
const defaultCaching =
|
||||||
this._cache.constructor.name === 'Collection' ||
|
this._cache.constructor.name === 'Collection' ||
|
||||||
((this._cache.maxSize === undefined || this._cache.maxSize === Infinity) &&
|
((this._cache.maxSize === undefined ||
|
||||||
(this._cache.sweepFilter === undefined || this._cache.sweepFilter.isDefault));
|
this._cache.maxSize === Infinity) &&
|
||||||
|
(this._cache.sweepFilter === undefined ||
|
||||||
|
this._cache.sweepFilter.isDefault));
|
||||||
if (!cacheWarningEmitted && !defaultCaching) {
|
if (!cacheWarningEmitted && !defaultCaching) {
|
||||||
cacheWarningEmitted = true;
|
cacheWarningEmitted = true;
|
||||||
process.emitWarning(
|
process.emitWarning(
|
||||||
@@ -44,10 +50,15 @@ class ChannelManager extends CachedManager {
|
|||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const channel = Channel.create(this.client, data, guild, { allowUnknownGuild });
|
const channel = Channel.create(this.client, data, guild, {
|
||||||
|
allowUnknownGuild,
|
||||||
|
});
|
||||||
|
|
||||||
if (!channel) {
|
if (!channel) {
|
||||||
this.client.emit(Events.DEBUG, `Failed to find guild, or unknown type for channel ${data.id} ${data.type}`);
|
this.client.emit(
|
||||||
|
Events.DEBUG,
|
||||||
|
`Failed to find guild, or unknown type for channel ${data.id} ${data.type}`,
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +126,10 @@ class ChannelManager extends CachedManager {
|
|||||||
* .then(channel => console.log(channel.name))
|
* .then(channel => console.log(channel.name))
|
||||||
* .catch(console.error);
|
* .catch(console.error);
|
||||||
*/
|
*/
|
||||||
async fetch(id, { allowUnknownGuild = false, cache = true, force = false } = {}) {
|
async fetch(
|
||||||
|
id,
|
||||||
|
{ allowUnknownGuild = false, cache = true, force = false } = {},
|
||||||
|
) {
|
||||||
if (!force) {
|
if (!force) {
|
||||||
const existing = this.cache.get(id);
|
const existing = this.cache.get(id);
|
||||||
if (existing && !existing.partial) return existing;
|
if (existing && !existing.partial) return existing;
|
||||||
@@ -133,11 +147,19 @@ class ChannelManager extends CachedManager {
|
|||||||
* client.channels.createGroupDM();
|
* client.channels.createGroupDM();
|
||||||
*/
|
*/
|
||||||
async createGroupDM(recipients = []) {
|
async createGroupDM(recipients = []) {
|
||||||
if (!Array.isArray(recipients)) throw new Error(`Expected an array of recipients (got ${typeof recipients})`);
|
if (!Array.isArray(recipients))
|
||||||
|
throw new Error(
|
||||||
|
`Expected an array of recipients (got ${typeof recipients})`,
|
||||||
|
);
|
||||||
recipients = recipients
|
recipients = recipients
|
||||||
.map(r => this.client.users.resolveId(r))
|
.map((r) => this.client.users.resolveId(r))
|
||||||
.filter(r => r && this.client.relationships.cache.get(r) == RelationshipTypes.FRIEND);
|
.filter(
|
||||||
if (recipients.length == 1 || recipients.length > 9) throw new Error('Invalid Users length (max=9)');
|
(r) =>
|
||||||
|
r &&
|
||||||
|
this.client.relationships.cache.get(r) == RelationshipTypes.FRIEND,
|
||||||
|
);
|
||||||
|
if (recipients.length == 1 || recipients.length > 9)
|
||||||
|
throw new Error('Invalid Users length (max=9)');
|
||||||
const data = await this.client.api.users['@me'].channels.post({
|
const data = await this.client.api.users['@me'].channels.post({
|
||||||
data: { recipients },
|
data: { recipients },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ class ClientUserSettingManager extends BaseManager {
|
|||||||
if ('custom_status' in data) {
|
if ('custom_status' in data) {
|
||||||
this.customStatus = data.custom_status;
|
this.customStatus = data.custom_status;
|
||||||
const activities = this.client.presence.activities.filter(
|
const activities = this.client.presence.activities.filter(
|
||||||
a => ![ActivityTypes.CUSTOM, 'CUSTOM'].includes(a.type),
|
(a) => ![ActivityTypes.CUSTOM, 'CUSTOM'].includes(a.type),
|
||||||
);
|
);
|
||||||
if (data.custom_status) {
|
if (data.custom_status) {
|
||||||
const custom = new CustomStatus(this.client);
|
const custom = new CustomStatus(this.client);
|
||||||
@@ -200,7 +200,10 @@ class ClientUserSettingManager extends BaseManager {
|
|||||||
if (emoji) custom.setEmoji(emoji);
|
if (emoji) custom.setEmoji(emoji);
|
||||||
activities.push(custom);
|
activities.push(custom);
|
||||||
}
|
}
|
||||||
this.client.emit('debug', '[SETTING > ClientUser] Sync activities & status');
|
this.client.emit(
|
||||||
|
'debug',
|
||||||
|
'[SETTING > ClientUser] Sync activities & status',
|
||||||
|
);
|
||||||
this.client.user.setPresence({ activities });
|
this.client.user.setPresence({ activities });
|
||||||
}
|
}
|
||||||
if ('friend_source_flags' in data) {
|
if ('friend_source_flags' in data) {
|
||||||
@@ -212,7 +215,10 @@ class ClientUserSettingManager extends BaseManager {
|
|||||||
* @type {Collection<Snowflake, Guild>}
|
* @type {Collection<Snowflake, Guild>}
|
||||||
*/
|
*/
|
||||||
this.disableDMfromGuilds = new Collection(
|
this.disableDMfromGuilds = new Collection(
|
||||||
data.restricted_guilds.map(guildId => [guildId, this.client.guilds.cache.get(guildId)]),
|
data.restricted_guilds.map((guildId) => [
|
||||||
|
guildId,
|
||||||
|
this.client.guilds.cache.get(guildId),
|
||||||
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,7 +299,10 @@ class ClientUserSettingManager extends BaseManager {
|
|||||||
data.emoji_name = options.emoji?.name;
|
data.emoji_name = options.emoji?.name;
|
||||||
data.emoji_id = options.emoji?.id;
|
data.emoji_id = options.emoji?.id;
|
||||||
} else {
|
} else {
|
||||||
data.emoji_name = typeof options.emoji?.name === 'string' ? options.emoji?.name : null;
|
data.emoji_name =
|
||||||
|
typeof options.emoji?.name === 'string'
|
||||||
|
? options.emoji?.name
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.edit({ custom_status: data });
|
return this.edit({ custom_status: data });
|
||||||
@@ -305,7 +314,9 @@ class ClientUserSettingManager extends BaseManager {
|
|||||||
};
|
};
|
||||||
if (typeof options.text === 'string') {
|
if (typeof options.text === 'string') {
|
||||||
if (options.text.length > 128) {
|
if (options.text.length > 128) {
|
||||||
throw new RangeError('[INVALID_VALUE] Custom status text must be less than 128 characters');
|
throw new RangeError(
|
||||||
|
'[INVALID_VALUE] Custom status text must be less than 128 characters',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
data.text = options.text;
|
data.text = options.text;
|
||||||
}
|
}
|
||||||
@@ -315,16 +326,20 @@ class ClientUserSettingManager extends BaseManager {
|
|||||||
data.emoji_name = emoji.name;
|
data.emoji_name = emoji.name;
|
||||||
data.emoji_id = emoji.id;
|
data.emoji_id = emoji.id;
|
||||||
} else {
|
} else {
|
||||||
data.emoji_name = typeof options.emoji === 'string' ? options.emoji : null;
|
data.emoji_name =
|
||||||
|
typeof options.emoji === 'string' ? options.emoji : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof options.expires === 'number') {
|
if (typeof options.expires === 'number') {
|
||||||
if (options.expires < Date.now()) {
|
if (options.expires < Date.now()) {
|
||||||
throw new RangeError(`[INVALID_VALUE] Custom status expiration must be greater than ${Date.now()}`);
|
throw new RangeError(
|
||||||
|
`[INVALID_VALUE] Custom status expiration must be greater than ${Date.now()}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
data.expires_at = new Date(options.expires).toISOString();
|
data.expires_at = new Date(options.expires).toISOString();
|
||||||
}
|
}
|
||||||
if (['online', 'idle', 'dnd', 'invisible'].includes(options.status)) this.edit({ status: options.status });
|
if (['online', 'idle', 'dnd', 'invisible'].includes(options.status))
|
||||||
|
this.edit({ status: options.status });
|
||||||
return this.edit({ custom_status: data });
|
return this.edit({ custom_status: data });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,7 +355,9 @@ class ClientUserSettingManager extends BaseManager {
|
|||||||
}
|
}
|
||||||
return this.edit({
|
return this.edit({
|
||||||
default_guilds_restricted: status,
|
default_guilds_restricted: status,
|
||||||
restricted_guilds: status ? this.client.guilds.cache.map(v => v.id) : [],
|
restricted_guilds: status
|
||||||
|
? this.client.guilds.cache.map((v) => v.id)
|
||||||
|
: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -364,8 +381,11 @@ class ClientUserSettingManager extends BaseManager {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
removeRestrictedGuild(guildId) {
|
removeRestrictedGuild(guildId) {
|
||||||
if (!this.disableDMfromServer.delete(guildId)) throw new Error('Guild is already restricted');
|
if (!this.disableDMfromServer.delete(guildId))
|
||||||
return this.edit({ restricted_guilds: this.disableDMfromServer.map((v, k) => k) });
|
throw new Error('Guild is already restricted');
|
||||||
|
return this.edit({
|
||||||
|
restricted_guilds: this.disableDMfromServer.map((v, k) => k),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ class DataManager extends BaseManager {
|
|||||||
*/
|
*/
|
||||||
resolve(idOrInstance) {
|
resolve(idOrInstance) {
|
||||||
if (idOrInstance instanceof this.holds) return idOrInstance;
|
if (idOrInstance instanceof this.holds) return idOrInstance;
|
||||||
if (typeof idOrInstance === 'string') return this.cache.get(idOrInstance) ?? null;
|
if (typeof idOrInstance === 'string')
|
||||||
|
return this.cache.get(idOrInstance) ?? null;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,9 @@ class GuildBanManager extends CachedManager {
|
|||||||
* @returns {?GuildBan}
|
* @returns {?GuildBan}
|
||||||
*/
|
*/
|
||||||
resolve(ban) {
|
resolve(ban) {
|
||||||
return super.resolve(ban) ?? super.resolve(this.client.users.resolveId(ban));
|
return (
|
||||||
|
super.resolve(ban) ?? super.resolve(this.client.users.resolveId(ban))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,7 +101,8 @@ class GuildBanManager extends CachedManager {
|
|||||||
if (!options) return this._fetchMany();
|
if (!options) return this._fetchMany();
|
||||||
const { user, cache, force, limit, before, after } = options;
|
const { user, cache, force, limit, before, after } = options;
|
||||||
const resolvedUser = this.client.users.resolveId(user ?? options);
|
const resolvedUser = this.client.users.resolveId(user ?? options);
|
||||||
if (resolvedUser) return this._fetchSingle({ user: resolvedUser, cache, force });
|
if (resolvedUser)
|
||||||
|
return this._fetchSingle({ user: resolvedUser, cache, force });
|
||||||
|
|
||||||
if (!before && !after && !limit && typeof cache === 'undefined') {
|
if (!before && !after && !limit && typeof cache === 'undefined') {
|
||||||
throw new Error('FETCH_BAN_RESOLVE_ID');
|
throw new Error('FETCH_BAN_RESOLVE_ID');
|
||||||
@@ -123,7 +126,10 @@ class GuildBanManager extends CachedManager {
|
|||||||
query: options,
|
query: options,
|
||||||
});
|
});
|
||||||
|
|
||||||
return data.reduce((col, ban) => col.set(ban.user.id, this._add(ban, options.cache)), new Collection());
|
return data.reduce(
|
||||||
|
(col, ban) => col.set(ban.user.id, this._add(ban, options.cache)),
|
||||||
|
new Collection(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Options used to ban a user from a guild.
|
* Options used to ban a user from a guild.
|
||||||
@@ -149,7 +155,8 @@ class GuildBanManager extends CachedManager {
|
|||||||
* .catch(console.error);
|
* .catch(console.error);
|
||||||
*/
|
*/
|
||||||
async create(user, options = {}) {
|
async create(user, options = {}) {
|
||||||
if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true);
|
if (typeof options !== 'object')
|
||||||
|
throw new TypeError('INVALID_TYPE', 'options', 'object', true);
|
||||||
const id = this.client.users.resolveId(user);
|
const id = this.client.users.resolveId(user);
|
||||||
if (!id) throw new Error('BAN_RESOLVE_ID', true);
|
if (!id) throw new Error('BAN_RESOLVE_ID', true);
|
||||||
|
|
||||||
@@ -232,18 +239,32 @@ class GuildBanManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async bulkCreate(users, options = {}) {
|
async bulkCreate(users, options = {}) {
|
||||||
if (!users || !(Array.isArray(users) || users instanceof Collection)) {
|
if (!users || !(Array.isArray(users) || users instanceof Collection)) {
|
||||||
throw new TypeError('INVALID_TYPE', 'users', 'Array or Collection of UserResolvable', true);
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'users',
|
||||||
|
'Array or Collection of UserResolvable',
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true);
|
if (typeof options !== 'object')
|
||||||
|
throw new TypeError('INVALID_TYPE', 'options', 'object', true);
|
||||||
|
|
||||||
const userIds = users.map(user => this.client.users.resolveId(user));
|
const userIds = users.map((user) => this.client.users.resolveId(user));
|
||||||
if (userIds.length === 0) throw new Error('BULK_BAN_USERS_OPTION_EMPTY');
|
if (userIds.length === 0) throw new Error('BULK_BAN_USERS_OPTION_EMPTY');
|
||||||
|
|
||||||
const result = await this.client.api.guilds(this.guild.id)['bulk-ban'].post({
|
const result = await this.client.api
|
||||||
data: { delete_message_days: options.deleteMessageSeconds, user_ids: userIds },
|
.guilds(this.guild.id)
|
||||||
|
['bulk-ban'].post({
|
||||||
|
data: {
|
||||||
|
delete_message_days: options.deleteMessageSeconds,
|
||||||
|
user_ids: userIds,
|
||||||
|
},
|
||||||
reason: options.reason,
|
reason: options.reason,
|
||||||
});
|
});
|
||||||
return { bannedUsers: result.banned_users, failedUsers: result.failed_users };
|
return {
|
||||||
|
bannedUsers: result.banned_users,
|
||||||
|
failedUsers: result.failed_users,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,11 @@ const {
|
|||||||
} = require('../util/Constants');
|
} = require('../util/Constants');
|
||||||
const DataResolver = require('../util/DataResolver');
|
const DataResolver = require('../util/DataResolver');
|
||||||
const Util = require('../util/Util');
|
const Util = require('../util/Util');
|
||||||
const { resolveAutoArchiveMaxLimit, transformGuildForumTag, transformGuildDefaultReaction } = require('../util/Util');
|
const {
|
||||||
|
resolveAutoArchiveMaxLimit,
|
||||||
|
transformGuildForumTag,
|
||||||
|
transformGuildDefaultReaction,
|
||||||
|
} = require('../util/Util');
|
||||||
|
|
||||||
let cacheWarningEmitted = false;
|
let cacheWarningEmitted = false;
|
||||||
let storeChannelDeprecationEmitted = false;
|
let storeChannelDeprecationEmitted = false;
|
||||||
@@ -32,8 +36,10 @@ class GuildChannelManager extends CachedManager {
|
|||||||
super(guild.client, GuildChannel, iterable);
|
super(guild.client, GuildChannel, iterable);
|
||||||
const defaultCaching =
|
const defaultCaching =
|
||||||
this._cache.constructor.name === 'Collection' ||
|
this._cache.constructor.name === 'Collection' ||
|
||||||
((this._cache.maxSize === undefined || this._cache.maxSize === Infinity) &&
|
((this._cache.maxSize === undefined ||
|
||||||
(this._cache.sweepFilter === undefined || this._cache.sweepFilter.isDefault));
|
this._cache.maxSize === Infinity) &&
|
||||||
|
(this._cache.sweepFilter === undefined ||
|
||||||
|
this._cache.sweepFilter.isDefault));
|
||||||
if (!cacheWarningEmitted && !defaultCaching) {
|
if (!cacheWarningEmitted && !defaultCaching) {
|
||||||
cacheWarningEmitted = true;
|
cacheWarningEmitted = true;
|
||||||
process.emitWarning(
|
process.emitWarning(
|
||||||
@@ -89,7 +95,8 @@ class GuildChannelManager extends CachedManager {
|
|||||||
* @returns {?(GuildChannel|ThreadChannel)}
|
* @returns {?(GuildChannel|ThreadChannel)}
|
||||||
*/
|
*/
|
||||||
resolve(channel) {
|
resolve(channel) {
|
||||||
if (channel instanceof ThreadChannel) return this.cache.get(channel.id) ?? null;
|
if (channel instanceof ThreadChannel)
|
||||||
|
return this.cache.get(channel.id) ?? null;
|
||||||
return super.resolve(channel);
|
return super.resolve(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,17 +161,33 @@ class GuildChannelManager extends CachedManager {
|
|||||||
} = {},
|
} = {},
|
||||||
) {
|
) {
|
||||||
parent &&= this.client.channels.resolveId(parent);
|
parent &&= this.client.channels.resolveId(parent);
|
||||||
permissionOverwrites &&= permissionOverwrites.map(o => PermissionOverwrites.resolve(o, this.guild));
|
permissionOverwrites &&= permissionOverwrites.map((o) =>
|
||||||
const intType = typeof type === 'number' ? type : ChannelTypes[type] ?? ChannelTypes.GUILD_TEXT;
|
PermissionOverwrites.resolve(o, this.guild),
|
||||||
|
);
|
||||||
|
const intType =
|
||||||
|
typeof type === 'number'
|
||||||
|
? type
|
||||||
|
: (ChannelTypes[type] ?? ChannelTypes.GUILD_TEXT);
|
||||||
|
|
||||||
const videoMode = typeof videoQualityMode === 'number' ? videoQualityMode : VideoQualityModes[videoQualityMode];
|
const videoMode =
|
||||||
|
typeof videoQualityMode === 'number'
|
||||||
|
? videoQualityMode
|
||||||
|
: VideoQualityModes[videoQualityMode];
|
||||||
|
|
||||||
const sortMode = typeof defaultSortOrder === 'number' ? defaultSortOrder : SortOrderTypes[defaultSortOrder];
|
const sortMode =
|
||||||
|
typeof defaultSortOrder === 'number'
|
||||||
|
? defaultSortOrder
|
||||||
|
: SortOrderTypes[defaultSortOrder];
|
||||||
|
|
||||||
const layoutMode =
|
const layoutMode =
|
||||||
typeof defaultForumLayout === 'number' ? defaultForumLayout : ForumLayoutTypes[defaultForumLayout];
|
typeof defaultForumLayout === 'number'
|
||||||
|
? defaultForumLayout
|
||||||
|
: ForumLayoutTypes[defaultForumLayout];
|
||||||
|
|
||||||
if (intType === ChannelTypes.GUILD_STORE && !storeChannelDeprecationEmitted) {
|
if (
|
||||||
|
intType === ChannelTypes.GUILD_STORE &&
|
||||||
|
!storeChannelDeprecationEmitted
|
||||||
|
) {
|
||||||
storeChannelDeprecationEmitted = true;
|
storeChannelDeprecationEmitted = true;
|
||||||
process.emitWarning(
|
process.emitWarning(
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
@@ -187,8 +210,12 @@ class GuildChannelManager extends CachedManager {
|
|||||||
rate_limit_per_user: rateLimitPerUser,
|
rate_limit_per_user: rateLimitPerUser,
|
||||||
rtc_region: rtcRegion,
|
rtc_region: rtcRegion,
|
||||||
video_quality_mode: videoMode,
|
video_quality_mode: videoMode,
|
||||||
available_tags: availableTags?.map(availableTag => transformGuildForumTag(availableTag)),
|
available_tags: availableTags?.map((availableTag) =>
|
||||||
default_reaction_emoji: defaultReactionEmoji && transformGuildDefaultReaction(defaultReactionEmoji),
|
transformGuildForumTag(availableTag),
|
||||||
|
),
|
||||||
|
default_reaction_emoji:
|
||||||
|
defaultReactionEmoji &&
|
||||||
|
transformGuildDefaultReaction(defaultReactionEmoji),
|
||||||
default_sort_order: sortMode,
|
default_sort_order: sortMode,
|
||||||
default_forum_layout: layoutMode,
|
default_forum_layout: layoutMode,
|
||||||
default_thread_rate_limit_per_user: defaultThreadRateLimitPerUser,
|
default_thread_rate_limit_per_user: defaultThreadRateLimitPerUser,
|
||||||
@@ -215,7 +242,8 @@ class GuildChannelManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async createWebhook(channel, name, { avatar, reason } = {}) {
|
async createWebhook(channel, name, { avatar, reason } = {}) {
|
||||||
const id = this.resolveId(channel);
|
const id = this.resolveId(channel);
|
||||||
if (!id) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable');
|
if (!id)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable');
|
||||||
|
|
||||||
const resolvedImage = await DataResolver.resolveImage(avatar);
|
const resolvedImage = await DataResolver.resolveImage(avatar);
|
||||||
|
|
||||||
@@ -239,8 +267,11 @@ class GuildChannelManager extends CachedManager {
|
|||||||
async addFollower(channel, targetChannel, reason) {
|
async addFollower(channel, targetChannel, reason) {
|
||||||
const channelId = this.resolveId(channel);
|
const channelId = this.resolveId(channel);
|
||||||
const targetChannelId = this.resolveId(targetChannel);
|
const targetChannelId = this.resolveId(targetChannel);
|
||||||
if (!channelId || !targetChannelId) throw new Error('GUILD_CHANNEL_RESOLVE');
|
if (!channelId || !targetChannelId)
|
||||||
const { webhook_id } = await this.client.api.channels[channelId].followers.post({
|
throw new Error('GUILD_CHANNEL_RESOLVE');
|
||||||
|
const { webhook_id } = await this.client.api.channels[
|
||||||
|
channelId
|
||||||
|
].followers.post({
|
||||||
data: { webhook_channel_id: targetChannelId },
|
data: { webhook_channel_id: targetChannelId },
|
||||||
reason,
|
reason,
|
||||||
});
|
});
|
||||||
@@ -288,31 +319,36 @@ class GuildChannelManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async edit(channel, data, reason) {
|
async edit(channel, data, reason) {
|
||||||
channel = this.resolve(channel);
|
channel = this.resolve(channel);
|
||||||
if (!channel) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable');
|
if (!channel)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable');
|
||||||
|
|
||||||
const parentId = data.parent && this.client.channels.resolveId(data.parent);
|
const parentId = data.parent && this.client.channels.resolveId(data.parent);
|
||||||
|
|
||||||
if (typeof data.position !== 'undefined') await this.setPosition(channel, data.position, { reason });
|
if (typeof data.position !== 'undefined')
|
||||||
|
await this.setPosition(channel, data.position, { reason });
|
||||||
|
|
||||||
let permission_overwrites = data.permissionOverwrites?.map(o => PermissionOverwrites.resolve(o, this.guild));
|
let permission_overwrites = data.permissionOverwrites?.map((o) =>
|
||||||
|
PermissionOverwrites.resolve(o, this.guild),
|
||||||
|
);
|
||||||
|
|
||||||
if (data.lockPermissions) {
|
if (data.lockPermissions) {
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
const newParent = this.cache.get(parentId);
|
const newParent = this.cache.get(parentId);
|
||||||
if (newParent?.type === 'GUILD_CATEGORY') {
|
if (newParent?.type === 'GUILD_CATEGORY') {
|
||||||
permission_overwrites = newParent.permissionOverwrites.cache.map(o =>
|
permission_overwrites = newParent.permissionOverwrites.cache.map(
|
||||||
PermissionOverwrites.resolve(o, this.guild),
|
(o) => PermissionOverwrites.resolve(o, this.guild),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (channel.parent) {
|
} else if (channel.parent) {
|
||||||
permission_overwrites = channel.parent.permissionOverwrites.cache.map(o =>
|
permission_overwrites = channel.parent.permissionOverwrites.cache.map(
|
||||||
PermissionOverwrites.resolve(o, this.guild),
|
(o) => PermissionOverwrites.resolve(o, this.guild),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let defaultAutoArchiveDuration = data.defaultAutoArchiveDuration;
|
let defaultAutoArchiveDuration = data.defaultAutoArchiveDuration;
|
||||||
if (defaultAutoArchiveDuration === 'MAX') defaultAutoArchiveDuration = resolveAutoArchiveMaxLimit(this.guild);
|
if (defaultAutoArchiveDuration === 'MAX')
|
||||||
|
defaultAutoArchiveDuration = resolveAutoArchiveMaxLimit(this.guild);
|
||||||
|
|
||||||
const newData = await this.client.api.channels(channel.id).patch({
|
const newData = await this.client.api.channels(channel.id).patch({
|
||||||
data: {
|
data: {
|
||||||
@@ -324,18 +360,26 @@ class GuildChannelManager extends CachedManager {
|
|||||||
user_limit: data.userLimit ?? channel.userLimit,
|
user_limit: data.userLimit ?? channel.userLimit,
|
||||||
rtc_region: 'rtcRegion' in data ? data.rtcRegion : channel.rtcRegion,
|
rtc_region: 'rtcRegion' in data ? data.rtcRegion : channel.rtcRegion,
|
||||||
video_quality_mode:
|
video_quality_mode:
|
||||||
typeof data.videoQualityMode === 'string' ? VideoQualityModes[data.videoQualityMode] : data.videoQualityMode,
|
typeof data.videoQualityMode === 'string'
|
||||||
|
? VideoQualityModes[data.videoQualityMode]
|
||||||
|
: data.videoQualityMode,
|
||||||
parent_id: parentId,
|
parent_id: parentId,
|
||||||
lock_permissions: data.lockPermissions,
|
lock_permissions: data.lockPermissions,
|
||||||
rate_limit_per_user: data.rateLimitPerUser,
|
rate_limit_per_user: data.rateLimitPerUser,
|
||||||
default_auto_archive_duration: defaultAutoArchiveDuration,
|
default_auto_archive_duration: defaultAutoArchiveDuration,
|
||||||
permission_overwrites,
|
permission_overwrites,
|
||||||
available_tags: data.availableTags?.map(availableTag => transformGuildForumTag(availableTag)),
|
available_tags: data.availableTags?.map((availableTag) =>
|
||||||
default_reaction_emoji: data.defaultReactionEmoji && transformGuildDefaultReaction(data.defaultReactionEmoji),
|
transformGuildForumTag(availableTag),
|
||||||
|
),
|
||||||
|
default_reaction_emoji:
|
||||||
|
data.defaultReactionEmoji &&
|
||||||
|
transformGuildDefaultReaction(data.defaultReactionEmoji),
|
||||||
default_thread_rate_limit_per_user: data.defaultThreadRateLimitPerUser,
|
default_thread_rate_limit_per_user: data.defaultThreadRateLimitPerUser,
|
||||||
flags: 'flags' in data ? ChannelFlags.resolve(data.flags) : undefined,
|
flags: 'flags' in data ? ChannelFlags.resolve(data.flags) : undefined,
|
||||||
default_sort_order:
|
default_sort_order:
|
||||||
typeof data.defaultSortOrder === 'string' ? SortOrderTypes[data.defaultSortOrder] : data.defaultSortOrder,
|
typeof data.defaultSortOrder === 'string'
|
||||||
|
? SortOrderTypes[data.defaultSortOrder]
|
||||||
|
: data.defaultSortOrder,
|
||||||
},
|
},
|
||||||
reason,
|
reason,
|
||||||
});
|
});
|
||||||
@@ -357,7 +401,8 @@ class GuildChannelManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async setPosition(channel, position, { relative, reason } = {}) {
|
async setPosition(channel, position, { relative, reason } = {}) {
|
||||||
channel = this.resolve(channel);
|
channel = this.resolve(channel);
|
||||||
if (!channel) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable');
|
if (!channel)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable');
|
||||||
const updatedChannels = await Util.setPosition(
|
const updatedChannels = await Util.setPosition(
|
||||||
channel,
|
channel,
|
||||||
position,
|
position,
|
||||||
@@ -399,13 +444,18 @@ class GuildChannelManager extends CachedManager {
|
|||||||
if (id) {
|
if (id) {
|
||||||
const data = await this.client.api.channels(id).get();
|
const data = await this.client.api.channels(id).get();
|
||||||
// Since this is the guild manager, throw if on a different guild
|
// Since this is the guild manager, throw if on a different guild
|
||||||
if (this.guild.id !== data.guild_id) throw new Error('GUILD_CHANNEL_UNOWNED');
|
if (this.guild.id !== data.guild_id)
|
||||||
|
throw new Error('GUILD_CHANNEL_UNOWNED');
|
||||||
return this.client.channels._add(data, this.guild, { cache });
|
return this.client.channels._add(data, this.guild, { cache });
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await this.client.api.guilds(this.guild.id).channels.get();
|
const data = await this.client.api.guilds(this.guild.id).channels.get();
|
||||||
const channels = new Collection();
|
const channels = new Collection();
|
||||||
for (const channel of data) channels.set(channel.id, this.client.channels._add(channel, this.guild, { cache }));
|
for (const channel of data)
|
||||||
|
channels.set(
|
||||||
|
channel.id,
|
||||||
|
this.client.channels._add(channel, this.guild, { cache }),
|
||||||
|
);
|
||||||
return channels;
|
return channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,9 +471,13 @@ class GuildChannelManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async fetchWebhooks(channel) {
|
async fetchWebhooks(channel) {
|
||||||
const id = this.resolveId(channel);
|
const id = this.resolveId(channel);
|
||||||
if (!id) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable');
|
if (!id)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable');
|
||||||
const data = await this.client.api.channels[id].webhooks.get();
|
const data = await this.client.api.channels[id].webhooks.get();
|
||||||
return data.reduce((hooks, hook) => hooks.set(hook.id, new Webhook(this.client, hook)), new Collection());
|
return data.reduce(
|
||||||
|
(hooks, hook) => hooks.set(hook.id, new Webhook(this.client, hook)),
|
||||||
|
new Collection(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -453,14 +507,17 @@ class GuildChannelManager extends CachedManager {
|
|||||||
* .catch(console.error);
|
* .catch(console.error);
|
||||||
*/
|
*/
|
||||||
async setPositions(channelPositions) {
|
async setPositions(channelPositions) {
|
||||||
channelPositions = channelPositions.map(r => ({
|
channelPositions = channelPositions.map((r) => ({
|
||||||
id: this.client.channels.resolveId(r.channel),
|
id: this.client.channels.resolveId(r.channel),
|
||||||
position: r.position,
|
position: r.position,
|
||||||
lock_permissions: r.lockPermissions,
|
lock_permissions: r.lockPermissions,
|
||||||
parent_id: typeof r.parent !== 'undefined' ? this.resolveId(r.parent) : undefined,
|
parent_id:
|
||||||
|
typeof r.parent !== 'undefined' ? this.resolveId(r.parent) : undefined,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await this.client.api.guilds(this.guild.id).channels.patch({ data: channelPositions });
|
await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
.channels.patch({ data: channelPositions });
|
||||||
return this.client.actions.GuildChannelsPositionUpdate.handle({
|
return this.client.actions.GuildChannelsPositionUpdate.handle({
|
||||||
guild_id: this.guild.id,
|
guild_id: this.guild.id,
|
||||||
channels: channelPositions,
|
channels: channelPositions,
|
||||||
@@ -480,7 +537,8 @@ class GuildChannelManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async delete(channel, reason) {
|
async delete(channel, reason) {
|
||||||
const id = this.resolveId(channel);
|
const id = this.resolveId(channel);
|
||||||
if (!id) throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable');
|
if (!id)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'channel', 'GuildChannelResolvable');
|
||||||
await this.client.api.channels(id).delete({ reason });
|
await this.client.api.channels(id).delete({ reason });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,17 +56,30 @@ class GuildEmojiManager extends BaseGuildEmojiManager {
|
|||||||
const data = { image: attachment, name };
|
const data = { image: attachment, name };
|
||||||
if (roles) {
|
if (roles) {
|
||||||
if (!Array.isArray(roles) && !(roles instanceof Collection)) {
|
if (!Array.isArray(roles) && !(roles instanceof Collection)) {
|
||||||
throw new TypeError('INVALID_TYPE', 'options.roles', 'Array or Collection of Roles or Snowflakes', true);
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'options.roles',
|
||||||
|
'Array or Collection of Roles or Snowflakes',
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
data.roles = [];
|
data.roles = [];
|
||||||
for (const role of roles.values()) {
|
for (const role of roles.values()) {
|
||||||
const resolvedRole = this.guild.roles.resolveId(role);
|
const resolvedRole = this.guild.roles.resolveId(role);
|
||||||
if (!resolvedRole) throw new TypeError('INVALID_ELEMENT', 'Array or Collection', 'options.roles', role);
|
if (!resolvedRole)
|
||||||
|
throw new TypeError(
|
||||||
|
'INVALID_ELEMENT',
|
||||||
|
'Array or Collection',
|
||||||
|
'options.roles',
|
||||||
|
role,
|
||||||
|
);
|
||||||
data.roles.push(resolvedRole);
|
data.roles.push(resolvedRole);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const emoji = await this.client.api.guilds(this.guild.id).emojis.post({ data, reason });
|
const emoji = await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
.emojis.post({ data, reason });
|
||||||
return this.client.actions.GuildEmojiCreate.handle(this.guild, emoji).emoji;
|
return this.client.actions.GuildEmojiCreate.handle(this.guild, emoji).emoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +105,10 @@ class GuildEmojiManager extends BaseGuildEmojiManager {
|
|||||||
const existing = this.cache.get(id);
|
const existing = this.cache.get(id);
|
||||||
if (existing) return existing;
|
if (existing) return existing;
|
||||||
}
|
}
|
||||||
const emoji = await this.client.api.guilds(this.guild.id).emojis(id).get();
|
const emoji = await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
.emojis(id)
|
||||||
|
.get();
|
||||||
return this._add(emoji, cache);
|
return this._add(emoji, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +126,8 @@ class GuildEmojiManager extends BaseGuildEmojiManager {
|
|||||||
*/
|
*/
|
||||||
async delete(emoji, reason) {
|
async delete(emoji, reason) {
|
||||||
const id = this.resolveId(emoji);
|
const id = this.resolveId(emoji);
|
||||||
if (!id) throw new TypeError('INVALID_TYPE', 'emoji', 'EmojiResolvable', true);
|
if (!id)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'emoji', 'EmojiResolvable', true);
|
||||||
await this.client.api.guilds(this.guild.id).emojis(id).delete({ reason });
|
await this.client.api.guilds(this.guild.id).emojis(id).delete({ reason });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,8 +140,9 @@ class GuildEmojiManager extends BaseGuildEmojiManager {
|
|||||||
*/
|
*/
|
||||||
async edit(emoji, data, reason) {
|
async edit(emoji, data, reason) {
|
||||||
const id = this.resolveId(emoji);
|
const id = this.resolveId(emoji);
|
||||||
if (!id) throw new TypeError('INVALID_TYPE', 'emoji', 'EmojiResolvable', true);
|
if (!id)
|
||||||
const roles = data.roles?.map(r => this.guild.roles.resolveId(r));
|
throw new TypeError('INVALID_TYPE', 'emoji', 'EmojiResolvable', true);
|
||||||
|
const roles = data.roles?.map((r) => this.guild.roles.resolveId(r));
|
||||||
const newData = await this.client.api
|
const newData = await this.client.api
|
||||||
.guilds(this.guild.id)
|
.guilds(this.guild.id)
|
||||||
.emojis(id)
|
.emojis(id)
|
||||||
@@ -151,7 +169,8 @@ class GuildEmojiManager extends BaseGuildEmojiManager {
|
|||||||
*/
|
*/
|
||||||
async fetchAuthor(emoji) {
|
async fetchAuthor(emoji) {
|
||||||
emoji = this.resolve(emoji);
|
emoji = this.resolve(emoji);
|
||||||
if (!emoji) throw new TypeError('INVALID_TYPE', 'emoji', 'EmojiResolvable', true);
|
if (!emoji)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'emoji', 'EmojiResolvable', true);
|
||||||
if (emoji.managed) {
|
if (emoji.managed) {
|
||||||
throw new Error('EMOJI_MANAGED');
|
throw new Error('EMOJI_MANAGED');
|
||||||
}
|
}
|
||||||
@@ -159,10 +178,16 @@ class GuildEmojiManager extends BaseGuildEmojiManager {
|
|||||||
const { me } = this.guild.members;
|
const { me } = this.guild.members;
|
||||||
if (!me) throw new Error('GUILD_UNCACHED_ME');
|
if (!me) throw new Error('GUILD_UNCACHED_ME');
|
||||||
if (!me.permissions.has(Permissions.FLAGS.MANAGE_EMOJIS_AND_STICKERS)) {
|
if (!me.permissions.has(Permissions.FLAGS.MANAGE_EMOJIS_AND_STICKERS)) {
|
||||||
throw new Error('MISSING_MANAGE_EMOJIS_AND_STICKERS_PERMISSION', this.guild);
|
throw new Error(
|
||||||
|
'MISSING_MANAGE_EMOJIS_AND_STICKERS_PERMISSION',
|
||||||
|
this.guild,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await this.client.api.guilds(this.guild.id).emojis(emoji.id).get();
|
const data = await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
.emojis(emoji.id)
|
||||||
|
.get();
|
||||||
emoji._patch(data);
|
emoji._patch(data);
|
||||||
return emoji.author;
|
return emoji.author;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ class GuildEmojiRoleManager extends DataManager {
|
|||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get cache() {
|
get cache() {
|
||||||
return this.guild.roles.cache.filter(role => this.emoji._roles.includes(role.id));
|
return this.guild.roles.cache.filter((role) =>
|
||||||
|
this.emoji._roles.includes(role.id),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,13 +42,19 @@ class GuildEmojiRoleManager extends DataManager {
|
|||||||
* @returns {Promise<GuildEmoji>}
|
* @returns {Promise<GuildEmoji>}
|
||||||
*/
|
*/
|
||||||
async add(roleOrRoles) {
|
async add(roleOrRoles) {
|
||||||
if (!Array.isArray(roleOrRoles) && !(roleOrRoles instanceof Collection)) roleOrRoles = [roleOrRoles];
|
if (!Array.isArray(roleOrRoles) && !(roleOrRoles instanceof Collection))
|
||||||
|
roleOrRoles = [roleOrRoles];
|
||||||
|
|
||||||
const resolvedRoles = [];
|
const resolvedRoles = [];
|
||||||
for (const role of roleOrRoles.values()) {
|
for (const role of roleOrRoles.values()) {
|
||||||
const resolvedRole = this.guild.roles.resolveId(role);
|
const resolvedRole = this.guild.roles.resolveId(role);
|
||||||
if (!resolvedRole) {
|
if (!resolvedRole) {
|
||||||
throw new TypeError('INVALID_ELEMENT', 'Array or Collection', 'roles', role);
|
throw new TypeError(
|
||||||
|
'INVALID_ELEMENT',
|
||||||
|
'Array or Collection',
|
||||||
|
'roles',
|
||||||
|
role,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
resolvedRoles.push(resolvedRole);
|
resolvedRoles.push(resolvedRole);
|
||||||
}
|
}
|
||||||
@@ -61,18 +69,26 @@ class GuildEmojiRoleManager extends DataManager {
|
|||||||
* @returns {Promise<GuildEmoji>}
|
* @returns {Promise<GuildEmoji>}
|
||||||
*/
|
*/
|
||||||
async remove(roleOrRoles) {
|
async remove(roleOrRoles) {
|
||||||
if (!Array.isArray(roleOrRoles) && !(roleOrRoles instanceof Collection)) roleOrRoles = [roleOrRoles];
|
if (!Array.isArray(roleOrRoles) && !(roleOrRoles instanceof Collection))
|
||||||
|
roleOrRoles = [roleOrRoles];
|
||||||
|
|
||||||
const resolvedRoleIds = [];
|
const resolvedRoleIds = [];
|
||||||
for (const role of roleOrRoles.values()) {
|
for (const role of roleOrRoles.values()) {
|
||||||
const roleId = this.guild.roles.resolveId(role);
|
const roleId = this.guild.roles.resolveId(role);
|
||||||
if (!roleId) {
|
if (!roleId) {
|
||||||
throw new TypeError('INVALID_ELEMENT', 'Array or Collection', 'roles', role);
|
throw new TypeError(
|
||||||
|
'INVALID_ELEMENT',
|
||||||
|
'Array or Collection',
|
||||||
|
'roles',
|
||||||
|
role,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
resolvedRoleIds.push(roleId);
|
resolvedRoleIds.push(roleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newRoles = [...this.cache.keys()].filter(id => !resolvedRoleIds.includes(id));
|
const newRoles = [...this.cache.keys()].filter(
|
||||||
|
(id) => !resolvedRoleIds.includes(id),
|
||||||
|
);
|
||||||
return this.set(newRoles);
|
return this.set(newRoles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,11 @@
|
|||||||
const ThreadManager = require('./ThreadManager');
|
const ThreadManager = require('./ThreadManager');
|
||||||
const { TypeError } = require('../errors');
|
const { TypeError } = require('../errors');
|
||||||
const MessagePayload = require('../structures/MessagePayload');
|
const MessagePayload = require('../structures/MessagePayload');
|
||||||
const { resolveAutoArchiveMaxLimit, getUploadURL, uploadFile } = require('../util/Util');
|
const {
|
||||||
|
resolveAutoArchiveMaxLimit,
|
||||||
|
getUploadURL,
|
||||||
|
uploadFile,
|
||||||
|
} = require('../util/Util');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages API methods for threads in forum channels and stores their cache.
|
* Manages API methods for threads in forum channels and stores their cache.
|
||||||
@@ -72,7 +76,7 @@ class GuildForumThreadManager extends ThreadManager {
|
|||||||
|
|
||||||
// New API
|
// New API
|
||||||
const attachments = await getUploadURL(this.client, this.channel.id, files);
|
const attachments = await getUploadURL(this.client, this.channel.id, files);
|
||||||
const requestPromises = attachments.map(async attachment => {
|
const requestPromises = attachments.map(async (attachment) => {
|
||||||
await uploadFile(files[attachment.id].file, attachment.upload_url);
|
await uploadFile(files[attachment.id].file, attachment.upload_url);
|
||||||
return {
|
return {
|
||||||
id: attachment.id,
|
id: attachment.id,
|
||||||
@@ -86,9 +90,12 @@ class GuildForumThreadManager extends ThreadManager {
|
|||||||
const attachmentsData = await Promise.all(requestPromises);
|
const attachmentsData = await Promise.all(requestPromises);
|
||||||
attachmentsData.sort((a, b) => parseInt(a.id) - parseInt(b.id));
|
attachmentsData.sort((a, b) => parseInt(a.id) - parseInt(b.id));
|
||||||
|
|
||||||
if (autoArchiveDuration === 'MAX') autoArchiveDuration = resolveAutoArchiveMaxLimit(this.channel.guild);
|
if (autoArchiveDuration === 'MAX')
|
||||||
|
autoArchiveDuration = resolveAutoArchiveMaxLimit(this.channel.guild);
|
||||||
|
|
||||||
const post_data = await this.client.api.channels(this.channel.id).threads.post({
|
const post_data = await this.client.api
|
||||||
|
.channels(this.channel.id)
|
||||||
|
.threads.post({
|
||||||
data: {
|
data: {
|
||||||
name,
|
name,
|
||||||
auto_archive_duration: autoArchiveDuration,
|
auto_archive_duration: autoArchiveDuration,
|
||||||
|
|||||||
@@ -156,12 +156,18 @@ class GuildInviteManager extends CachedManager {
|
|||||||
|
|
||||||
async _fetchMany(cache) {
|
async _fetchMany(cache) {
|
||||||
const data = await this.client.api.guilds(this.guild.id).invites.get();
|
const data = await this.client.api.guilds(this.guild.id).invites.get();
|
||||||
return data.reduce((col, invite) => col.set(invite.code, this._add(invite, cache)), new Collection());
|
return data.reduce(
|
||||||
|
(col, invite) => col.set(invite.code, this._add(invite, cache)),
|
||||||
|
new Collection(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _fetchChannelMany(channelId, cache) {
|
async _fetchChannelMany(channelId, cache) {
|
||||||
const data = await this.client.api.channels(channelId).invites.get();
|
const data = await this.client.api.channels(channelId).invites.get();
|
||||||
return data.reduce((col, invite) => col.set(invite.code, this._add(invite, cache)), new Collection());
|
return data.reduce(
|
||||||
|
(col, invite) => col.set(invite.code, this._add(invite, cache)),
|
||||||
|
new Collection(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -177,7 +183,16 @@ class GuildInviteManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async create(
|
async create(
|
||||||
channel,
|
channel,
|
||||||
{ temporary = false, maxAge = 86400, maxUses = 0, unique, targetUser, targetApplication, targetType, reason } = {},
|
{
|
||||||
|
temporary = false,
|
||||||
|
maxAge = 86400,
|
||||||
|
maxUses = 0,
|
||||||
|
unique,
|
||||||
|
targetUser,
|
||||||
|
targetApplication,
|
||||||
|
targetType,
|
||||||
|
reason,
|
||||||
|
} = {},
|
||||||
) {
|
) {
|
||||||
const id = this.guild.channels.resolveId(channel);
|
const id = this.guild.channels.resolveId(channel);
|
||||||
if (!id) throw new Error('GUILD_CHANNEL_RESOLVE');
|
if (!id) throw new Error('GUILD_CHANNEL_RESOLVE');
|
||||||
@@ -189,7 +204,10 @@ class GuildInviteManager extends CachedManager {
|
|||||||
max_uses: maxUses,
|
max_uses: maxUses,
|
||||||
unique,
|
unique,
|
||||||
target_user_id: this.client.users.resolveId(targetUser),
|
target_user_id: this.client.users.resolveId(targetUser),
|
||||||
target_application_id: targetApplication?.id ?? targetApplication?.applicationId ?? targetApplication,
|
target_application_id:
|
||||||
|
targetApplication?.id ??
|
||||||
|
targetApplication?.applicationId ??
|
||||||
|
targetApplication,
|
||||||
target_type: targetType,
|
target_type: targetType,
|
||||||
},
|
},
|
||||||
reason,
|
reason,
|
||||||
|
|||||||
@@ -187,13 +187,18 @@ class GuildManager extends CachedManager {
|
|||||||
verificationLevel = VerificationLevels[verificationLevel];
|
verificationLevel = VerificationLevels[verificationLevel];
|
||||||
}
|
}
|
||||||
if (typeof defaultMessageNotifications === 'string') {
|
if (typeof defaultMessageNotifications === 'string') {
|
||||||
defaultMessageNotifications = DefaultMessageNotificationLevels[defaultMessageNotifications];
|
defaultMessageNotifications =
|
||||||
|
DefaultMessageNotificationLevels[defaultMessageNotifications];
|
||||||
}
|
}
|
||||||
if (typeof explicitContentFilter === 'string') {
|
if (typeof explicitContentFilter === 'string') {
|
||||||
explicitContentFilter = ExplicitContentFilterLevels[explicitContentFilter];
|
explicitContentFilter =
|
||||||
|
ExplicitContentFilterLevels[explicitContentFilter];
|
||||||
}
|
}
|
||||||
for (const channel of channels) {
|
for (const channel of channels) {
|
||||||
channel.type &&= typeof channel.type === 'number' ? channel.type : ChannelTypes[channel.type];
|
channel.type &&=
|
||||||
|
typeof channel.type === 'number'
|
||||||
|
? channel.type
|
||||||
|
: ChannelTypes[channel.type];
|
||||||
channel.parent_id = channel.parentId;
|
channel.parent_id = channel.parentId;
|
||||||
delete channel.parentId;
|
delete channel.parentId;
|
||||||
channel.user_limit = channel.userLimit;
|
channel.user_limit = channel.userLimit;
|
||||||
@@ -242,10 +247,11 @@ class GuildManager extends CachedManager {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.client.guilds.cache.has(data.id)) return this.client.guilds.cache.get(data.id);
|
if (this.client.guilds.cache.has(data.id))
|
||||||
|
return this.client.guilds.cache.get(data.id);
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
const handleGuild = guild => {
|
const handleGuild = (guild) => {
|
||||||
if (guild.id === data.id) {
|
if (guild.id === data.id) {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
this.client.removeListener(Events.GUILD_CREATE, handleGuild);
|
this.client.removeListener(Events.GUILD_CREATE, handleGuild);
|
||||||
@@ -293,12 +299,19 @@ class GuildManager extends CachedManager {
|
|||||||
if (existing) return existing;
|
if (existing) return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await this.client.api.guilds(id).get({ query: { with_counts: options.withCounts ?? true } });
|
const data = await this.client.api
|
||||||
|
.guilds(id)
|
||||||
|
.get({ query: { with_counts: options.withCounts ?? true } });
|
||||||
return this._add(data, options.cache);
|
return this._add(data, options.cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await this.client.api.users('@me').guilds.get({ query: options });
|
const data = await this.client.api
|
||||||
return data.reduce((coll, guild) => coll.set(guild.id, new OAuth2Guild(this.client, guild)), new Collection());
|
.users('@me')
|
||||||
|
.guilds.get({ query: options });
|
||||||
|
return data.reduce(
|
||||||
|
(coll, guild) => coll.set(guild.id, new OAuth2Guild(this.client, guild)),
|
||||||
|
new Collection(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -319,8 +332,10 @@ class GuildManager extends CachedManager {
|
|||||||
|
|
||||||
const data = await this.client.api.guilds(guildId)['incident-actions'].put({
|
const data = await this.client.api.guilds(guildId)['incident-actions'].put({
|
||||||
data: {
|
data: {
|
||||||
invites_disabled_until: invitesDisabledUntil && new Date(invitesDisabledUntil).toISOString(),
|
invites_disabled_until:
|
||||||
dms_disabled_until: dmsDisabledUntil && new Date(dmsDisabledUntil).toISOString(),
|
invitesDisabledUntil && new Date(invitesDisabledUntil).toISOString(),
|
||||||
|
dms_disabled_until:
|
||||||
|
dmsDisabledUntil && new Date(dmsDisabledUntil).toISOString(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -106,20 +106,41 @@ class GuildMemberManager extends CachedManager {
|
|||||||
deaf: options.deaf,
|
deaf: options.deaf,
|
||||||
};
|
};
|
||||||
if (options.roles) {
|
if (options.roles) {
|
||||||
if (!Array.isArray(options.roles) && !(options.roles instanceof Collection)) {
|
if (
|
||||||
throw new TypeError('INVALID_TYPE', 'options.roles', 'Array or Collection of Roles or Snowflakes', true);
|
!Array.isArray(options.roles) &&
|
||||||
|
!(options.roles instanceof Collection)
|
||||||
|
) {
|
||||||
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'options.roles',
|
||||||
|
'Array or Collection of Roles or Snowflakes',
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const resolvedRoles = [];
|
const resolvedRoles = [];
|
||||||
for (const role of options.roles.values()) {
|
for (const role of options.roles.values()) {
|
||||||
const resolvedRole = this.guild.roles.resolveId(role);
|
const resolvedRole = this.guild.roles.resolveId(role);
|
||||||
if (!resolvedRole) throw new TypeError('INVALID_ELEMENT', 'Array or Collection', 'options.roles', role);
|
if (!resolvedRole)
|
||||||
|
throw new TypeError(
|
||||||
|
'INVALID_ELEMENT',
|
||||||
|
'Array or Collection',
|
||||||
|
'options.roles',
|
||||||
|
role,
|
||||||
|
);
|
||||||
resolvedRoles.push(resolvedRole);
|
resolvedRoles.push(resolvedRole);
|
||||||
}
|
}
|
||||||
resolvedOptions.roles = resolvedRoles;
|
resolvedOptions.roles = resolvedRoles;
|
||||||
}
|
}
|
||||||
const data = await this.client.api.guilds(this.guild.id).members(userId).put({ data: resolvedOptions });
|
const data = await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
.members(userId)
|
||||||
|
.put({ data: resolvedOptions });
|
||||||
// Data is an empty buffer if the member is already part of the guild.
|
// Data is an empty buffer if the member is already part of the guild.
|
||||||
return data instanceof Buffer ? (options.fetchWhenExisting === false ? null : this.fetch(userId)) : this._add(data);
|
return data instanceof Buffer
|
||||||
|
? options.fetchWhenExisting === false
|
||||||
|
? null
|
||||||
|
: this.fetch(userId)
|
||||||
|
: this._add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -207,12 +228,13 @@ class GuildMemberManager extends CachedManager {
|
|||||||
if (user) return this._fetchSingle({ user, cache: true });
|
if (user) return this._fetchSingle({ user, cache: true });
|
||||||
if (options.user) {
|
if (options.user) {
|
||||||
if (Array.isArray(options.user)) {
|
if (Array.isArray(options.user)) {
|
||||||
options.user = options.user.map(u => this.client.users.resolveId(u));
|
options.user = options.user.map((u) => this.client.users.resolveId(u));
|
||||||
return this._fetchMany(options);
|
return this._fetchMany(options);
|
||||||
} else {
|
} else {
|
||||||
options.user = this.client.users.resolveId(options.user);
|
options.user = this.client.users.resolveId(options.user);
|
||||||
}
|
}
|
||||||
if (!options.limit && !options.withPresences) return this._fetchSingle(options);
|
if (!options.limit && !options.withPresences)
|
||||||
|
return this._fetchSingle(options);
|
||||||
}
|
}
|
||||||
return this._fetchMany(options);
|
return this._fetchMany(options);
|
||||||
}
|
}
|
||||||
@@ -240,8 +262,13 @@ class GuildMemberManager extends CachedManager {
|
|||||||
* @returns {Promise<Collection<Snowflake, GuildMember>>}
|
* @returns {Promise<Collection<Snowflake, GuildMember>>}
|
||||||
*/
|
*/
|
||||||
async search({ query, limit = 1, cache = true } = {}) {
|
async search({ query, limit = 1, cache = true } = {}) {
|
||||||
const data = await this.client.api.guilds(this.guild.id).members.search.get({ query: { query, limit } });
|
const data = await this.client.api
|
||||||
return data.reduce((col, member) => col.set(member.user.id, this._add(member, cache)), new Collection());
|
.guilds(this.guild.id)
|
||||||
|
.members.search.get({ query: { query, limit } });
|
||||||
|
return data.reduce(
|
||||||
|
(col, member) => col.set(member.user.id, this._add(member, cache)),
|
||||||
|
new Collection(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -286,10 +313,13 @@ class GuildMemberManager extends CachedManager {
|
|||||||
_data.channel_id = null;
|
_data.channel_id = null;
|
||||||
_data.channel = undefined;
|
_data.channel = undefined;
|
||||||
}
|
}
|
||||||
_data.roles &&= _data.roles.map(role => (role instanceof Role ? role.id : role));
|
_data.roles &&= _data.roles.map((role) =>
|
||||||
|
role instanceof Role ? role.id : role,
|
||||||
|
);
|
||||||
|
|
||||||
_data.communication_disabled_until =
|
_data.communication_disabled_until =
|
||||||
_data.communicationDisabledUntil && new Date(_data.communicationDisabledUntil).toISOString();
|
_data.communicationDisabledUntil &&
|
||||||
|
new Date(_data.communicationDisabledUntil).toISOString();
|
||||||
|
|
||||||
_data.flags = _data.flags && GuildMemberFlags.resolve(_data.flags);
|
_data.flags = _data.flags && GuildMemberFlags.resolve(_data.flags);
|
||||||
|
|
||||||
@@ -304,7 +334,10 @@ class GuildMemberManager extends CachedManager {
|
|||||||
let endpoint = this.client.api.guilds(this.guild.id);
|
let endpoint = this.client.api.guilds(this.guild.id);
|
||||||
if (id === this.client.user.id) {
|
if (id === this.client.user.id) {
|
||||||
const keys = Object.keys(data);
|
const keys = Object.keys(data);
|
||||||
if (keys.length === 1 && ['nick', 'avatar', 'banner', 'bio'].includes(keys[0])) {
|
if (
|
||||||
|
keys.length === 1 &&
|
||||||
|
['nick', 'avatar', 'banner', 'bio'].includes(keys[0])
|
||||||
|
) {
|
||||||
endpoint = endpoint.members('@me');
|
endpoint = endpoint.members('@me');
|
||||||
} else {
|
} else {
|
||||||
endpoint = endpoint.members(id);
|
endpoint = endpoint.members(id);
|
||||||
@@ -351,7 +384,13 @@ class GuildMemberManager extends CachedManager {
|
|||||||
* .then(pruned => console.log(`I just pruned ${pruned} people!`))
|
* .then(pruned => console.log(`I just pruned ${pruned} people!`))
|
||||||
* .catch(console.error);
|
* .catch(console.error);
|
||||||
*/
|
*/
|
||||||
async prune({ days = 7, dry = false, count: compute_prune_count = true, roles = [], reason } = {}) {
|
async prune({
|
||||||
|
days = 7,
|
||||||
|
dry = false,
|
||||||
|
count: compute_prune_count = true,
|
||||||
|
roles = [],
|
||||||
|
reason,
|
||||||
|
} = {}) {
|
||||||
if (typeof days !== 'number') throw new TypeError('PRUNE_DAYS_TYPE');
|
if (typeof days !== 'number') throw new TypeError('PRUNE_DAYS_TYPE');
|
||||||
|
|
||||||
const query = { days };
|
const query = { days };
|
||||||
@@ -440,7 +479,10 @@ class GuildMemberManager extends CachedManager {
|
|||||||
if (existing && !existing.partial) return existing;
|
if (existing && !existing.partial) return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await this.client.api.guilds(this.guild.id).members(user).get();
|
const data = await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
.members(user)
|
||||||
|
.get();
|
||||||
return this._add(data, cache);
|
return this._add(data, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,7 +497,11 @@ class GuildMemberManager extends CachedManager {
|
|||||||
const userId = this.resolveId(user);
|
const userId = this.resolveId(user);
|
||||||
const roleId = this.guild.roles.resolveId(role);
|
const roleId = this.guild.roles.resolveId(role);
|
||||||
|
|
||||||
await this.client.api.guilds(this.guild.id).members(userId).roles(roleId).put({ reason });
|
await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
.members(userId)
|
||||||
|
.roles(roleId)
|
||||||
|
.put({ reason });
|
||||||
|
|
||||||
return this.resolve(user) ?? this.client.users.resolve(user) ?? userId;
|
return this.resolve(user) ?? this.client.users.resolve(user) ?? userId;
|
||||||
}
|
}
|
||||||
@@ -471,7 +517,11 @@ class GuildMemberManager extends CachedManager {
|
|||||||
const userId = this.resolveId(user);
|
const userId = this.resolveId(user);
|
||||||
const roleId = this.guild.roles.resolveId(role);
|
const roleId = this.guild.roles.resolveId(role);
|
||||||
|
|
||||||
await this.client.api.guilds(this.guild.id).members(userId).roles(roleId).delete({ reason });
|
await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
.members(userId)
|
||||||
|
.roles(roleId)
|
||||||
|
.delete({ reason });
|
||||||
|
|
||||||
return this.resolve(user) ?? this.client.users.resolve(user) ?? userId;
|
return this.resolve(user) ?? this.client.users.resolve(user) ?? userId;
|
||||||
}
|
}
|
||||||
@@ -483,7 +533,7 @@ class GuildMemberManager extends CachedManager {
|
|||||||
* @returns {Promise<Collection<Snowflake, GuildMember>>}
|
* @returns {Promise<Collection<Snowflake, GuildMember>>}
|
||||||
*/
|
*/
|
||||||
fetchByMemberSafety(timeout = 15_000) {
|
fetchByMemberSafety(timeout = 15_000) {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
const nonce = SnowflakeUtil.generate();
|
const nonce = SnowflakeUtil.generate();
|
||||||
const fetchedMembers = new Collection();
|
const fetchedMembers = new Collection();
|
||||||
let timeout_ = setTimeout(() => {
|
let timeout_ = setTimeout(() => {
|
||||||
@@ -556,12 +606,17 @@ class GuildMemberManager extends CachedManager {
|
|||||||
for (const member of members.values()) {
|
for (const member of members.values()) {
|
||||||
fetchedMembers.set(member.id, member);
|
fetchedMembers.set(member.id, member);
|
||||||
}
|
}
|
||||||
if (members.size < 1_000 || (limit && fetchedMembers.size >= limit) || i === chunk.count) {
|
if (
|
||||||
|
members.size < 1_000 ||
|
||||||
|
(limit && fetchedMembers.size >= limit) ||
|
||||||
|
i === chunk.count
|
||||||
|
) {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
this.client.removeListener(Events.GUILD_MEMBERS_CHUNK, handler);
|
this.client.removeListener(Events.GUILD_MEMBERS_CHUNK, handler);
|
||||||
this.client.decrementMaxListeners();
|
this.client.decrementMaxListeners();
|
||||||
let fetched = fetchedMembers;
|
let fetched = fetchedMembers;
|
||||||
if (user_ids && !Array.isArray(user_ids) && fetched.size) fetched = fetched.first();
|
if (user_ids && !Array.isArray(user_ids) && fetched.size)
|
||||||
|
fetched = fetched.first();
|
||||||
resolve(fetched);
|
resolve(fetched);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ class GuildMemberRoleManager extends DataManager {
|
|||||||
*/
|
*/
|
||||||
get cache() {
|
get cache() {
|
||||||
const everyone = this.guild.roles.everyone;
|
const everyone = this.guild.roles.everyone;
|
||||||
return this.guild.roles.cache.filter(role => this.member._roles.includes(role.id)).set(everyone.id, everyone);
|
return this.guild.roles.cache
|
||||||
|
.filter((role) => this.member._roles.includes(role.id))
|
||||||
|
.set(everyone.id, everyone);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,9 +44,11 @@ class GuildMemberRoleManager extends DataManager {
|
|||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get hoist() {
|
get hoist() {
|
||||||
const hoistedRoles = this.cache.filter(role => role.hoist);
|
const hoistedRoles = this.cache.filter((role) => role.hoist);
|
||||||
if (!hoistedRoles.size) return null;
|
if (!hoistedRoles.size) return null;
|
||||||
return hoistedRoles.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev));
|
return hoistedRoles.reduce((prev, role) =>
|
||||||
|
role.comparePositionTo(prev) > 0 ? role : prev,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,9 +57,13 @@ class GuildMemberRoleManager extends DataManager {
|
|||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get icon() {
|
get icon() {
|
||||||
const iconRoles = this.cache.filter(role => role.icon || role.unicodeEmoji);
|
const iconRoles = this.cache.filter(
|
||||||
|
(role) => role.icon || role.unicodeEmoji,
|
||||||
|
);
|
||||||
if (!iconRoles.size) return null;
|
if (!iconRoles.size) return null;
|
||||||
return iconRoles.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev));
|
return iconRoles.reduce((prev, role) =>
|
||||||
|
role.comparePositionTo(prev) > 0 ? role : prev,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,9 +72,11 @@ class GuildMemberRoleManager extends DataManager {
|
|||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get color() {
|
get color() {
|
||||||
const coloredRoles = this.cache.filter(role => role.colors.primaryColor);
|
const coloredRoles = this.cache.filter((role) => role.colors.primaryColor);
|
||||||
if (!coloredRoles.size) return null;
|
if (!coloredRoles.size) return null;
|
||||||
return coloredRoles.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev));
|
return coloredRoles.reduce((prev, role) =>
|
||||||
|
role.comparePositionTo(prev) > 0 ? role : prev,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,7 +85,10 @@ class GuildMemberRoleManager extends DataManager {
|
|||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get highest() {
|
get highest() {
|
||||||
return this.cache.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev), this.cache.first());
|
return this.cache.reduce(
|
||||||
|
(prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev),
|
||||||
|
this.cache.first(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,7 +97,7 @@ class GuildMemberRoleManager extends DataManager {
|
|||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get premiumSubscriberRole() {
|
get premiumSubscriberRole() {
|
||||||
return this.cache.find(role => role.tags?.premiumSubscriberRole) ?? null;
|
return this.cache.find((role) => role.tags?.premiumSubscriberRole) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,7 +108,10 @@ class GuildMemberRoleManager extends DataManager {
|
|||||||
*/
|
*/
|
||||||
get botRole() {
|
get botRole() {
|
||||||
if (!this.member.user.bot) return null;
|
if (!this.member.user.bot) return null;
|
||||||
return this.cache.find(role => role.tags?.botId === this.member.user.id) ?? null;
|
return (
|
||||||
|
this.cache.find((role) => role.tags?.botId === this.member.user.id) ??
|
||||||
|
null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,7 +127,13 @@ class GuildMemberRoleManager extends DataManager {
|
|||||||
const resolvedRoles = [];
|
const resolvedRoles = [];
|
||||||
for (const role of roleOrRoles.values()) {
|
for (const role of roleOrRoles.values()) {
|
||||||
const resolvedRole = this.guild.roles.resolveId(role);
|
const resolvedRole = this.guild.roles.resolveId(role);
|
||||||
if (!resolvedRole) throw new TypeError('INVALID_ELEMENT', 'Array or Collection', 'roles', role);
|
if (!resolvedRole)
|
||||||
|
throw new TypeError(
|
||||||
|
'INVALID_ELEMENT',
|
||||||
|
'Array or Collection',
|
||||||
|
'roles',
|
||||||
|
role,
|
||||||
|
);
|
||||||
resolvedRoles.push(resolvedRole);
|
resolvedRoles.push(resolvedRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,10 +142,16 @@ class GuildMemberRoleManager extends DataManager {
|
|||||||
} else {
|
} else {
|
||||||
roleOrRoles = this.guild.roles.resolveId(roleOrRoles);
|
roleOrRoles = this.guild.roles.resolveId(roleOrRoles);
|
||||||
if (roleOrRoles === null) {
|
if (roleOrRoles === null) {
|
||||||
throw new TypeError('INVALID_TYPE', 'roles', 'Role, Snowflake or Array or Collection of Roles or Snowflakes');
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'roles',
|
||||||
|
'Role, Snowflake or Array or Collection of Roles or Snowflakes',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.client.api.guilds[this.guild.id].members[this.member.id].roles[roleOrRoles].put({ reason });
|
await this.client.api.guilds[this.guild.id].members[this.member.id].roles[
|
||||||
|
roleOrRoles
|
||||||
|
].put({ reason });
|
||||||
|
|
||||||
const clone = this.member._clone();
|
const clone = this.member._clone();
|
||||||
clone._roles = [...this.cache.keys(), roleOrRoles];
|
clone._roles = [...this.cache.keys(), roleOrRoles];
|
||||||
@@ -144,22 +172,36 @@ class GuildMemberRoleManager extends DataManager {
|
|||||||
const resolvedRoles = [];
|
const resolvedRoles = [];
|
||||||
for (const role of roleOrRoles.values()) {
|
for (const role of roleOrRoles.values()) {
|
||||||
const resolvedRole = this.guild.roles.resolveId(role);
|
const resolvedRole = this.guild.roles.resolveId(role);
|
||||||
if (!resolvedRole) throw new TypeError('INVALID_ELEMENT', 'Array or Collection', 'roles', role);
|
if (!resolvedRole)
|
||||||
|
throw new TypeError(
|
||||||
|
'INVALID_ELEMENT',
|
||||||
|
'Array or Collection',
|
||||||
|
'roles',
|
||||||
|
role,
|
||||||
|
);
|
||||||
resolvedRoles.push(resolvedRole);
|
resolvedRoles.push(resolvedRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newRoles = this.cache.filter(role => !resolvedRoles.includes(role.id));
|
const newRoles = this.cache.filter(
|
||||||
|
(role) => !resolvedRoles.includes(role.id),
|
||||||
|
);
|
||||||
return this.set(newRoles, reason);
|
return this.set(newRoles, reason);
|
||||||
} else {
|
} else {
|
||||||
roleOrRoles = this.guild.roles.resolveId(roleOrRoles);
|
roleOrRoles = this.guild.roles.resolveId(roleOrRoles);
|
||||||
if (roleOrRoles === null) {
|
if (roleOrRoles === null) {
|
||||||
throw new TypeError('INVALID_TYPE', 'roles', 'Role, Snowflake or Array or Collection of Roles or Snowflakes');
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'roles',
|
||||||
|
'Role, Snowflake or Array or Collection of Roles or Snowflakes',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.client.api.guilds[this.guild.id].members[this.member.id].roles[roleOrRoles].delete({ reason });
|
await this.client.api.guilds[this.guild.id].members[this.member.id].roles[
|
||||||
|
roleOrRoles
|
||||||
|
].delete({ reason });
|
||||||
|
|
||||||
const clone = this.member._clone();
|
const clone = this.member._clone();
|
||||||
const newRoles = this.cache.filter(role => role.id !== roleOrRoles);
|
const newRoles = this.cache.filter((role) => role.id !== roleOrRoles);
|
||||||
clone._roles = [...newRoles.keys()];
|
clone._roles = [...newRoles.keys()];
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ const { Collection } = require('@discordjs/collection');
|
|||||||
const CachedManager = require('./CachedManager');
|
const CachedManager = require('./CachedManager');
|
||||||
const { TypeError, Error } = require('../errors');
|
const { TypeError, Error } = require('../errors');
|
||||||
const { GuildScheduledEvent } = require('../structures/GuildScheduledEvent');
|
const { GuildScheduledEvent } = require('../structures/GuildScheduledEvent');
|
||||||
const { PrivacyLevels, GuildScheduledEventEntityTypes, GuildScheduledEventStatuses } = require('../util/Constants');
|
const {
|
||||||
|
PrivacyLevels,
|
||||||
|
GuildScheduledEventEntityTypes,
|
||||||
|
GuildScheduledEventStatuses,
|
||||||
|
} = require('../util/Constants');
|
||||||
const DataResolver = require('../util/DataResolver');
|
const DataResolver = require('../util/DataResolver');
|
||||||
const Util = require('../util/Util');
|
const Util = require('../util/Util');
|
||||||
|
|
||||||
@@ -82,7 +86,8 @@ class GuildScheduledEventManager extends CachedManager {
|
|||||||
* @returns {Promise<GuildScheduledEvent>}
|
* @returns {Promise<GuildScheduledEvent>}
|
||||||
*/
|
*/
|
||||||
async create(options) {
|
async create(options) {
|
||||||
if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true);
|
if (typeof options !== 'object')
|
||||||
|
throw new TypeError('INVALID_TYPE', 'options', 'object', true);
|
||||||
let {
|
let {
|
||||||
privacyLevel,
|
privacyLevel,
|
||||||
entityType,
|
entityType,
|
||||||
@@ -97,8 +102,10 @@ class GuildScheduledEventManager extends CachedManager {
|
|||||||
recurrenceRule,
|
recurrenceRule,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
if (typeof privacyLevel === 'string') privacyLevel = PrivacyLevels[privacyLevel];
|
if (typeof privacyLevel === 'string')
|
||||||
if (typeof entityType === 'string') entityType = GuildScheduledEventEntityTypes[entityType];
|
privacyLevel = PrivacyLevels[privacyLevel];
|
||||||
|
if (typeof entityType === 'string')
|
||||||
|
entityType = GuildScheduledEventEntityTypes[entityType];
|
||||||
|
|
||||||
let entity_metadata, channel_id;
|
let entity_metadata, channel_id;
|
||||||
if (entityType === GuildScheduledEventEntityTypes.EXTERNAL) {
|
if (entityType === GuildScheduledEventEntityTypes.EXTERNAL) {
|
||||||
@@ -107,21 +114,28 @@ class GuildScheduledEventManager extends CachedManager {
|
|||||||
} else {
|
} else {
|
||||||
channel_id = this.guild.channels.resolveId(channel);
|
channel_id = this.guild.channels.resolveId(channel);
|
||||||
if (!channel_id) throw new Error('GUILD_VOICE_CHANNEL_RESOLVE');
|
if (!channel_id) throw new Error('GUILD_VOICE_CHANNEL_RESOLVE');
|
||||||
entity_metadata = typeof entityMetadata === 'undefined' ? entityMetadata : null;
|
entity_metadata =
|
||||||
|
typeof entityMetadata === 'undefined' ? entityMetadata : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await this.client.api.guilds(this.guild.id, 'scheduled-events').post({
|
const data = await this.client.api
|
||||||
|
.guilds(this.guild.id, 'scheduled-events')
|
||||||
|
.post({
|
||||||
data: {
|
data: {
|
||||||
channel_id,
|
channel_id,
|
||||||
name,
|
name,
|
||||||
privacy_level: privacyLevel,
|
privacy_level: privacyLevel,
|
||||||
scheduled_start_time: new Date(scheduledStartTime).toISOString(),
|
scheduled_start_time: new Date(scheduledStartTime).toISOString(),
|
||||||
scheduled_end_time: scheduledEndTime ? new Date(scheduledEndTime).toISOString() : scheduledEndTime,
|
scheduled_end_time: scheduledEndTime
|
||||||
|
? new Date(scheduledEndTime).toISOString()
|
||||||
|
: scheduledEndTime,
|
||||||
description,
|
description,
|
||||||
image: image && (await DataResolver.resolveImage(image)),
|
image: image && (await DataResolver.resolveImage(image)),
|
||||||
entity_type: entityType,
|
entity_type: entityType,
|
||||||
entity_metadata,
|
entity_metadata,
|
||||||
recurrence_rule: recurrenceRule && Util.transformGuildScheduledEventRecurrenceRule(recurrenceRule),
|
recurrence_rule:
|
||||||
|
recurrenceRule &&
|
||||||
|
Util.transformGuildScheduledEventRecurrenceRule(recurrenceRule),
|
||||||
},
|
},
|
||||||
reason,
|
reason,
|
||||||
});
|
});
|
||||||
@@ -171,7 +185,10 @@ class GuildScheduledEventManager extends CachedManager {
|
|||||||
|
|
||||||
return data.reduce(
|
return data.reduce(
|
||||||
(coll, rawGuildScheduledEventData) =>
|
(coll, rawGuildScheduledEventData) =>
|
||||||
coll.set(rawGuildScheduledEventData.id, this._add(rawGuildScheduledEventData, options.cache)),
|
coll.set(
|
||||||
|
rawGuildScheduledEventData.id,
|
||||||
|
this._add(rawGuildScheduledEventData, options.cache),
|
||||||
|
),
|
||||||
new Collection(),
|
new Collection(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -204,9 +221,11 @@ class GuildScheduledEventManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async edit(guildScheduledEvent, options) {
|
async edit(guildScheduledEvent, options) {
|
||||||
const guildScheduledEventId = this.resolveId(guildScheduledEvent);
|
const guildScheduledEventId = this.resolveId(guildScheduledEvent);
|
||||||
if (!guildScheduledEventId) throw new Error('GUILD_SCHEDULED_EVENT_RESOLVE');
|
if (!guildScheduledEventId)
|
||||||
|
throw new Error('GUILD_SCHEDULED_EVENT_RESOLVE');
|
||||||
|
|
||||||
if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true);
|
if (typeof options !== 'object')
|
||||||
|
throw new TypeError('INVALID_TYPE', 'options', 'object', true);
|
||||||
let {
|
let {
|
||||||
privacyLevel,
|
privacyLevel,
|
||||||
entityType,
|
entityType,
|
||||||
@@ -222,9 +241,12 @@ class GuildScheduledEventManager extends CachedManager {
|
|||||||
recurrenceRule,
|
recurrenceRule,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
if (typeof privacyLevel === 'string') privacyLevel = PrivacyLevels[privacyLevel];
|
if (typeof privacyLevel === 'string')
|
||||||
if (typeof entityType === 'string') entityType = GuildScheduledEventEntityTypes[entityType];
|
privacyLevel = PrivacyLevels[privacyLevel];
|
||||||
if (typeof status === 'string') status = GuildScheduledEventStatuses[status];
|
if (typeof entityType === 'string')
|
||||||
|
entityType = GuildScheduledEventEntityTypes[entityType];
|
||||||
|
if (typeof status === 'string')
|
||||||
|
status = GuildScheduledEventStatuses[status];
|
||||||
|
|
||||||
let entity_metadata;
|
let entity_metadata;
|
||||||
if (entityMetadata) {
|
if (entityMetadata) {
|
||||||
@@ -233,19 +255,30 @@ class GuildScheduledEventManager extends CachedManager {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await this.client.api.guilds(this.guild.id, 'scheduled-events', guildScheduledEventId).patch({
|
const data = await this.client.api
|
||||||
|
.guilds(this.guild.id, 'scheduled-events', guildScheduledEventId)
|
||||||
|
.patch({
|
||||||
data: {
|
data: {
|
||||||
channel_id: typeof channel === 'undefined' ? channel : this.guild.channels.resolveId(channel),
|
channel_id:
|
||||||
|
typeof channel === 'undefined'
|
||||||
|
? channel
|
||||||
|
: this.guild.channels.resolveId(channel),
|
||||||
name,
|
name,
|
||||||
privacy_level: privacyLevel,
|
privacy_level: privacyLevel,
|
||||||
scheduled_start_time: scheduledStartTime ? new Date(scheduledStartTime).toISOString() : undefined,
|
scheduled_start_time: scheduledStartTime
|
||||||
scheduled_end_time: scheduledEndTime ? new Date(scheduledEndTime).toISOString() : scheduledEndTime,
|
? new Date(scheduledStartTime).toISOString()
|
||||||
|
: undefined,
|
||||||
|
scheduled_end_time: scheduledEndTime
|
||||||
|
? new Date(scheduledEndTime).toISOString()
|
||||||
|
: scheduledEndTime,
|
||||||
description,
|
description,
|
||||||
entity_type: entityType,
|
entity_type: entityType,
|
||||||
status,
|
status,
|
||||||
image: image && (await DataResolver.resolveImage(image)),
|
image: image && (await DataResolver.resolveImage(image)),
|
||||||
entity_metadata,
|
entity_metadata,
|
||||||
recurrence_rule: recurrenceRule && Util.transformGuildScheduledEventRecurrenceRule(recurrenceRule),
|
recurrence_rule:
|
||||||
|
recurrenceRule &&
|
||||||
|
Util.transformGuildScheduledEventRecurrenceRule(recurrenceRule),
|
||||||
},
|
},
|
||||||
reason,
|
reason,
|
||||||
});
|
});
|
||||||
@@ -260,9 +293,12 @@ class GuildScheduledEventManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async delete(guildScheduledEvent) {
|
async delete(guildScheduledEvent) {
|
||||||
const guildScheduledEventId = this.resolveId(guildScheduledEvent);
|
const guildScheduledEventId = this.resolveId(guildScheduledEvent);
|
||||||
if (!guildScheduledEventId) throw new Error('GUILD_SCHEDULED_EVENT_RESOLVE');
|
if (!guildScheduledEventId)
|
||||||
|
throw new Error('GUILD_SCHEDULED_EVENT_RESOLVE');
|
||||||
|
|
||||||
await this.client.api.guilds(this.guild.id, 'scheduled-events', guildScheduledEventId).delete();
|
await this.client.api
|
||||||
|
.guilds(this.guild.id, 'scheduled-events', guildScheduledEventId)
|
||||||
|
.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -291,11 +327,14 @@ class GuildScheduledEventManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async fetchSubscribers(guildScheduledEvent, options = {}) {
|
async fetchSubscribers(guildScheduledEvent, options = {}) {
|
||||||
const guildScheduledEventId = this.resolveId(guildScheduledEvent);
|
const guildScheduledEventId = this.resolveId(guildScheduledEvent);
|
||||||
if (!guildScheduledEventId) throw new Error('GUILD_SCHEDULED_EVENT_RESOLVE');
|
if (!guildScheduledEventId)
|
||||||
|
throw new Error('GUILD_SCHEDULED_EVENT_RESOLVE');
|
||||||
|
|
||||||
let { limit, withMember, before, after } = options;
|
let { limit, withMember, before, after } = options;
|
||||||
|
|
||||||
const data = await this.client.api.guilds(this.guild.id, 'scheduled-events', guildScheduledEventId).users.get({
|
const data = await this.client.api
|
||||||
|
.guilds(this.guild.id, 'scheduled-events', guildScheduledEventId)
|
||||||
|
.users.get({
|
||||||
query: { limit, with_member: withMember, before, after },
|
query: { limit, with_member: withMember, before, after },
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -304,7 +343,9 @@ class GuildScheduledEventManager extends CachedManager {
|
|||||||
coll.set(rawData.user.id, {
|
coll.set(rawData.user.id, {
|
||||||
guildScheduledEventId: rawData.guild_scheduled_event_id,
|
guildScheduledEventId: rawData.guild_scheduled_event_id,
|
||||||
user: this.client.users._add(rawData.user),
|
user: this.client.users._add(rawData.user),
|
||||||
member: rawData.member ? this.guild.members._add({ ...rawData.member, user: rawData.user }) : null,
|
member: rawData.member
|
||||||
|
? this.guild.members._add({ ...rawData.member, user: rawData.user })
|
||||||
|
: null,
|
||||||
}),
|
}),
|
||||||
new Collection(),
|
new Collection(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -66,7 +66,8 @@ class GuildStickerManager extends CachedManager {
|
|||||||
const sticker = await this.client.api
|
const sticker = await this.client.api
|
||||||
.guilds(this.guild.id)
|
.guilds(this.guild.id)
|
||||||
.stickers.post({ data, files: [file], reason, dontUsePayloadJSON: true });
|
.stickers.post({ data, files: [file], reason, dontUsePayloadJSON: true });
|
||||||
return this.client.actions.GuildStickerCreate.handle(this.guild, sticker).sticker;
|
return this.client.actions.GuildStickerCreate.handle(this.guild, sticker)
|
||||||
|
.sticker;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,9 +104,13 @@ class GuildStickerManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async edit(sticker, data, reason) {
|
async edit(sticker, data, reason) {
|
||||||
const stickerId = this.resolveId(sticker);
|
const stickerId = this.resolveId(sticker);
|
||||||
if (!stickerId) throw new TypeError('INVALID_TYPE', 'sticker', 'StickerResolvable');
|
if (!stickerId)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'sticker', 'StickerResolvable');
|
||||||
|
|
||||||
const d = await this.client.api.guilds(this.guild.id).stickers(stickerId).patch({
|
const d = await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
.stickers(stickerId)
|
||||||
|
.patch({
|
||||||
data,
|
data,
|
||||||
reason,
|
reason,
|
||||||
});
|
});
|
||||||
@@ -127,9 +132,13 @@ class GuildStickerManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async delete(sticker, reason) {
|
async delete(sticker, reason) {
|
||||||
sticker = this.resolveId(sticker);
|
sticker = this.resolveId(sticker);
|
||||||
if (!sticker) throw new TypeError('INVALID_TYPE', 'sticker', 'StickerResolvable');
|
if (!sticker)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'sticker', 'StickerResolvable');
|
||||||
|
|
||||||
await this.client.api.guilds(this.guild.id).stickers(sticker).delete({ reason });
|
await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
.stickers(sticker)
|
||||||
|
.delete({ reason });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -154,12 +163,17 @@ class GuildStickerManager extends CachedManager {
|
|||||||
const existing = this.cache.get(id);
|
const existing = this.cache.get(id);
|
||||||
if (existing) return existing;
|
if (existing) return existing;
|
||||||
}
|
}
|
||||||
const sticker = await this.client.api.guilds(this.guild.id).stickers(id).get();
|
const sticker = await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
.stickers(id)
|
||||||
|
.get();
|
||||||
return this._add(sticker, cache);
|
return this._add(sticker, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await this.client.api.guilds(this.guild.id).stickers.get();
|
const data = await this.client.api.guilds(this.guild.id).stickers.get();
|
||||||
return new Collection(data.map(sticker => [sticker.id, this._add(sticker, cache)]));
|
return new Collection(
|
||||||
|
data.map((sticker) => [sticker.id, this._add(sticker, cache)]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -169,8 +183,12 @@ class GuildStickerManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async fetchUser(sticker) {
|
async fetchUser(sticker) {
|
||||||
sticker = this.resolve(sticker);
|
sticker = this.resolve(sticker);
|
||||||
if (!sticker) throw new TypeError('INVALID_TYPE', 'sticker', 'StickerResolvable');
|
if (!sticker)
|
||||||
const data = await this.client.api.guilds(this.guild.id).stickers(sticker.id).get();
|
throw new TypeError('INVALID_TYPE', 'sticker', 'StickerResolvable');
|
||||||
|
const data = await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
.stickers(sticker.id)
|
||||||
|
.get();
|
||||||
sticker._patch(data);
|
sticker._patch(data);
|
||||||
return sticker.user;
|
return sticker.user;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,26 +66,42 @@ class GuildTextThreadManager extends ThreadManager {
|
|||||||
} = {}) {
|
} = {}) {
|
||||||
let path = this.client.api.channels(this.channel.id);
|
let path = this.client.api.channels(this.channel.id);
|
||||||
if (type && typeof type !== 'string' && typeof type !== 'number') {
|
if (type && typeof type !== 'string' && typeof type !== 'number') {
|
||||||
throw new TypeError('INVALID_TYPE', 'type', 'ThreadChannelType or Number');
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'type',
|
||||||
|
'ThreadChannelType or Number',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let resolvedType =
|
let resolvedType =
|
||||||
this.channel.type === 'GUILD_NEWS' ? ChannelTypes.GUILD_NEWS_THREAD : ChannelTypes.GUILD_PUBLIC_THREAD;
|
this.channel.type === 'GUILD_NEWS'
|
||||||
|
? ChannelTypes.GUILD_NEWS_THREAD
|
||||||
|
: ChannelTypes.GUILD_PUBLIC_THREAD;
|
||||||
if (startMessage) {
|
if (startMessage) {
|
||||||
const startMessageId = this.channel.messages.resolveId(startMessage);
|
const startMessageId = this.channel.messages.resolveId(startMessage);
|
||||||
if (!startMessageId) throw new TypeError('INVALID_TYPE', 'startMessage', 'MessageResolvable');
|
if (!startMessageId)
|
||||||
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'startMessage',
|
||||||
|
'MessageResolvable',
|
||||||
|
);
|
||||||
path = path.messages(startMessageId);
|
path = path.messages(startMessageId);
|
||||||
} else if (this.channel.type !== 'GUILD_NEWS') {
|
} else if (this.channel.type !== 'GUILD_NEWS') {
|
||||||
resolvedType = typeof type === 'string' ? ChannelTypes[type] : type ?? resolvedType;
|
resolvedType =
|
||||||
|
typeof type === 'string' ? ChannelTypes[type] : (type ?? resolvedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (autoArchiveDuration === 'MAX') autoArchiveDuration = resolveAutoArchiveMaxLimit(this.channel.guild);
|
if (autoArchiveDuration === 'MAX')
|
||||||
|
autoArchiveDuration = resolveAutoArchiveMaxLimit(this.channel.guild);
|
||||||
|
|
||||||
const data = await path.threads.post({
|
const data = await path.threads.post({
|
||||||
data: {
|
data: {
|
||||||
name,
|
name,
|
||||||
auto_archive_duration: autoArchiveDuration,
|
auto_archive_duration: autoArchiveDuration,
|
||||||
type: resolvedType,
|
type: resolvedType,
|
||||||
invitable: resolvedType === ChannelTypes.GUILD_PRIVATE_THREAD ? invitable : undefined,
|
invitable:
|
||||||
|
resolvedType === ChannelTypes.GUILD_PRIVATE_THREAD
|
||||||
|
? invitable
|
||||||
|
: undefined,
|
||||||
rate_limit_per_user: rateLimitPerUser,
|
rate_limit_per_user: rateLimitPerUser,
|
||||||
},
|
},
|
||||||
reason,
|
reason,
|
||||||
|
|||||||
@@ -66,7 +66,9 @@ class MessageManager extends CachedManager {
|
|||||||
* .catch(console.error);
|
* .catch(console.error);
|
||||||
*/
|
*/
|
||||||
fetch(message, { cache = true, force = false } = {}) {
|
fetch(message, { cache = true, force = false } = {}) {
|
||||||
return typeof message === 'string' ? this._fetchId(message, cache, force) : this._fetchMany(message, cache);
|
return typeof message === 'string'
|
||||||
|
? this._fetchId(message, cache, force)
|
||||||
|
: this._fetchMany(message, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,11 +84,14 @@ class MessageManager extends CachedManager {
|
|||||||
* .catch(console.error);
|
* .catch(console.error);
|
||||||
*/
|
*/
|
||||||
async fetchPinned(cache = true) {
|
async fetchPinned(cache = true) {
|
||||||
const data = await this.client.api.channels[this.channel.id].messages.pins.get({
|
const data = await this.client.api.channels[
|
||||||
|
this.channel.id
|
||||||
|
].messages.pins.get({
|
||||||
query: { limit: 50 },
|
query: { limit: 50 },
|
||||||
});
|
});
|
||||||
const messages = new Collection();
|
const messages = new Collection();
|
||||||
for (const message of data?.items || []) messages.set(message.id, this._add(message, cache));
|
for (const message of data?.items || [])
|
||||||
|
messages.set(message.id, this._add(message, cache));
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,18 +128,26 @@ class MessageManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async edit(message, options) {
|
async edit(message, options) {
|
||||||
const messageId = this.resolveId(message);
|
const messageId = this.resolveId(message);
|
||||||
if (!messageId) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
if (!messageId)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
||||||
|
|
||||||
const { data, files } = await (options instanceof MessagePayload
|
const { data, files } = await (options instanceof MessagePayload
|
||||||
? options
|
? options
|
||||||
: MessagePayload.create(message instanceof Message ? message : this, options)
|
: MessagePayload.create(
|
||||||
|
message instanceof Message ? message : this,
|
||||||
|
options,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.resolveData()
|
.resolveData()
|
||||||
.resolveFiles();
|
.resolveFiles();
|
||||||
|
|
||||||
// New API
|
// New API
|
||||||
const attachments = await Util.getUploadURL(this.client, this.channel.id, files);
|
const attachments = await Util.getUploadURL(
|
||||||
const requestPromises = attachments.map(async attachment => {
|
this.client,
|
||||||
|
this.channel.id,
|
||||||
|
files,
|
||||||
|
);
|
||||||
|
const requestPromises = attachments.map(async (attachment) => {
|
||||||
await Util.uploadFile(files[attachment.id].file, attachment.upload_url);
|
await Util.uploadFile(files[attachment.id].file, attachment.upload_url);
|
||||||
return {
|
return {
|
||||||
id: attachment.id,
|
id: attachment.id,
|
||||||
@@ -150,7 +163,9 @@ class MessageManager extends CachedManager {
|
|||||||
data.attachments = attachmentsData;
|
data.attachments = attachmentsData;
|
||||||
// Empty Files
|
// Empty Files
|
||||||
|
|
||||||
const d = await this.client.api.channels[this.channel.id].messages[messageId].patch({ data });
|
const d = await this.client.api.channels[this.channel.id].messages[
|
||||||
|
messageId
|
||||||
|
].patch({ data });
|
||||||
|
|
||||||
const existing = this.cache.get(messageId);
|
const existing = this.cache.get(messageId);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
@@ -168,9 +183,13 @@ class MessageManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async crosspost(message) {
|
async crosspost(message) {
|
||||||
message = this.resolveId(message);
|
message = this.resolveId(message);
|
||||||
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
if (!message)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
||||||
|
|
||||||
const data = await this.client.api.channels(this.channel.id).messages(message).crosspost.post();
|
const data = await this.client.api
|
||||||
|
.channels(this.channel.id)
|
||||||
|
.messages(message)
|
||||||
|
.crosspost.post();
|
||||||
return this.cache.get(data.id) ?? this._add(data);
|
return this.cache.get(data.id) ?? this._add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,9 +201,13 @@ class MessageManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async pin(message, reason) {
|
async pin(message, reason) {
|
||||||
message = this.resolveId(message);
|
message = this.resolveId(message);
|
||||||
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
if (!message)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
||||||
|
|
||||||
await this.client.api.channels(this.channel.id).messages.pins(message).put({ reason });
|
await this.client.api
|
||||||
|
.channels(this.channel.id)
|
||||||
|
.messages.pins(message)
|
||||||
|
.put({ reason });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -195,9 +218,13 @@ class MessageManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async unpin(message, reason) {
|
async unpin(message, reason) {
|
||||||
message = this.resolveId(message);
|
message = this.resolveId(message);
|
||||||
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
if (!message)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
||||||
|
|
||||||
await this.client.api.channels(this.channel.id).messages.pins(message).delete({ reason });
|
await this.client.api
|
||||||
|
.channels(this.channel.id)
|
||||||
|
.messages.pins(message)
|
||||||
|
.delete({ reason });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -209,10 +236,12 @@ class MessageManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async react(message, emoji, burst = false) {
|
async react(message, emoji, burst = false) {
|
||||||
message = this.resolveId(message);
|
message = this.resolveId(message);
|
||||||
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
if (!message)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
||||||
|
|
||||||
emoji = Util.resolvePartialEmoji(emoji);
|
emoji = Util.resolvePartialEmoji(emoji);
|
||||||
if (!emoji) throw new TypeError('EMOJI_TYPE', 'emoji', 'EmojiIdentifierResolvable');
|
if (!emoji)
|
||||||
|
throw new TypeError('EMOJI_TYPE', 'emoji', 'EmojiIdentifierResolvable');
|
||||||
|
|
||||||
const emojiId = emoji.id
|
const emojiId = emoji.id
|
||||||
? `${emoji.animated ? 'a:' : ''}${emoji.name}:${emoji.id}`
|
? `${emoji.animated ? 'a:' : ''}${emoji.name}:${emoji.id}`
|
||||||
@@ -237,7 +266,8 @@ class MessageManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async delete(message) {
|
async delete(message) {
|
||||||
message = this.resolveId(message);
|
message = this.resolveId(message);
|
||||||
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
if (!message)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
|
||||||
|
|
||||||
await this.client.api.channels(this.channel.id).messages(message).delete();
|
await this.client.api.channels(this.channel.id).messages(message).delete();
|
||||||
}
|
}
|
||||||
@@ -257,8 +287,10 @@ class MessageManager extends CachedManager {
|
|||||||
},
|
},
|
||||||
cache,
|
cache,
|
||||||
)
|
)
|
||||||
.then(data_ =>
|
.then((data_) =>
|
||||||
data_.has(messageId) ? resolve(data_.get(messageId)) : reject(new Error('MESSAGE_ID_NOT_FOUND')),
|
data_.has(messageId)
|
||||||
|
? resolve(data_.get(messageId))
|
||||||
|
: reject(new Error('MESSAGE_ID_NOT_FOUND')),
|
||||||
)
|
)
|
||||||
.catch(reject);
|
.catch(reject);
|
||||||
});
|
});
|
||||||
@@ -296,8 +328,21 @@ class MessageManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async search(options = {}) {
|
async search(options = {}) {
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
let { authors, content, mentions, has, maxId, minId, channels, pinned, nsfw, offset, limit, sortBy, sortOrder } =
|
let {
|
||||||
Object.assign(
|
authors,
|
||||||
|
content,
|
||||||
|
mentions,
|
||||||
|
has,
|
||||||
|
maxId,
|
||||||
|
minId,
|
||||||
|
channels,
|
||||||
|
pinned,
|
||||||
|
nsfw,
|
||||||
|
offset,
|
||||||
|
limit,
|
||||||
|
sortBy,
|
||||||
|
sortOrder,
|
||||||
|
} = Object.assign(
|
||||||
{
|
{
|
||||||
authors: [],
|
authors: [],
|
||||||
content: '',
|
content: '',
|
||||||
@@ -316,17 +361,20 @@ class MessageManager extends CachedManager {
|
|||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
// Validate
|
// Validate
|
||||||
if (authors.length > 0) authors = authors.map(u => this.client.users.resolveId(u));
|
if (authors.length > 0)
|
||||||
if (mentions.length > 0) mentions = mentions.map(u => this.client.users.resolveId(u));
|
authors = authors.map((u) => this.client.users.resolveId(u));
|
||||||
|
if (mentions.length > 0)
|
||||||
|
mentions = mentions.map((u) => this.client.users.resolveId(u));
|
||||||
if (channels.length > 0) {
|
if (channels.length > 0) {
|
||||||
channels = channels
|
channels = channels
|
||||||
.map(c => this.client.channels.resolveId(c))
|
.map((c) => this.client.channels.resolveId(c))
|
||||||
.filter(id => {
|
.filter((id) => {
|
||||||
if (this.channel.guildId) {
|
if (this.channel.guildId) {
|
||||||
const c = this.channel.guild.channels.cache.get(id);
|
const c = this.channel.guild.channels.cache.get(id);
|
||||||
if (!c || !c.messages) return false;
|
if (!c || !c.messages) return false;
|
||||||
const perm = c.permissionsFor(this.client.user);
|
const perm = c.permissionsFor(this.client.user);
|
||||||
if (!perm.has('READ_MESSAGE_HISTORY') || !perm.has('VIEW_CHANNEL')) return false;
|
if (!perm.has('READ_MESSAGE_HISTORY') || !perm.has('VIEW_CHANNEL'))
|
||||||
|
return false;
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
@@ -337,11 +385,18 @@ class MessageManager extends CachedManager {
|
|||||||
let stringQuery = [];
|
let stringQuery = [];
|
||||||
const result = new Collection();
|
const result = new Collection();
|
||||||
let data;
|
let data;
|
||||||
if (authors.length > 0) stringQuery.push(authors.map(id => `author_id=${id}`).join('&'));
|
if (authors.length > 0)
|
||||||
if (content && content.length) stringQuery.push(`content=${encodeURIComponent(content)}`);
|
stringQuery.push(authors.map((id) => `author_id=${id}`).join('&'));
|
||||||
if (mentions.length > 0) stringQuery.push(mentions.map(id => `mentions=${id}`).join('&'));
|
if (content && content.length)
|
||||||
has = has.filter(v => ['link', 'embed', 'file', 'video', 'image', 'sound', 'sticker'].includes(v));
|
stringQuery.push(`content=${encodeURIComponent(content)}`);
|
||||||
if (has.length > 0) stringQuery.push(has.map(v => `has=${v}`).join('&'));
|
if (mentions.length > 0)
|
||||||
|
stringQuery.push(mentions.map((id) => `mentions=${id}`).join('&'));
|
||||||
|
has = has.filter((v) =>
|
||||||
|
['link', 'embed', 'file', 'video', 'image', 'sound', 'sticker'].includes(
|
||||||
|
v,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (has.length > 0) stringQuery.push(has.map((v) => `has=${v}`).join('&'));
|
||||||
if (maxId) stringQuery.push(`max_id=${maxId}`);
|
if (maxId) stringQuery.push(`max_id=${maxId}`);
|
||||||
if (minId) stringQuery.push(`min_id=${minId}`);
|
if (minId) stringQuery.push(`min_id=${minId}`);
|
||||||
if (nsfw) stringQuery.push('include_nsfw=true');
|
if (nsfw) stringQuery.push('include_nsfw=true');
|
||||||
@@ -358,7 +413,7 @@ class MessageManager extends CachedManager {
|
|||||||
stringQuery.push('sort_order=desc');
|
stringQuery.push('sort_order=desc');
|
||||||
}
|
}
|
||||||
if (this.channel.guildId && channels.length > 0) {
|
if (this.channel.guildId && channels.length > 0) {
|
||||||
stringQuery.push(channels.map(id => `channel_id=${id}`).join('&'));
|
stringQuery.push(channels.map((id) => `channel_id=${id}`).join('&'));
|
||||||
}
|
}
|
||||||
if (typeof pinned == 'boolean') stringQuery.push(`pinned=${pinned}`);
|
if (typeof pinned == 'boolean') stringQuery.push(`pinned=${pinned}`);
|
||||||
// Main
|
// Main
|
||||||
@@ -369,13 +424,22 @@ class MessageManager extends CachedManager {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (this.channel.guildId) {
|
if (this.channel.guildId) {
|
||||||
data = await this.client.api.guilds[this.channel.guildId].messages[`search?${stringQuery.join('&')}`].get();
|
data =
|
||||||
|
await this.client.api.guilds[this.channel.guildId].messages[
|
||||||
|
`search?${stringQuery.join('&')}`
|
||||||
|
].get();
|
||||||
} else {
|
} else {
|
||||||
stringQuery = stringQuery.filter(v => !v.startsWith('channel_id') && !v.startsWith('include_nsfw'));
|
stringQuery = stringQuery.filter(
|
||||||
data = await this.client.api.channels[this.channel.id].messages[`search?${stringQuery.join('&')}`].get();
|
(v) => !v.startsWith('channel_id') && !v.startsWith('include_nsfw'),
|
||||||
|
);
|
||||||
|
data =
|
||||||
|
await this.client.api.channels[this.channel.id].messages[
|
||||||
|
`search?${stringQuery.join('&')}`
|
||||||
|
].get();
|
||||||
}
|
}
|
||||||
|
|
||||||
for await (const message of data.messages) result.set(message[0].id, new Message(this.client, message[0]));
|
for await (const message of data.messages)
|
||||||
|
result.set(message[0].id, new Message(this.client, message[0]));
|
||||||
return {
|
return {
|
||||||
messages: result,
|
messages: result,
|
||||||
total: data.total_results,
|
total: data.total_results,
|
||||||
@@ -383,9 +447,12 @@ class MessageManager extends CachedManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _fetchMany(options = {}, cache) {
|
async _fetchMany(options = {}, cache) {
|
||||||
const data = await this.client.api.channels[this.channel.id].messages.get({ query: options });
|
const data = await this.client.api.channels[this.channel.id].messages.get({
|
||||||
|
query: options,
|
||||||
|
});
|
||||||
const messages = new Collection();
|
const messages = new Collection();
|
||||||
for (const message of data) messages.set(message.id, this._add(message, cache));
|
for (const message of data)
|
||||||
|
messages.set(message.id, this._add(message, cache));
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,7 +462,10 @@ class MessageManager extends CachedManager {
|
|||||||
* @returns {Promise<Message>}
|
* @returns {Promise<Message>}
|
||||||
*/
|
*/
|
||||||
async endPoll(messageId) {
|
async endPoll(messageId) {
|
||||||
const message = await this.client.api.channels(this.channel.id).polls(messageId).expire.post();
|
const message = await this.client.api
|
||||||
|
.channels(this.channel.id)
|
||||||
|
.polls(messageId)
|
||||||
|
.expire.post();
|
||||||
return this._add(message, false);
|
return this._add(message, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,11 +482,18 @@ class MessageManager extends CachedManager {
|
|||||||
* @returns {Promise<Collection<Snowflake, User>>}
|
* @returns {Promise<Collection<Snowflake, User>>}
|
||||||
*/
|
*/
|
||||||
async fetchPollAnswerVoters({ messageId, answerId, after, limit }) {
|
async fetchPollAnswerVoters({ messageId, answerId, after, limit }) {
|
||||||
const voters = await this.client.channels(this.channel.id).polls(messageId).answers(answerId).get({
|
const voters = await this.client
|
||||||
|
.channels(this.channel.id)
|
||||||
|
.polls(messageId)
|
||||||
|
.answers(answerId)
|
||||||
|
.get({
|
||||||
query: { limit, after },
|
query: { limit, after },
|
||||||
});
|
});
|
||||||
|
|
||||||
return voters.users.reduce((acc, user) => acc.set(user.id, this.client.users._add(user, false)), new Collection());
|
return voters.users.reduce(
|
||||||
|
(acc, user) => acc.set(user.id, this.client.users._add(user, false)),
|
||||||
|
new Collection(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,12 @@ class PermissionOverwriteManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async set(overwrites, reason) {
|
async set(overwrites, reason) {
|
||||||
if (!Array.isArray(overwrites) && !(overwrites instanceof Collection)) {
|
if (!Array.isArray(overwrites) && !(overwrites instanceof Collection)) {
|
||||||
throw new TypeError('INVALID_TYPE', 'overwrites', 'Array or Collection of Permission Overwrites', true);
|
throw new TypeError(
|
||||||
|
'INVALID_TYPE',
|
||||||
|
'overwrites',
|
||||||
|
'Array or Collection of Permission Overwrites',
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return this.channel.edit({ permissionOverwrites: overwrites, reason });
|
return this.channel.edit({ permissionOverwrites: overwrites, reason });
|
||||||
}
|
}
|
||||||
@@ -87,15 +92,26 @@ class PermissionOverwriteManager extends CachedManager {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
async upsert(userOrRole, options, overwriteOptions = {}, existing) {
|
async upsert(userOrRole, options, overwriteOptions = {}, existing) {
|
||||||
let userOrRoleId = this.channel.guild.roles.resolveId(userOrRole) ?? this.client.users.resolveId(userOrRole);
|
let userOrRoleId =
|
||||||
|
this.channel.guild.roles.resolveId(userOrRole) ??
|
||||||
|
this.client.users.resolveId(userOrRole);
|
||||||
let { type, reason } = overwriteOptions;
|
let { type, reason } = overwriteOptions;
|
||||||
if (typeof type !== 'number') {
|
if (typeof type !== 'number') {
|
||||||
userOrRole = this.channel.guild.roles.resolve(userOrRole) ?? this.client.users.resolve(userOrRole);
|
userOrRole =
|
||||||
if (!userOrRole) throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role');
|
this.channel.guild.roles.resolve(userOrRole) ??
|
||||||
type = userOrRole instanceof Role ? OverwriteTypes.role : OverwriteTypes.member;
|
this.client.users.resolve(userOrRole);
|
||||||
|
if (!userOrRole)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role');
|
||||||
|
type =
|
||||||
|
userOrRole instanceof Role
|
||||||
|
? OverwriteTypes.role
|
||||||
|
: OverwriteTypes.member;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { allow, deny } = PermissionOverwrites.resolveOverwriteOptions(options, existing);
|
const { allow, deny } = PermissionOverwrites.resolveOverwriteOptions(
|
||||||
|
options,
|
||||||
|
existing,
|
||||||
|
);
|
||||||
|
|
||||||
await this.client.api
|
await this.client.api
|
||||||
.channels(this.channel.id)
|
.channels(this.channel.id)
|
||||||
@@ -141,7 +157,8 @@ class PermissionOverwriteManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
edit(userOrRole, options, overwriteOptions) {
|
edit(userOrRole, options, overwriteOptions) {
|
||||||
const existing = this.cache.get(
|
const existing = this.cache.get(
|
||||||
this.channel.guild.roles.resolveId(userOrRole) ?? this.client.users.resolveId(userOrRole),
|
this.channel.guild.roles.resolveId(userOrRole) ??
|
||||||
|
this.client.users.resolveId(userOrRole),
|
||||||
);
|
);
|
||||||
return this.upsert(userOrRole, options, overwriteOptions, existing);
|
return this.upsert(userOrRole, options, overwriteOptions, existing);
|
||||||
}
|
}
|
||||||
@@ -153,10 +170,16 @@ class PermissionOverwriteManager extends CachedManager {
|
|||||||
* @returns {Promise<GuildChannel>}
|
* @returns {Promise<GuildChannel>}
|
||||||
*/
|
*/
|
||||||
async delete(userOrRole, reason) {
|
async delete(userOrRole, reason) {
|
||||||
const userOrRoleId = this.channel.guild.roles.resolveId(userOrRole) ?? this.client.users.resolveId(userOrRole);
|
const userOrRoleId =
|
||||||
if (!userOrRoleId) throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role');
|
this.channel.guild.roles.resolveId(userOrRole) ??
|
||||||
|
this.client.users.resolveId(userOrRole);
|
||||||
|
if (!userOrRoleId)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'parameter', 'User nor a Role');
|
||||||
|
|
||||||
await this.client.api.channels(this.channel.id).permissions(userOrRoleId).delete({ reason });
|
await this.client.api
|
||||||
|
.channels(this.channel.id)
|
||||||
|
.permissions(userOrRoleId)
|
||||||
|
.delete({ reason });
|
||||||
return this.channel;
|
return this.channel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class PresenceManager extends CachedManager {
|
|||||||
async fetch() {
|
async fetch() {
|
||||||
const data = await this.client.api.presences.get();
|
const data = await this.client.api.presences.get();
|
||||||
// https://docs.discord.food/resources/presence#endpoints
|
// https://docs.discord.food/resources/presence#endpoints
|
||||||
data.presences.forEach(presence => {
|
data.presences.forEach((presence) => {
|
||||||
this._add(presence, true);
|
this._add(presence, true);
|
||||||
});
|
});
|
||||||
return this.cache;
|
return this.cache;
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ class ReactionManager extends CachedManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_add(data, cache) {
|
_add(data, cache) {
|
||||||
return super._add(data, cache, { id: data.emoji.id ?? data.emoji.name, extras: [this.message] });
|
return super._add(data, cache, {
|
||||||
|
id: data.emoji.id ?? data.emoji.name,
|
||||||
|
extras: [this.message],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,7 +62,10 @@ class ReactionManager extends CachedManager {
|
|||||||
* @returns {Promise<Message>}
|
* @returns {Promise<Message>}
|
||||||
*/
|
*/
|
||||||
async removeAll() {
|
async removeAll() {
|
||||||
await this.client.api.channels(this.message.channelId).messages(this.message.id).reactions.delete();
|
await this.client.api
|
||||||
|
.channels(this.message.channelId)
|
||||||
|
.messages(this.message.id)
|
||||||
|
.reactions.delete();
|
||||||
return this.message;
|
return this.message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,9 +42,15 @@ class ReactionUserManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async fetch({ limit = 100, after, type = 'NORMAL' } = {}) {
|
async fetch({ limit = 100, after, type = 'NORMAL' } = {}) {
|
||||||
const message = this.reaction.message;
|
const message = this.reaction.message;
|
||||||
const data = await this.client.api.channels[message.channelId].messages[message.id].reactions[
|
const data = await this.client.api.channels[message.channelId].messages[
|
||||||
this.reaction.emoji.identifier
|
message.id
|
||||||
].get({ query: { limit, after, type: typeof type == 'number' ? type : ReactionTypes[type] } });
|
].reactions[this.reaction.emoji.identifier].get({
|
||||||
|
query: {
|
||||||
|
limit,
|
||||||
|
after,
|
||||||
|
type: typeof type == 'number' ? type : ReactionTypes[type],
|
||||||
|
},
|
||||||
|
});
|
||||||
const users = new Collection();
|
const users = new Collection();
|
||||||
for (const rawUser of data) {
|
for (const rawUser of data) {
|
||||||
const user = this.client.users._add(rawUser);
|
const user = this.client.users._add(rawUser);
|
||||||
@@ -63,7 +69,9 @@ class ReactionUserManager extends CachedManager {
|
|||||||
const userId = this.client.users.resolveId(user);
|
const userId = this.client.users.resolveId(user);
|
||||||
if (!userId) throw new Error('REACTION_RESOLVE_USER');
|
if (!userId) throw new Error('REACTION_RESOLVE_USER');
|
||||||
const message = this.reaction.message;
|
const message = this.reaction.message;
|
||||||
await this.client.api.channels[message.channelId].messages[message.id].reactions[this.reaction.emoji.identifier][
|
await this.client.api.channels[message.channelId].messages[
|
||||||
|
message.id
|
||||||
|
].reactions[this.reaction.emoji.identifier][
|
||||||
userId === this.client.user.id ? '@me' : userId
|
userId === this.client.user.id ? '@me' : userId
|
||||||
].delete();
|
].delete();
|
||||||
return this.reaction;
|
return this.reaction;
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class RelationshipManager extends BaseManager {
|
|||||||
*/
|
*/
|
||||||
get friendCache() {
|
get friendCache() {
|
||||||
const users = this.cache
|
const users = this.cache
|
||||||
.filter(value => value === RelationshipTypes.FRIEND)
|
.filter((value) => value === RelationshipTypes.FRIEND)
|
||||||
.map((_, key) => [key, this.client.users.cache.get(key)]);
|
.map((_, key) => [key, this.client.users.cache.get(key)]);
|
||||||
return new Collection(users);
|
return new Collection(users);
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ class RelationshipManager extends BaseManager {
|
|||||||
*/
|
*/
|
||||||
get blockedCache() {
|
get blockedCache() {
|
||||||
const users = this.cache
|
const users = this.cache
|
||||||
.filter(value => value === RelationshipTypes.BLOCKED)
|
.filter((value) => value === RelationshipTypes.BLOCKED)
|
||||||
.map((_, key) => [key, this.client.users.cache.get(key)]);
|
.map((_, key) => [key, this.client.users.cache.get(key)]);
|
||||||
return new Collection(users);
|
return new Collection(users);
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ class RelationshipManager extends BaseManager {
|
|||||||
*/
|
*/
|
||||||
get incomingCache() {
|
get incomingCache() {
|
||||||
const users = this.cache
|
const users = this.cache
|
||||||
.filter(value => value === RelationshipTypes.PENDING_INCOMING)
|
.filter((value) => value === RelationshipTypes.PENDING_INCOMING)
|
||||||
.map((_, key) => [key, this.client.users.cache.get(key)]);
|
.map((_, key) => [key, this.client.users.cache.get(key)]);
|
||||||
return new Collection(users);
|
return new Collection(users);
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ class RelationshipManager extends BaseManager {
|
|||||||
*/
|
*/
|
||||||
get outgoingCache() {
|
get outgoingCache() {
|
||||||
const users = this.cache
|
const users = this.cache
|
||||||
.filter(value => value === RelationshipTypes.PENDING_OUTGOING)
|
.filter((value) => value === RelationshipTypes.PENDING_OUTGOING)
|
||||||
.map((_, key) => [key, this.client.users.cache.get(key)]);
|
.map((_, key) => [key, this.client.users.cache.get(key)]);
|
||||||
return new Collection(users);
|
return new Collection(users);
|
||||||
}
|
}
|
||||||
@@ -172,9 +172,11 @@ class RelationshipManager extends BaseManager {
|
|||||||
// eslint-disable-next-line no-unreachable
|
// eslint-disable-next-line no-unreachable
|
||||||
const id = this.resolveId(user);
|
const id = this.resolveId(user);
|
||||||
if (
|
if (
|
||||||
![RelationshipTypes.FRIEND, RelationshipTypes.BLOCKED, RelationshipTypes.PENDING_OUTGOING].includes(
|
![
|
||||||
this.cache.get(id),
|
RelationshipTypes.FRIEND,
|
||||||
)
|
RelationshipTypes.BLOCKED,
|
||||||
|
RelationshipTypes.PENDING_OUTGOING,
|
||||||
|
].includes(this.cache.get(id))
|
||||||
) {
|
) {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
}
|
}
|
||||||
@@ -222,9 +224,11 @@ class RelationshipManager extends BaseManager {
|
|||||||
// eslint-disable-next-line no-unreachable
|
// eslint-disable-next-line no-unreachable
|
||||||
const id = this.resolveId(user);
|
const id = this.resolveId(user);
|
||||||
// Check if already friends
|
// Check if already friends
|
||||||
if (this.cache.get(id) === RelationshipTypes.FRIEND) return Promise.resolve(false);
|
if (this.cache.get(id) === RelationshipTypes.FRIEND)
|
||||||
|
return Promise.resolve(false);
|
||||||
// Check if outgoing request
|
// Check if outgoing request
|
||||||
if (this.cache.get(id) === RelationshipTypes.PENDING_OUTGOING) return Promise.resolve(false);
|
if (this.cache.get(id) === RelationshipTypes.PENDING_OUTGOING)
|
||||||
|
return Promise.resolve(false);
|
||||||
await this.client.api.users['@me'].relationships[id].put({
|
await this.client.api.users['@me'].relationships[id].put({
|
||||||
data: { confirm_stranger_request: true },
|
data: { confirm_stranger_request: true },
|
||||||
DiscordContext: { location: 'Friends' },
|
DiscordContext: { location: 'Friends' },
|
||||||
@@ -240,7 +244,8 @@ class RelationshipManager extends BaseManager {
|
|||||||
*/
|
*/
|
||||||
async setNickname(user, nickname = null) {
|
async setNickname(user, nickname = null) {
|
||||||
const id = this.resolveId(user);
|
const id = this.resolveId(user);
|
||||||
if (this.cache.get(id) !== RelationshipTypes.FRIEND) return Promise.resolve(false);
|
if (this.cache.get(id) !== RelationshipTypes.FRIEND)
|
||||||
|
return Promise.resolve(false);
|
||||||
await this.client.api.users['@me'].relationships[id].patch({
|
await this.client.api.users['@me'].relationships[id].patch({
|
||||||
data: {
|
data: {
|
||||||
nickname: typeof nickname === 'string' ? nickname : null,
|
nickname: typeof nickname === 'string' ? nickname : null,
|
||||||
@@ -264,7 +269,8 @@ class RelationshipManager extends BaseManager {
|
|||||||
// eslint-disable-next-line no-unreachable
|
// eslint-disable-next-line no-unreachable
|
||||||
const id = this.resolveId(user);
|
const id = this.resolveId(user);
|
||||||
// Check
|
// Check
|
||||||
if (this.cache.get(id) === RelationshipTypes.BLOCKED) return Promise.resolve(false);
|
if (this.cache.get(id) === RelationshipTypes.BLOCKED)
|
||||||
|
return Promise.resolve(false);
|
||||||
await this.client.api.users['@me'].relationships[id].put({
|
await this.client.api.users['@me'].relationships[id].put({
|
||||||
data: {
|
data: {
|
||||||
type: RelationshipTypes.BLOCKED,
|
type: RelationshipTypes.BLOCKED,
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class RoleManager extends CachedManager {
|
|||||||
const data = await this.client.api.guilds(this.guild.id).roles.get();
|
const data = await this.client.api.guilds(this.guild.id).roles.get();
|
||||||
const roles = new Collection();
|
const roles = new Collection();
|
||||||
for (const role of data) roles.set(role.id, this._add(role, cache));
|
for (const role of data) roles.set(role.id, this._add(role, cache));
|
||||||
return id ? roles.get(id) ?? null : roles;
|
return id ? (roles.get(id) ?? null) : roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,7 +80,10 @@ class RoleManager extends CachedManager {
|
|||||||
* @returns {Promise<Record<Snowflake, number>>}
|
* @returns {Promise<Record<Snowflake, number>>}
|
||||||
*/
|
*/
|
||||||
async fetchMemberCounts() {
|
async fetchMemberCounts() {
|
||||||
const data = await this.client.api.guilds(this.guild.id).roles('member-counts').get();
|
const data = await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
.roles('member-counts')
|
||||||
|
.get();
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -95,7 +98,10 @@ class RoleManager extends CachedManager {
|
|||||||
const id = this.resolveId(role);
|
const id = this.resolveId(role);
|
||||||
if (!id) throw new TypeError('INVALID_TYPE', 'role', 'RoleResolvable');
|
if (!id) throw new TypeError('INVALID_TYPE', 'role', 'RoleResolvable');
|
||||||
|
|
||||||
const data = await this.client.api.guilds(this.guild.id).roles(id, 'member-ids').get();
|
const data = await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
.roles(id, 'member-ids')
|
||||||
|
.get();
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -191,24 +197,34 @@ class RoleManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async create(options = {}) {
|
async create(options = {}) {
|
||||||
let { permissions, icon } = options;
|
let { permissions, icon } = options;
|
||||||
const { name, color, hoist, position, mentionable, reason, unicodeEmoji } = options;
|
const { name, color, hoist, position, mentionable, reason, unicodeEmoji } =
|
||||||
|
options;
|
||||||
|
|
||||||
if (typeof permissions !== 'undefined') permissions = new Permissions(permissions);
|
if (typeof permissions !== 'undefined')
|
||||||
|
permissions = new Permissions(permissions);
|
||||||
if (icon) {
|
if (icon) {
|
||||||
const guildEmojiURL = this.guild.emojis.resolve(icon)?.url;
|
const guildEmojiURL = this.guild.emojis.resolve(icon)?.url;
|
||||||
icon = guildEmojiURL ? await DataResolver.resolveImage(guildEmojiURL) : await DataResolver.resolveImage(icon);
|
icon = guildEmojiURL
|
||||||
|
? await DataResolver.resolveImage(guildEmojiURL)
|
||||||
|
: await DataResolver.resolveImage(icon);
|
||||||
if (typeof icon !== 'string') icon = undefined;
|
if (typeof icon !== 'string') icon = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
let colors = options.colors && {
|
let colors = options.colors && {
|
||||||
primary_color: resolveColor(options.colors.primaryColor),
|
primary_color: resolveColor(options.colors.primaryColor),
|
||||||
secondary_color: options.colors.secondaryColor && resolveColor(options.colors.secondaryColor),
|
secondary_color:
|
||||||
tertiary_color: options.colors.tertiaryColor && resolveColor(options.colors.tertiaryColor),
|
options.colors.secondaryColor &&
|
||||||
|
resolveColor(options.colors.secondaryColor),
|
||||||
|
tertiary_color:
|
||||||
|
options.colors.tertiaryColor &&
|
||||||
|
resolveColor(options.colors.tertiaryColor),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (color !== undefined) {
|
if (color !== undefined) {
|
||||||
if (!deprecationEmittedForCreate) {
|
if (!deprecationEmittedForCreate) {
|
||||||
process.emitWarning(`Passing "color" to RoleManager#create() is deprecated. Use "colors" instead.`);
|
process.emitWarning(
|
||||||
|
`Passing "color" to RoleManager#create() is deprecated. Use "colors" instead.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
deprecationEmittedForCreate = true;
|
deprecationEmittedForCreate = true;
|
||||||
@@ -256,24 +272,31 @@ class RoleManager extends CachedManager {
|
|||||||
role = this.resolve(role);
|
role = this.resolve(role);
|
||||||
if (!role) throw new TypeError('INVALID_TYPE', 'role', 'RoleResolvable');
|
if (!role) throw new TypeError('INVALID_TYPE', 'role', 'RoleResolvable');
|
||||||
|
|
||||||
if (typeof data.position === 'number') await this.setPosition(role, data.position, { reason });
|
if (typeof data.position === 'number')
|
||||||
|
await this.setPosition(role, data.position, { reason });
|
||||||
|
|
||||||
let icon = data.icon;
|
let icon = data.icon;
|
||||||
if (icon) {
|
if (icon) {
|
||||||
const guildEmojiURL = this.guild.emojis.resolve(icon)?.url;
|
const guildEmojiURL = this.guild.emojis.resolve(icon)?.url;
|
||||||
icon = guildEmojiURL ? await DataResolver.resolveImage(guildEmojiURL) : await DataResolver.resolveImage(icon);
|
icon = guildEmojiURL
|
||||||
|
? await DataResolver.resolveImage(guildEmojiURL)
|
||||||
|
: await DataResolver.resolveImage(icon);
|
||||||
if (typeof icon !== 'string') icon = undefined;
|
if (typeof icon !== 'string') icon = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
let colors = data.colors && {
|
let colors = data.colors && {
|
||||||
primary_color: resolveColor(data.colors.primaryColor),
|
primary_color: resolveColor(data.colors.primaryColor),
|
||||||
secondary_color: data.colors.secondaryColor && resolveColor(data.colors.secondaryColor),
|
secondary_color:
|
||||||
tertiary_color: data.colors.tertiaryColor && resolveColor(data.colors.tertiaryColor),
|
data.colors.secondaryColor && resolveColor(data.colors.secondaryColor),
|
||||||
|
tertiary_color:
|
||||||
|
data.colors.tertiaryColor && resolveColor(data.colors.tertiaryColor),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (data.color !== undefined) {
|
if (data.color !== undefined) {
|
||||||
if (!deprecationEmittedForEdit) {
|
if (!deprecationEmittedForEdit) {
|
||||||
process.emitWarning(`Passing "color" to RoleManager#edit() is deprecated. Use "colors" instead.`);
|
process.emitWarning(
|
||||||
|
`Passing "color" to RoleManager#edit() is deprecated. Use "colors" instead.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
deprecationEmittedForEdit = true;
|
deprecationEmittedForEdit = true;
|
||||||
@@ -289,13 +312,19 @@ class RoleManager extends CachedManager {
|
|||||||
name: data.name,
|
name: data.name,
|
||||||
colors,
|
colors,
|
||||||
hoist: data.hoist,
|
hoist: data.hoist,
|
||||||
permissions: typeof data.permissions === 'undefined' ? undefined : new Permissions(data.permissions),
|
permissions:
|
||||||
|
typeof data.permissions === 'undefined'
|
||||||
|
? undefined
|
||||||
|
: new Permissions(data.permissions),
|
||||||
mentionable: data.mentionable,
|
mentionable: data.mentionable,
|
||||||
icon,
|
icon,
|
||||||
unicode_emoji: data.unicodeEmoji,
|
unicode_emoji: data.unicodeEmoji,
|
||||||
};
|
};
|
||||||
|
|
||||||
const d = await this.client.api.guilds(this.guild.id).roles(role.id).patch({ data: _data, reason });
|
const d = await this.client.api
|
||||||
|
.guilds(this.guild.id)
|
||||||
|
.roles(role.id)
|
||||||
|
.patch({ data: _data, reason });
|
||||||
|
|
||||||
const clone = role._clone();
|
const clone = role._clone();
|
||||||
clone._patch(d);
|
clone._patch(d);
|
||||||
@@ -316,7 +345,10 @@ class RoleManager extends CachedManager {
|
|||||||
async delete(role, reason) {
|
async delete(role, reason) {
|
||||||
const id = this.resolveId(role);
|
const id = this.resolveId(role);
|
||||||
await this.client.api.guilds[this.guild.id].roles[id].delete({ reason });
|
await this.client.api.guilds[this.guild.id].roles[id].delete({ reason });
|
||||||
this.client.actions.GuildRoleDelete.handle({ guild_id: this.guild.id, role_id: id });
|
this.client.actions.GuildRoleDelete.handle({
|
||||||
|
guild_id: this.guild.id,
|
||||||
|
role_id: id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -368,7 +400,7 @@ class RoleManager extends CachedManager {
|
|||||||
*/
|
*/
|
||||||
async setPositions(rolePositions) {
|
async setPositions(rolePositions) {
|
||||||
// Make sure rolePositions are prepared for API
|
// Make sure rolePositions are prepared for API
|
||||||
rolePositions = rolePositions.map(o => ({
|
rolePositions = rolePositions.map((o) => ({
|
||||||
id: this.resolveId(o.role),
|
id: this.resolveId(o.role),
|
||||||
position: o.position,
|
position: o.position,
|
||||||
}));
|
}));
|
||||||
@@ -393,7 +425,8 @@ class RoleManager extends CachedManager {
|
|||||||
comparePositions(role1, role2) {
|
comparePositions(role1, role2) {
|
||||||
const resolvedRole1 = this.resolve(role1);
|
const resolvedRole1 = this.resolve(role1);
|
||||||
const resolvedRole2 = this.resolve(role2);
|
const resolvedRole2 = this.resolve(role2);
|
||||||
if (!resolvedRole1 || !resolvedRole2) throw new TypeError('INVALID_TYPE', 'role', 'Role nor a Snowflake');
|
if (!resolvedRole1 || !resolvedRole2)
|
||||||
|
throw new TypeError('INVALID_TYPE', 'role', 'Role nor a Snowflake');
|
||||||
|
|
||||||
const role1Position = resolvedRole1.position;
|
const role1Position = resolvedRole1.position;
|
||||||
const role2Position = resolvedRole2.position;
|
const role2Position = resolvedRole2.position;
|
||||||
@@ -414,7 +447,7 @@ class RoleManager extends CachedManager {
|
|||||||
botRoleFor(user) {
|
botRoleFor(user) {
|
||||||
const userId = this.client.users.resolveId(user);
|
const userId = this.client.users.resolveId(user);
|
||||||
if (!userId) return null;
|
if (!userId) return null;
|
||||||
return this.cache.find(role => role.tags?.botId === userId) ?? null;
|
return this.cache.find((role) => role.tags?.botId === userId) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -432,7 +465,7 @@ class RoleManager extends CachedManager {
|
|||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get premiumSubscriberRole() {
|
get premiumSubscriberRole() {
|
||||||
return this.cache.find(role => role.tags?.premiumSubscriberRole) ?? null;
|
return this.cache.find((role) => role.tags?.premiumSubscriberRole) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -441,7 +474,10 @@ class RoleManager extends CachedManager {
|
|||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get highest() {
|
get highest() {
|
||||||
return this.cache.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev), this.cache.first());
|
return this.cache.reduce(
|
||||||
|
(prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev),
|
||||||
|
this.cache.first(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class SessionManager extends CachedManager {
|
|||||||
* @returns {Promise<Collection<string, Session>>}
|
* @returns {Promise<Collection<string, Session>>}
|
||||||
*/
|
*/
|
||||||
fetch() {
|
fetch() {
|
||||||
return this.client.api.auth.sessions.get().then(data => {
|
return this.client.api.auth.sessions.get().then((data) => {
|
||||||
const allData = data.user_sessions;
|
const allData = data.user_sessions;
|
||||||
this.cache.clear();
|
this.cache.clear();
|
||||||
for (const session of allData) {
|
for (const session of allData) {
|
||||||
@@ -45,7 +45,7 @@ class SessionManager extends CachedManager {
|
|||||||
logoutAllDevices() {
|
logoutAllDevices() {
|
||||||
return this.client.api.auth.sessions.logout({
|
return this.client.api.auth.sessions.logout({
|
||||||
data: {
|
data: {
|
||||||
session_id_hashes: this.cache.map(session => session.id),
|
session_id_hashes: this.cache.map((session) => session.id),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,11 +60,17 @@ class StageInstanceManager extends CachedManager {
|
|||||||
async create(channel, options) {
|
async create(channel, options) {
|
||||||
const channelId = this.guild.channels.resolveId(channel);
|
const channelId = this.guild.channels.resolveId(channel);
|
||||||
if (!channelId) throw new Error('STAGE_CHANNEL_RESOLVE');
|
if (!channelId) throw new Error('STAGE_CHANNEL_RESOLVE');
|
||||||
if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true);
|
if (typeof options !== 'object')
|
||||||
let { guildScheduledEvent, topic, privacyLevel, sendStartNotification } = options;
|
throw new TypeError('INVALID_TYPE', 'options', 'object', true);
|
||||||
|
let { guildScheduledEvent, topic, privacyLevel, sendStartNotification } =
|
||||||
|
options;
|
||||||
|
|
||||||
privacyLevel &&= typeof privacyLevel === 'number' ? privacyLevel : PrivacyLevels[privacyLevel];
|
privacyLevel &&=
|
||||||
const guildScheduledEventId = guildScheduledEvent && this.resolveId(guildScheduledEvent);
|
typeof privacyLevel === 'number'
|
||||||
|
? privacyLevel
|
||||||
|
: PrivacyLevels[privacyLevel];
|
||||||
|
const guildScheduledEventId =
|
||||||
|
guildScheduledEvent && this.resolveId(guildScheduledEvent);
|
||||||
|
|
||||||
const data = await this.client.api['stage-instances'].post({
|
const data = await this.client.api['stage-instances'].post({
|
||||||
data: {
|
data: {
|
||||||
@@ -95,7 +101,9 @@ class StageInstanceManager extends CachedManager {
|
|||||||
if (!channelId) throw new Error('STAGE_CHANNEL_RESOLVE');
|
if (!channelId) throw new Error('STAGE_CHANNEL_RESOLVE');
|
||||||
|
|
||||||
if (!force) {
|
if (!force) {
|
||||||
const existing = this.cache.find(stageInstance => stageInstance.channelId === channelId);
|
const existing = this.cache.find(
|
||||||
|
(stageInstance) => stageInstance.channelId === channelId,
|
||||||
|
);
|
||||||
if (existing) return existing;
|
if (existing) return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,13 +130,17 @@ class StageInstanceManager extends CachedManager {
|
|||||||
* .catch(console.error);
|
* .catch(console.error);
|
||||||
*/
|
*/
|
||||||
async edit(channel, options) {
|
async edit(channel, options) {
|
||||||
if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true);
|
if (typeof options !== 'object')
|
||||||
|
throw new TypeError('INVALID_TYPE', 'options', 'object', true);
|
||||||
const channelId = this.guild.channels.resolveId(channel);
|
const channelId = this.guild.channels.resolveId(channel);
|
||||||
if (!channelId) throw new Error('STAGE_CHANNEL_RESOLVE');
|
if (!channelId) throw new Error('STAGE_CHANNEL_RESOLVE');
|
||||||
|
|
||||||
let { topic, privacyLevel } = options;
|
let { topic, privacyLevel } = options;
|
||||||
|
|
||||||
privacyLevel &&= typeof privacyLevel === 'number' ? privacyLevel : PrivacyLevels[privacyLevel];
|
privacyLevel &&=
|
||||||
|
typeof privacyLevel === 'number'
|
||||||
|
? privacyLevel
|
||||||
|
: PrivacyLevels[privacyLevel];
|
||||||
|
|
||||||
const data = await this.client.api('stage-instances', channelId).patch({
|
const data = await this.client.api('stage-instances', channelId).patch({
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -139,7 +139,9 @@ class ThreadManager extends CachedManager {
|
|||||||
* @returns {Promise<FetchedThreads>}
|
* @returns {Promise<FetchedThreads>}
|
||||||
*/
|
*/
|
||||||
async fetchActive(cache = true, options = {}) {
|
async fetchActive(cache = true, options = {}) {
|
||||||
const raw = await this.client.api.channels(this.channel.id).threads.search.get({
|
const raw = await this.client.api
|
||||||
|
.channels(this.channel.id)
|
||||||
|
.threads.search.get({
|
||||||
query: {
|
query: {
|
||||||
archived: options?.archived ?? false,
|
archived: options?.archived ?? false,
|
||||||
limit: options?.limit ?? 25,
|
limit: options?.limit ?? 25,
|
||||||
@@ -149,17 +151,23 @@ class ThreadManager extends CachedManager {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.constructor._mapThreads(raw, this.client, { parent: this.channel, cache });
|
return this.constructor._mapThreads(raw, this.client, {
|
||||||
|
parent: this.channel,
|
||||||
|
cache,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static _mapThreads(rawThreads, client, { parent, guild, cache }) {
|
static _mapThreads(rawThreads, client, { parent, guild, cache }) {
|
||||||
const threads = rawThreads.threads.reduce((coll, raw) => {
|
const threads = rawThreads.threads.reduce((coll, raw) => {
|
||||||
const thread = client.channels._add(raw, guild ?? parent?.guild, { cache });
|
const thread = client.channels._add(raw, guild ?? parent?.guild, {
|
||||||
|
cache,
|
||||||
|
});
|
||||||
if (parent && thread.parentId !== parent.id) return coll;
|
if (parent && thread.parentId !== parent.id) return coll;
|
||||||
return coll.set(thread.id, thread);
|
return coll.set(thread.id, thread);
|
||||||
}, new Collection());
|
}, new Collection());
|
||||||
// Discord sends the thread id as id in this object
|
// Discord sends the thread id as id in this object
|
||||||
for (const rawMember of rawThreads.members) client.channels.cache.get(rawMember.id)?.members._add(rawMember);
|
for (const rawMember of rawThreads.members)
|
||||||
|
client.channels.cache.get(rawMember.id)?.members._add(rawMember);
|
||||||
// Patch firstMessage
|
// Patch firstMessage
|
||||||
// According to https://github.com/aiko-chan-ai/discord.js-selfbot-v13/issues/1502, rawThreads.first_messages could be null.
|
// According to https://github.com/aiko-chan-ai/discord.js-selfbot-v13/issues/1502, rawThreads.first_messages could be null.
|
||||||
for (const rawMessage of rawThreads?.first_messages || []) {
|
for (const rawMessage of rawThreads?.first_messages || []) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user