copy eslint + tslint from Discord.js (v13)

This commit is contained in:
March 7th
2022-04-16 17:44:43 +07:00
parent 7fc069f4b9
commit 522f7f756a
64 changed files with 9164 additions and 9003 deletions

3
.eslintignore Normal file
View File

@@ -0,0 +1,3 @@
{
"ignorePatterns": ["node_modules/*"],
}

285
.eslintrc.json Normal file
View File

@@ -0,0 +1,285 @@
{
"extends": [
"eslint:recommended",
"plugin:prettier/recommended"
],
"plugins": [
"import"
],
"parserOptions": {
"ecmaVersion": 2021
},
"env": {
"es2021": true,
"node": true
},
"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": "error",
"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": "warn",
"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": [
"error",
120,
2
],
"max-nested-callbacks": [
"error",
{
"max": 4
}
],
"max-statements-per-line": [
"error",
{
"max": 2
}
],
"new-cap": "off",
"newline-per-chained-call": [
"error",
{
"ignoreChainWithDepth": 3
}
],
"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"
}
]
}
}

8
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
time: "12:00"
open-pull-requests-limit: 15

24
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Lint
on:
push:
branches:
- '*'
pull_request:
branches:
- '*'
jobs:
eslint:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run lint:all

7
.prettierrc.json Normal file
View File

@@ -0,0 +1,7 @@
{
"singleQuote": true,
"printWidth": 120,
"trailingComma": "all",
"endOfLine": "lf",
"arrowParens": "avoid"
}

View File

@@ -5,7 +5,12 @@
"main": "./src/index.js", "main": "./src/index.js",
"types": "./typings/index.d.ts", "types": "./typings/index.d.ts",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"lint:typings": "tslint typings/index.d.ts",
"format": "prettier --write src/**/*.js typings/**/*.ts",
"lint:all": "npm run lint && npm run lint:typings"
}, },
"files": [ "files": [
"src", "src",

View File

@@ -126,7 +126,7 @@ class Client extends BaseClient {
this.users = new UserManager(this); this.users = new UserManager(this);
/** Patch /** Patch
* *
*/ */
this.relationships = new RelationshipsManager(this); this.relationships = new RelationshipsManager(this);
this.setting = new ClientUserSettingManager(this); this.setting = new ClientUserSettingManager(this);
@@ -244,44 +244,41 @@ class Client extends BaseClient {
* Update Cloudflare Cookie and Discord Fingerprint * Update Cloudflare Cookie and Discord Fingerprint
*/ */
async updateCookie() { async updateCookie() {
/* Auto find fingerprint and add Cookie */ /* Auto find fingerprint and add Cookie */
let cookie = ''; let cookie = '';
await require('axios')({ await require('axios')({
method: 'get', method: 'get',
url: 'https://discord.com/api/v9/experiments', url: 'https://discord.com/api/v9/experiments',
headers: this.options.http.headers, headers: this.options.http.headers,
}) })
.then((res) => { .then(res => {
if (!'set-cookie' in res.headers) return; if (!'set-cookie' in res.headers) return;
res.headers['set-cookie'].map((line) => { res.headers['set-cookie'].map(line => {
line.split('; ').map((arr) => { line.split('; ').map(arr => {
if ( if (
arr.startsWith('Expires') || arr.startsWith('Expires') ||
arr.startsWith('Path') || arr.startsWith('Path') ||
arr.startsWith('Domain') || arr.startsWith('Domain') ||
arr.startsWith('HttpOnly') || arr.startsWith('HttpOnly') ||
arr.startsWith('Secure') || arr.startsWith('Secure') ||
arr.startsWith('Max-Age') || arr.startsWith('Max-Age') ||
arr.startsWith('SameSite') arr.startsWith('SameSite')
) { ) {
return; return;
} else { } else {
cookie += `${arr}; `; cookie += `${arr}; `;
} }
}); });
}); });
this.options.http.headers['Cookie'] = `${cookie}locale=en`; this.options.http.headers['Cookie'] = `${cookie}locale=en`;
this.options.http.headers['x-fingerprint'] = res.data.fingerprint; this.options.http.headers['x-fingerprint'] = res.data.fingerprint;
this.emit(Events.DEBUG, `Added Cookie: ${cookie}`); this.emit(Events.DEBUG, `Added Cookie: ${cookie}`);
this.emit(Events.DEBUG, `Added Fingerprint: ${res.data.fingerprint}`); this.emit(Events.DEBUG, `Added Fingerprint: ${res.data.fingerprint}`);
}) })
.catch((err) => { .catch(err => {
this.emit( this.emit(Events.DEBUG, `Finding Cookie and Fingerprint failed: ${err.message}`);
Events.DEBUG, });
`Finding Cookie and Fingerprint failed: ${err.message}`, }
);
});
}
/** /**
* Logs the client in, establishing a WebSocket connection to Discord. * Logs the client in, establishing a WebSocket connection to Discord.

View File

@@ -60,7 +60,10 @@ class InteractionCreateAction extends Action {
InteractionType = AutocompleteInteraction; InteractionType = AutocompleteInteraction;
break; break;
default: default:
client.emit(Events.DEBUG, `[INTERACTION] Received [BOT] / Send (Selfbot) interactionID ${data.id} with unknown type: ${data.type}`); client.emit(
Events.DEBUG,
`[INTERACTION] Received [BOT] / Send (Selfbot) interactionID ${data.id} with unknown type: ${data.type}`,
);
return; return;
} }

View File

@@ -217,7 +217,7 @@ class WebSocketManager extends EventEmitter {
this.shardQueue.add(shard); this.shardQueue.add(shard);
if (shard.sessionId) { if (shard.sessionId) {
this.debug(`Session id is present, attempting an immediate reconnect...`, shard); this.debug('Session id is present, attempting an immediate reconnect...', shard);
this.reconnect(); this.reconnect();
} else { } else {
shard.destroy({ reset: true, emit: false, log: false }); shard.destroy({ reset: true, emit: false, log: false });
@@ -279,7 +279,7 @@ class WebSocketManager extends EventEmitter {
} 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);
this.reconnecting = false; this.reconnecting = false;
return this.reconnect(); return this.reconnect();

View File

@@ -13,7 +13,7 @@ let zlib;
try { try {
zlib = require('zlib-sync'); zlib = require('zlib-sync');
} catch { } // eslint-disable-line no-empty } catch {} // eslint-disable-line no-empty
/** /**
* Represents a Shard's WebSocket connection * Represents a Shard's WebSocket connection
@@ -487,8 +487,9 @@ class WebSocketShard extends EventEmitter {
() => { () => {
this.debug( this.debug(
`Shard ${hasGuildsIntent ? 'did' : 'will'} not receive any more guild packets` + `Shard ${hasGuildsIntent ? 'did' : 'will'} not receive any more guild packets` +
`${hasGuildsIntent ? ` in ${waitGuildTimeout} ms` : ''}.\nUnavailable guild count: ${this.expectedGuilds.size `${hasGuildsIntent ? ` in ${waitGuildTimeout} ms` : ''}.\nUnavailable guild count: ${
}`, this.expectedGuilds.size
}`,
); );
this.readyTimeout = null; this.readyTimeout = null;

View File

@@ -1,10 +1,10 @@
'use strict'; 'use strict';
module.exports = (client, { d: data }) => { module.exports = (client, { d: data }) => {
if (!data.application_commands[0]) return; if (!data.application_commands[0]) return;
for (const command of data.application_commands) { for (const command of data.application_commands) {
const user = client.users.cache.get(command.application_id); const user = client.users.cache.get(command.application_id);
if (!user) continue; if (!user) continue;
user.applications._add(command, true); user.applications._add(command, true);
}; }
}; };

View File

@@ -4,32 +4,34 @@ const { Collection } = require('@discordjs/collection');
const { Events } = require('../../../util/Constants'); const { Events } = require('../../../util/Constants');
module.exports = (client, { d: data }) => { module.exports = (client, { d: data }) => {
// console.log(data); // console.log(data);
// console.log(data.ops[0]) // console.log(data.ops[0])
const guild = client.guilds.cache.get(data.guild_id); const guild = client.guilds.cache.get(data.guild_id);
if (!guild) return; if (!guild) return;
const members = new Collection(); const members = new Collection();
// Get Member from side Discord Channel (online counting if large server) // Get Member from side Discord Channel (online counting if large server)
for (const object of data.ops) { for (const object of data.ops) {
if (object.op == 'SYNC') { if (object.op == 'SYNC') {
for (const member_ of object.items) { for (const member_ of object.items) {
const member = member_.member; const member = member_.member;
if (!member) continue; if (!member) continue;
members.set(member.user.id, guild.members._add(member)); members.set(member.user.id, guild.members._add(member));
if (member.presence) if (member.presence) {
guild.presences._add(Object.assign(member.presence, { guild })); guild.presences._add(Object.assign(member.presence, { guild }));
}
} else if (object.op == 'INVALIDATE') {
console.warn(`Invalidate [${object.range[0]}, ${object.range[1]}]`);
} else if (object.op == 'UPDATE' || object.op == 'INSERT') {
const member = object.item.member;
if (!member) continue;
members.set(member.user.id, guild.members._add(member));
if (member.presence)
guild.presences._add(Object.assign(member.presence, { guild }));
} else if (object.op == 'DELETE') {
// nothing;
} }
}
} else if (object.op == 'INVALIDATE') {
console.warn(`Invalidate [${object.range[0]}, ${object.range[1]}]`);
} else if (object.op == 'UPDATE' || object.op == 'INSERT') {
const member = object.item.member;
if (!member) continue;
members.set(member.user.id, guild.members._add(member));
if (member.presence) {
guild.presences._add(Object.assign(member.presence, { guild }));
}
} else if (object.op == 'DELETE') {
// nothing;
} }
client.emit(Events.GUILD_MEMBER_LIST_UPDATE, members, guild, data); }
client.emit(Events.GUILD_MEMBER_LIST_UPDATE, members, guild, data);
}; };

View File

@@ -2,6 +2,6 @@
const { Events } = require('../../../util/Constants'); const { Events } = require('../../../util/Constants');
module.exports = (client, packet) => { module.exports = (client, packet) => {
if (client.user.bot) client.actions.InteractionCreate.handle(packet.d); if (client.user.bot) client.actions.InteractionCreate.handle(packet.d);
else client.emit(Events.INTERACTION_CREATE, packet.d); else client.emit(Events.INTERACTION_CREATE, packet.d);
}; };

View File

@@ -2,5 +2,5 @@
const { Events } = require('../../../util/Constants'); const { Events } = require('../../../util/Constants');
module.exports = (client, { d: data }) => { module.exports = (client, { d: data }) => {
client.emit(Events.INTERACTION_FAILED, data); client.emit(Events.INTERACTION_FAILED, data);
}; };

View File

@@ -2,5 +2,5 @@
const { Events } = require('../../../util/Constants'); const { Events } = require('../../../util/Constants');
module.exports = (client, { d: data }) => { module.exports = (client, { d: data }) => {
client.emit(Events.INTERACTION_SUCCESS, data); client.emit(Events.INTERACTION_SUCCESS, data);
}; };

View File

@@ -1,5 +1,5 @@
'use strict'; 'use strict';
module.exports = (client, { d: data }) => { module.exports = (client, { d: data }) => {
// client.user.messageMentions.delete(data.channel_id); // client.user.messageMentions.delete(data.channel_id);
}; };

View File

@@ -5,112 +5,99 @@ const chalk = require('chalk');
const axios = require('axios'); const axios = require('axios');
const Discord = require('../../../index'); const Discord = require('../../../index');
const RichPresence = require('discord-rpc-contructor'); const RichPresence = require('discord-rpc-contructor');
const { ChannelTypes } = require('../../../util/Constants');
const checkUpdate = async () => { const checkUpdate = async () => {
const res_ = await axios.get( const res_ = await axios.get(`https://registry.npmjs.com/${encodeURIComponent('discord.js-selfbot-v13')}`);
`https://registry.npmjs.com/${encodeURIComponent( const lastest_tag = res_.data['dist-tags'].latest;
'discord.js-selfbot-v13', // Checking if the package is outdated
)}`, // Stable version
); if (lastest_tag !== Discord.version && Discord.version.includes('-') == false) {
const lastest_tag = res_.data['dist-tags'].latest; return console.log(`${chalk.yellowBright('[WARNING]')} New Discord.js-selfbot-v13 version.
// Checking if the package is outdated Old Version: ${chalk.redBright(Discord.version)} => New Version: ${chalk.greenBright(lastest_tag)}`);
// Stable version }
if (lastest_tag !== Discord.version && Discord.version.includes('-') == false) { return console.log(
return console.log(`${chalk.yellowBright( `${chalk.greenBright('[OK]')} Discord.js-selfbot-v13 is up to date. Version: ${chalk.blueBright(Discord.version)}`,
'[WARNING]', );
)} New Discord.js-selfbot-v13 version.
Old Version: ${chalk.redBright(
Discord.version,
)} => New Version: ${chalk.greenBright(lastest_tag)}`);
}
return console.log(
`${chalk.greenBright(
'[OK]',
)} Discord.js-selfbot-v13 is up to date. Version: ${chalk.blueBright(
Discord.version,
)}`,
);
}; };
const customStatusAuto = async (client) => { const customStatusAuto = async client => {
let custom_status; let custom_status;
if ( if (client.setting.rawSetting.custom_status?.text || res.rawSetting.custom_status?.emoji_name) {
client.setting.rawSetting.custom_status?.text || custom_status = new RichPresence.CustomStatus();
res.rawSetting.custom_status?.emoji_name if (client.setting.rawSetting.custom_status.emoji_id) {
) { const emoji = await client.emojis.resolve(client.setting.rawSetting.custom_status.emoji_id);
custom_status = new RichPresence.CustomStatus(); if (emoji) custom_status.setDiscordEmoji(emoji);
if (client.setting.rawSetting.custom_status.emoji_id) { } else {
const emoji = await client.emojis.resolve( custom_status.setUnicodeEmoji(client.setting.rawSetting.custom_status.emoji_name);
client.setting.rawSetting.custom_status.emoji_id, }
); custom_status.setState(client.setting.rawSetting.custom_status?.text);
if (emoji) custom_status.setDiscordEmoji(emoji); client.user.setPresence({
} else { activities: custom_status ? [custom_status.toDiscord()] : [],
custom_status.setUnicodeEmoji( status: client.setting.rawSetting.status,
client.setting.rawSetting.custom_status.emoji_name, });
); }
} };
custom_status.setState(client.setting.rawSetting.custom_status?.text);
client.user.setPresence({
activities: custom_status ? [custom_status.toDiscord()] : [],
status: client.setting.rawSetting.status,
});
}
}
module.exports = (client, { d: data }, shard) => { module.exports = (client, { d: data }, shard) => {
if (client.options.checkUpdate) { console.log(data.private_channels);
try { if (client.options.checkUpdate) {
checkUpdate(); try {
} catch (e) { checkUpdate();
console.log(e); } catch (e) {
} console.log(e);
} }
client.session_id = data.session_id; }
if (client.user) { client.session_id = data.session_id;
client.user._patch(data.user); if (client.user) {
} else { client.user._patch(data.user);
ClientUser ??= require('../../../structures/ClientUser'); } else {
client.user = new ClientUser(client, data.user); ClientUser ??= require('../../../structures/ClientUser');
client.users.cache.set(client.user.id, client.user); client.user = new ClientUser(client, data.user);
} client.users.cache.set(client.user.id, client.user);
}
client.user.setAFK(false); client.user.setAFK(false);
client.setting._patch(data.user_settings); client.setting._patch(data.user_settings);
client.user.connectedAccounts = data.connected_accounts ?? []; client.user.connectedAccounts = data.connected_accounts ?? [];
for (const [userid, note] of Object.entries(data.notes)) { for (const [userid, note] of Object.entries(data.notes)) {
client.user.notes.set(userid, note); client.user.notes.set(userid, note);
} }
if (client.options.readyStatus) { for (const private_channel of data.private_channels) {
customStatusAuto(client); client.channels._add(private_channel);
} }
/** if (client.options.readyStatus) {
* read_state: Return Array: customStatusAuto(client);
* { }
* mention_count: 14, // ok it's ping count
* last_pin_timestamp: '1970-01-01T00:00:00+00:00', // why discord ?
* last_message_id: 0, // :)
* id: '840218426969817119' // channel id
* },
*/
/* /**
* read_state: Return Array:
* {
* mention_count: 14, // ok it's ping count
* last_pin_timestamp: '1970-01-01T00:00:00+00:00', // why discord ?
* last_message_id: 0, // :)
* id: '840218426969817119' // channel id
* },
*/
/*
for (const object of data.read_state) { for (const object of data.read_state) {
if (object.mention_count == 0) continue; if (object.mention_count == 0) continue;
client.user.messageMentions.set(object.id, object); client.user.messageMentions.set(object.id, object);
} }
*/ */
for (const guild of data.guilds) { for (const guild of data.guilds) {
guild.shardId = shard.id; guild.shardId = shard.id;
client.guilds._add(guild); client.guilds._add(guild);
} }
client.relationships._setup(data.relationships); client.relationships._setup(data.relationships);
shard.checkReady(); shard.checkReady();
}; };

View File

@@ -3,13 +3,13 @@
const { Events } = require('../../../util/Constants'); const { Events } = require('../../../util/Constants');
module.exports = (client, { d: data }) => { module.exports = (client, { d: data }) => {
data.user ? client.users._add(data.user) : null; data.user ? client.users._add(data.user) : null;
client.relationships.cache.set(data.id, data.type); client.relationships.cache.set(data.id, data.type);
/** /**
* Emitted whenever a relationship is updated. * Emitted whenever a relationship is updated.
* @event Client#relationshipUpdate * @event Client#relationshipUpdate
* @param {UserID} user The userID that was updated * @param {UserID} user The userID that was updated
* @param {Number} type The new relationship type * @param {Number} type The new relationship type
*/ */
client.emit(Events.RELATIONSHIP_ADD, data.id, data.type); client.emit(Events.RELATIONSHIP_ADD, data.id, data.type);
}; };

View File

@@ -3,11 +3,11 @@
const { Events } = require('../../../util/Constants'); const { Events } = require('../../../util/Constants');
module.exports = (client, { d: data }) => { module.exports = (client, { d: data }) => {
client.relationships.cache.delete(data.id); client.relationships.cache.delete(data.id);
/** /**
* Emitted whenever a relationship is updated. * Emitted whenever a relationship is updated.
* @event Client#relationshipUpdate * @event Client#relationshipUpdate
* @param {UserID} user The userID that was updated * @param {UserID} user The userID that was updated
*/ */
client.emit(Events.RELATIONSHIP_REMOVE, data.id); client.emit(Events.RELATIONSHIP_REMOVE, data.id);
}; };

View File

@@ -1,5 +1,5 @@
'use strict'; 'use strict';
module.exports = (client, { d: data }) => { module.exports = (client, { d: data }) => {
client.user.notes.set(data.id, data.note); client.user.notes.set(data.id, data.note);
}; };

View File

@@ -1,5 +1,5 @@
'use strict'; 'use strict';
module.exports = (client, { d: data }) => { module.exports = (client, { d: data }) => {
client.setting._patch(data); client.setting._patch(data);
}; };

View File

@@ -1,80 +1,71 @@
'use strict'; 'use strict';
const handlers = Object.fromEntries([ const handlers = Object.fromEntries([
['READY', require('./READY')], ['READY', require('./READY')],
['RESUMED', require('./RESUMED')], ['RESUMED', require('./RESUMED')],
['RELATIONSHIP_ADD', require('./RELATIONSHIP_ADD')], ['RELATIONSHIP_ADD', require('./RELATIONSHIP_ADD')],
['RELATIONSHIP_REMOVE', require('./RELATIONSHIP_REMOVE')], ['RELATIONSHIP_REMOVE', require('./RELATIONSHIP_REMOVE')],
['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')],
['GUILD_CREATE', require('./GUILD_CREATE')], ['GUILD_CREATE', require('./GUILD_CREATE')],
['GUILD_DELETE', require('./GUILD_DELETE')], ['GUILD_DELETE', require('./GUILD_DELETE')],
['GUILD_UPDATE', require('./GUILD_UPDATE')], ['GUILD_UPDATE', require('./GUILD_UPDATE')],
['INVITE_CREATE', require('./INVITE_CREATE')], ['INVITE_CREATE', require('./INVITE_CREATE')],
['INVITE_DELETE', require('./INVITE_DELETE')], ['INVITE_DELETE', require('./INVITE_DELETE')],
['GUILD_MEMBER_ADD', require('./GUILD_MEMBER_ADD')], ['GUILD_MEMBER_ADD', require('./GUILD_MEMBER_ADD')],
['GUILD_MEMBER_REMOVE', require('./GUILD_MEMBER_REMOVE')], ['GUILD_MEMBER_REMOVE', require('./GUILD_MEMBER_REMOVE')],
['GUILD_MEMBER_UPDATE', require('./GUILD_MEMBER_UPDATE')], ['GUILD_MEMBER_UPDATE', require('./GUILD_MEMBER_UPDATE')],
['GUILD_MEMBERS_CHUNK', require('./GUILD_MEMBERS_CHUNK')], ['GUILD_MEMBERS_CHUNK', require('./GUILD_MEMBERS_CHUNK')],
['GUILD_MEMBER_LIST_UPDATE', require('./GUILD_MEMBER_LIST_UPDATE.js')], ['GUILD_MEMBER_LIST_UPDATE', require('./GUILD_MEMBER_LIST_UPDATE.js')],
[ ['GUILD_APPLICATION_COMMANDS_UPDATE', require('./GUILD_APPLICATION_COMMANDS_UPDATE.js')],
'GUILD_APPLICATION_COMMANDS_UPDATE', ['GUILD_INTEGRATIONS_UPDATE', require('./GUILD_INTEGRATIONS_UPDATE')],
require('./GUILD_APPLICATION_COMMANDS_UPDATE.js'), ['GUILD_ROLE_CREATE', require('./GUILD_ROLE_CREATE')],
], ['GUILD_ROLE_DELETE', require('./GUILD_ROLE_DELETE')],
['GUILD_INTEGRATIONS_UPDATE', require('./GUILD_INTEGRATIONS_UPDATE')], ['GUILD_ROLE_UPDATE', require('./GUILD_ROLE_UPDATE')],
['GUILD_ROLE_CREATE', require('./GUILD_ROLE_CREATE')], ['GUILD_BAN_ADD', require('./GUILD_BAN_ADD')],
['GUILD_ROLE_DELETE', require('./GUILD_ROLE_DELETE')], ['GUILD_BAN_REMOVE', require('./GUILD_BAN_REMOVE')],
['GUILD_ROLE_UPDATE', require('./GUILD_ROLE_UPDATE')], ['GUILD_EMOJIS_UPDATE', require('./GUILD_EMOJIS_UPDATE')],
['GUILD_BAN_ADD', require('./GUILD_BAN_ADD')], ['CHANNEL_CREATE', require('./CHANNEL_CREATE')],
['GUILD_BAN_REMOVE', require('./GUILD_BAN_REMOVE')], ['CHANNEL_DELETE', require('./CHANNEL_DELETE')],
['GUILD_EMOJIS_UPDATE', require('./GUILD_EMOJIS_UPDATE')], ['CHANNEL_UPDATE', require('./CHANNEL_UPDATE')],
['CHANNEL_CREATE', require('./CHANNEL_CREATE')], ['CHANNEL_PINS_UPDATE', require('./CHANNEL_PINS_UPDATE')],
['CHANNEL_DELETE', require('./CHANNEL_DELETE')], ['MESSAGE_CREATE', require('./MESSAGE_CREATE')],
['CHANNEL_UPDATE', require('./CHANNEL_UPDATE')], ['MESSAGE_DELETE', require('./MESSAGE_DELETE')],
['CHANNEL_PINS_UPDATE', require('./CHANNEL_PINS_UPDATE')], ['MESSAGE_UPDATE', require('./MESSAGE_UPDATE')],
['MESSAGE_CREATE', require('./MESSAGE_CREATE')], ['MESSAGE_DELETE_BULK', require('./MESSAGE_DELETE_BULK')],
['MESSAGE_DELETE', require('./MESSAGE_DELETE')], ['MESSAGE_REACTION_ADD', require('./MESSAGE_REACTION_ADD')],
['MESSAGE_UPDATE', require('./MESSAGE_UPDATE')], ['MESSAGE_REACTION_REMOVE', require('./MESSAGE_REACTION_REMOVE')],
['MESSAGE_DELETE_BULK', require('./MESSAGE_DELETE_BULK')], ['MESSAGE_REACTION_REMOVE_ALL', require('./MESSAGE_REACTION_REMOVE_ALL')],
['MESSAGE_REACTION_ADD', require('./MESSAGE_REACTION_ADD')], ['MESSAGE_REACTION_REMOVE_EMOJI', require('./MESSAGE_REACTION_REMOVE_EMOJI')],
['MESSAGE_REACTION_REMOVE', require('./MESSAGE_REACTION_REMOVE')], ['THREAD_CREATE', require('./THREAD_CREATE')],
['MESSAGE_REACTION_REMOVE_ALL', require('./MESSAGE_REACTION_REMOVE_ALL')], ['THREAD_UPDATE', require('./THREAD_UPDATE')],
['MESSAGE_REACTION_REMOVE_EMOJI', require('./MESSAGE_REACTION_REMOVE_EMOJI')], ['THREAD_DELETE', require('./THREAD_DELETE')],
['THREAD_CREATE', require('./THREAD_CREATE')], ['THREAD_LIST_SYNC', require('./THREAD_LIST_SYNC')],
['THREAD_UPDATE', require('./THREAD_UPDATE')], ['THREAD_MEMBER_UPDATE', require('./THREAD_MEMBER_UPDATE')],
['THREAD_DELETE', require('./THREAD_DELETE')], ['THREAD_MEMBERS_UPDATE', require('./THREAD_MEMBERS_UPDATE')],
['THREAD_LIST_SYNC', require('./THREAD_LIST_SYNC')], ['USER_SETTINGS_UPDATE', require('./USER_SETTINGS_UPDATE')], // Opcode 0
['THREAD_MEMBER_UPDATE', require('./THREAD_MEMBER_UPDATE')], // USER_SETTINGS_PROTO_UPDATE // opcode 0
['THREAD_MEMBERS_UPDATE', require('./THREAD_MEMBERS_UPDATE')], ['MESSAGE_ACK', require('./MESSAGE_ACK')],
['USER_SETTINGS_UPDATE', require('./USER_SETTINGS_UPDATE')], // opcode 0 ['USER_NOTE_UPDATE', require('./USER_NOTE_UPDATE')],
// USER_SETTINGS_PROTO_UPDATE // opcode 0 ['USER_UPDATE', require('./USER_UPDATE')],
['MESSAGE_ACK', require('./MESSAGE_ACK')], ['PRESENCE_UPDATE', require('./PRESENCE_UPDATE')],
['USER_NOTE_UPDATE', require('./USER_NOTE_UPDATE')], ['TYPING_START', require('./TYPING_START')],
['USER_UPDATE', require('./USER_UPDATE')], ['VOICE_STATE_UPDATE', require('./VOICE_STATE_UPDATE')],
['PRESENCE_UPDATE', require('./PRESENCE_UPDATE')], ['VOICE_SERVER_UPDATE', require('./VOICE_SERVER_UPDATE')],
['TYPING_START', require('./TYPING_START')], ['WEBHOOKS_UPDATE', require('./WEBHOOKS_UPDATE')],
['VOICE_STATE_UPDATE', require('./VOICE_STATE_UPDATE')], ['INTERACTION_CREATE', require('./INTERACTION_CREATE')],
['VOICE_SERVER_UPDATE', require('./VOICE_SERVER_UPDATE')], ['INTERACTION_SUCCESS', require('./INTERACTION_SUCCESS')],
['WEBHOOKS_UPDATE', require('./WEBHOOKS_UPDATE')], ['INTERACTION_FAILED', require('./INTERACTION_FAILED')],
['INTERACTION_CREATE', require('./INTERACTION_CREATE')], ['STAGE_INSTANCE_CREATE', require('./STAGE_INSTANCE_CREATE')],
['INTERACTION_SUCCESS', require('./INTERACTION_SUCCESS')], ['STAGE_INSTANCE_UPDATE', require('./STAGE_INSTANCE_UPDATE')],
['INTERACTION_FAILED', require('./INTERACTION_FAILED')], ['STAGE_INSTANCE_DELETE', require('./STAGE_INSTANCE_DELETE')],
['STAGE_INSTANCE_CREATE', require('./STAGE_INSTANCE_CREATE')], ['GUILD_STICKERS_UPDATE', require('./GUILD_STICKERS_UPDATE')],
['STAGE_INSTANCE_UPDATE', require('./STAGE_INSTANCE_UPDATE')], ['GUILD_SCHEDULED_EVENT_CREATE', require('./GUILD_SCHEDULED_EVENT_CREATE')],
['STAGE_INSTANCE_DELETE', require('./STAGE_INSTANCE_DELETE')], ['GUILD_SCHEDULED_EVENT_UPDATE', require('./GUILD_SCHEDULED_EVENT_UPDATE')],
['GUILD_STICKERS_UPDATE', require('./GUILD_STICKERS_UPDATE')], ['GUILD_SCHEDULED_EVENT_DELETE', require('./GUILD_SCHEDULED_EVENT_DELETE')],
['GUILD_SCHEDULED_EVENT_CREATE', require('./GUILD_SCHEDULED_EVENT_CREATE')], ['GUILD_SCHEDULED_EVENT_USER_ADD', require('./GUILD_SCHEDULED_EVENT_USER_ADD')],
['GUILD_SCHEDULED_EVENT_UPDATE', require('./GUILD_SCHEDULED_EVENT_UPDATE')], ['GUILD_SCHEDULED_EVENT_USER_REMOVE', require('./GUILD_SCHEDULED_EVENT_USER_REMOVE')],
['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'),
],
]); ]);
module.exports = handlers; module.exports = handlers;

View File

@@ -3,210 +3,173 @@
const { register } = require('./DJSError'); const { register } = require('./DJSError');
const Messages = { 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) => CLIENT_NOT_READY: action => `The client needs to be logged in to ${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: TOKEN_MISSING: 'Request to use token, but token was unavailable to the client.',
'Request to use token, but token was unavailable to the client.',
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: 'Invalid shard settings were provided.', SHARDING_INVALID: 'Invalid shard settings were provided.',
SHARDING_REQUIRED: SHARDING_REQUIRED: 'This session would have handled too many guilds - Sharding is required.',
'This session would have handled too many guilds - Sharding is required.', INVALID_INTENTS: 'Invalid intent provided for WebSocket intents.',
INVALID_INTENTS: 'Invalid intent provided for WebSocket intents.', DISALLOWED_INTENTS: 'Privileged intent provided is not enabled or whitelisted.',
DISALLOWED_INTENTS: SHARDING_NO_SHARDS: 'No shards have been spawned.',
'Privileged intent provided is not enabled or whitelisted.', SHARDING_IN_PROCESS: 'Shards are still being spawned.',
SHARDING_NO_SHARDS: 'No shards have been spawned.', SHARDING_INVALID_EVAL_BROADCAST: 'Script to evaluate must be a function',
SHARDING_IN_PROCESS: 'Shards are still being spawned.', SHARDING_SHARD_NOT_FOUND: id => `Shard ${id} could not be found.`,
SHARDING_INVALID_EVAL_BROADCAST: 'Script to evaluate must be a function', SHARDING_ALREADY_SPAWNED: count => `Already spawned ${count} shards.`,
SHARDING_SHARD_NOT_FOUND: (id) => `Shard ${id} could not be found.`, SHARDING_PROCESS_EXISTS: id => `Shard ${id} already has an active process.`,
SHARDING_ALREADY_SPAWNED: (count) => `Already spawned ${count} shards.`, SHARDING_WORKER_EXISTS: id => `Shard ${id} already has an active worker.`,
SHARDING_PROCESS_EXISTS: (id) => `Shard ${id} already has an active process.`, SHARDING_READY_TIMEOUT: id => `Shard ${id}'s Client took too long to become ready.`,
SHARDING_WORKER_EXISTS: (id) => `Shard ${id} already has an active worker.`, SHARDING_READY_DISCONNECTED: id => `Shard ${id}'s Client disconnected before becoming ready.`,
SHARDING_READY_TIMEOUT: (id) => SHARDING_READY_DIED: id => `Shard ${id}'s process exited before its Client became ready.`,
`Shard ${id}'s Client took too long to become ready.`, SHARDING_NO_CHILD_EXISTS: id => `Shard ${id} has no active process or worker.`,
SHARDING_READY_DISCONNECTED: (id) => SHARDING_SHARD_MISCALCULATION: (shard, guild, count) =>
`Shard ${id}'s Client disconnected before becoming ready.`, `Calculated invalid shard ${shard} for guild ${guild} with ${count} shards.`,
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) =>
`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: INVITE_OPTIONS_MISSING_CHANNEL: 'A valid guild channel must be provided when GuildScheduledEvent is EXTERNAL.',
'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.',
EMBED_FIELD_VALUE: 'MessageEmbed field values must be non-empty strings.', EMBED_FIELD_VALUE: 'MessageEmbed field values must be non-empty strings.',
EMBED_FOOTER_TEXT: 'MessageEmbed footer text must be a string.', EMBED_FOOTER_TEXT: 'MessageEmbed footer text must be a string.',
EMBED_DESCRIPTION: 'MessageEmbed description must be a string.', EMBED_DESCRIPTION: 'MessageEmbed description must be a string.',
EMBED_AUTHOR_NAME: 'MessageEmbed author name must be a string.', EMBED_AUTHOR_NAME: 'MessageEmbed author name must be a string.',
/* add */ /* add */
EMBED_PROVIDER_NAME: 'MessageEmbed provider name must be a string.', EMBED_PROVIDER_NAME: 'MessageEmbed provider name must be a string.',
BUTTON_LABEL: 'MessageButton label must be a string', BUTTON_LABEL: 'MessageButton label must be a string',
BUTTON_URL: 'MessageButton URL must be a string', BUTTON_URL: 'MessageButton URL must be a string',
BUTTON_CUSTOM_ID: 'MessageButton customId must be a string', BUTTON_CUSTOM_ID: 'MessageButton customId must be a string',
SELECT_MENU_CUSTOM_ID: 'MessageSelectMenu customId must be a string', SELECT_MENU_CUSTOM_ID: 'MessageSelectMenu customId must be a string',
SELECT_MENU_PLACEHOLDER: 'MessageSelectMenu placeholder must be a string', SELECT_MENU_PLACEHOLDER: 'MessageSelectMenu placeholder must be a string',
SELECT_OPTION_LABEL: 'MessageSelectOption label must be a string', SELECT_OPTION_LABEL: 'MessageSelectOption label must be a string',
SELECT_OPTION_VALUE: 'MessageSelectOption value must be a string', SELECT_OPTION_VALUE: 'MessageSelectOption value must be a string',
SELECT_OPTION_DESCRIPTION: 'MessageSelectOption description must be a string', SELECT_OPTION_DESCRIPTION: 'MessageSelectOption description must be a string',
INTERACTION_COLLECTOR_ERROR: (reason) => INTERACTION_COLLECTOR_ERROR: reason => `Collector received no interactions before ending with reason: ${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: USER_BANNER_NOT_FETCHED: "You must fetch this user's banner before trying to generate its URL!",
"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: REQ_RESOURCE_TYPE: 'The resource must be a string, Buffer or a valid file stream.',
'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: MESSAGE_BULK_DELETE_TYPE: 'The messages must be an Array, Collection, or number.',
'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: SPLIT_MAX_LEN: 'Chunk exceeds the max length and contains no split characters.',
'Chunk exceeds the max length and contains no split characters.',
BAN_RESOLVE_ID: (ban = false) => BAN_RESOLVE_ID: (ban = false) => `Couldn't resolve the user id to ${ban ? 'ban' : 'unban'}.`,
`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: GUILD_VOICE_CHANNEL_RESOLVE: 'Could not resolve channel to a guild voice channel.',
'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: GUILD_OWNED: 'Guild is owned by the client.',
"The fetched channel does not belong to this manager's guild.", GUILD_MEMBERS_TIMEOUT: "Members didn't arrive in time.",
GUILD_OWNED: 'Guild is owned by the client.', GUILD_UNCACHED_ME: 'The client user as a member of this guild is uncached.',
GUILD_MEMBERS_TIMEOUT: "Members didn't arrive in time.", CHANNEL_NOT_CACHED: 'Could not find the channel where this message came from in the cache!',
GUILD_UNCACHED_ME: 'The client user as a member of this guild is uncached.', STAGE_CHANNEL_RESOLVE: 'Could not resolve channel to a stage channel.',
CHANNEL_NOT_CACHED: GUILD_SCHEDULED_EVENT_RESOLVE: 'Could not resolve the guild scheduled event.',
'Could not find the channel where this message came from in the cache!',
STAGE_CHANNEL_RESOLVE: 'Could not resolve channel to a stage channel.',
GUILD_SCHEDULED_EVENT_RESOLVE: 'Could not resolve the guild scheduled event.',
INVALID_TYPE: (name, expected, an = false) => INVALID_TYPE: (name, expected, an = false) => `Supplied ${name} is not a${an ? 'n' : ''} ${expected}.`,
`Supplied ${name} is not a${an ? 'n' : ''} ${expected}.`, INVALID_ELEMENT: (type, name, elem) => `Supplied ${type} ${name} includes an invalid element: ${elem}`,
INVALID_ELEMENT: (type, name, elem) =>
`Supplied ${type} ${name} includes an invalid element: ${elem}`,
MESSAGE_THREAD_PARENT: MESSAGE_THREAD_PARENT: 'The message was not sent in a guild text or news channel',
'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: WEBHOOK_TOKEN_UNAVAILABLE: 'This action requires a webhook token, but none is available.',
'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: MESSAGE_REFERENCE_MISSING: 'The message does not reference another message',
'This message webhook belongs to an application and cannot be fetched.',
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: NOT_GUILD_STICKER: 'Sticker is a standard (non-guild) sticker and has no author.',
'Sticker is a standard (non-guild) sticker and has no author.',
REACTION_RESOLVE_USER: REACTION_RESOLVE_USER: "Couldn't resolve the user id to remove from the reaction.",
"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.',
INVITE_RESOLVE_CODE: 'Could not resolve the code to fetch the invite.', INVITE_RESOLVE_CODE: 'Could not resolve the code to fetch the invite.',
INVITE_NOT_FOUND: 'Could not find the requested invite.', INVITE_NOT_FOUND: 'Could not find the requested invite.',
NOT_OWNER_GROUP_DM_CHANNEL: "You can't do this action [Missing Permission]", NOT_OWNER_GROUP_DM_CHANNEL: "You can't do this action [Missing Permission]",
USER_ALREADY_IN_GROUP_DM_CHANNEL: 'User is already in the channel.', USER_ALREADY_IN_GROUP_DM_CHANNEL: 'User is already in the channel.',
USER_NOT_IN_GROUP_DM_CHANNEL: 'User is not in the channel.', USER_NOT_IN_GROUP_DM_CHANNEL: 'User is not in the channel.',
DELETE_GROUP_DM_CHANNEL: DELETE_GROUP_DM_CHANNEL: "Bots don't have access to Group DM Channels and cannot delete 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",
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: GUILD_UNCACHED_ROLE_RESOLVE: 'Cannot resolve roles from an arbitrary guild, provide an id instead',
'Cannot resolve roles from an arbitrary guild, provide an id instead',
INTERACTION_ALREADY_REPLIED: INTERACTION_ALREADY_REPLIED: 'The reply to this interaction has already 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.',
INTERACTION_NOT_REPLIED: INTERACTION_EPHEMERAL_REPLIED: 'Ephemeral responses cannot be deleted.',
'The reply to this interaction has not been sent or deferred.',
INTERACTION_EPHEMERAL_REPLIED: 'Ephemeral responses cannot be deleted.',
COMMAND_INTERACTION_OPTION_NOT_FOUND: (name) => COMMAND_INTERACTION_OPTION_NOT_FOUND: name => `Required option "${name}" not found.`,
`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.',
INVITE_MISSING_SCOPES: INVITE_MISSING_SCOPES: 'At least one valid scope must be provided for the invite',
'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: SWEEP_FILTER_RETURN: 'The return value of the sweepFilter function was not false or a Function',
'The return value of the sweepFilter function was not false or a Function',
INVALID_BOT_METHOD: `Bot accounts cannot use this method`, INVALID_BOT_METHOD: 'Bot accounts cannot use this method',
INVALID_USER_METHOD: `User accounts cannot use this method`, INVALID_USER_METHOD: 'User accounts cannot use this method',
INVALID_LOCALE: 'Unable to select this location', INVALID_LOCALE: 'Unable to select this location',
FOLDER_NOT_FOUND: 'Server directory not found', FOLDER_NOT_FOUND: 'Server directory not found',
FOLDER_POSITION_INVALID: 'The server index in the directory is invalid', FOLDER_POSITION_INVALID: 'The server index in the directory is invalid',
APPLICATION_ID_INVALID: "The application isn't BOT", APPLICATION_ID_INVALID: "The application isn't BOT",
INVALID_NITRO: 'Invalid Nitro Code', INVALID_NITRO: 'Invalid Nitro Code',
MESSAGE_ID_NOT_FOUND: 'Message ID not found', MESSAGE_ID_NOT_FOUND: 'Message ID not found',
MESSAGE_EMBED_LINK_LENGTH: MESSAGE_EMBED_LINK_LENGTH: 'Message content with embed link length is too long',
'Message content with embed link length is too long', GUILD_MEMBERS_FETCH: msg => `${msg}`,
GUILD_MEMBERS_FETCH: (msg) => `${msg}`
}; };
for (const [name, message] of Object.entries(Messages)) register(name, message); for (const [name, message] of Object.entries(Messages)) register(name, message);

View File

@@ -155,4 +155,4 @@ exports.Widget = require('./structures/Widget');
exports.WidgetMember = require('./structures/WidgetMember'); exports.WidgetMember = require('./structures/WidgetMember');
exports.WelcomeChannel = require('./structures/WelcomeChannel'); exports.WelcomeChannel = require('./structures/WelcomeChannel');
exports.WelcomeScreen = require('./structures/WelcomeScreen'); exports.WelcomeScreen = require('./structures/WelcomeScreen');
exports.WebSocket = require('./WebSocket'); exports.WebSocket = require('./WebSocket');

View File

@@ -1,224 +1,224 @@
'use strict'; 'use strict';
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
const ApplicationCommandPermissionsManager = require('./ApplicationCommandPermissionsManager'); const ApplicationCommandPermissionsManager = require('./ApplicationCommandPermissionsManager');
const CachedManager = require('./CachedManager'); const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors'); const { TypeError } = require('../errors');
const ApplicationCommand = require('../structures/ApplicationCommand'); const ApplicationCommand = require('../structures/ApplicationCommand');
const { ApplicationCommandTypes } = require('../util/Constants'); const { ApplicationCommandTypes } = require('../util/Constants');
/** /**
* Manages API methods for application commands and stores their cache. * Manages API methods for application commands and stores their cache.
* @extends {CachedManager} * @extends {CachedManager}
*/ */
class ApplicationCommandManager extends CachedManager { class ApplicationCommandManager extends CachedManager {
constructor(client, iterable, user) { constructor(client, iterable, user) {
super(client, ApplicationCommand, iterable); super(client, ApplicationCommand, iterable);
/** /**
* The manager for permissions of arbitrary commands on arbitrary guilds * The manager for permissions of arbitrary commands on arbitrary guilds
* @type {ApplicationCommandPermissionsManager} * @type {ApplicationCommandPermissionsManager}
*/ */
this.permissions = new ApplicationCommandPermissionsManager(this, user); this.permissions = new ApplicationCommandPermissionsManager(this, user);
this.user = user; this.user = user;
} }
/** /**
* The cache of this manager * The cache of this manager
* @type {Collection<Snowflake, ApplicationCommand>} * @type {Collection<Snowflake, ApplicationCommand>}
* @name ApplicationCommandManager#cache * @name ApplicationCommandManager#cache
*/ */
_add(data, cache, guildId) { _add(data, cache, guildId) {
return super._add(data, cache, { extras: [this.guild, guildId] }); return super._add(data, cache, { extras: [this.guild, guildId] });
} }
/** /**
* The APIRouter path to the commands * The APIRouter path to the commands
* @param {Snowflake} [options.id] The application command's id * @param {Snowflake} [options.id] The application command's id
* @param {Snowflake} [options.guildId] The guild's id to use in the path, * @param {Snowflake} [options.guildId] The guild's id to use in the path,
* ignored when using a {@link GuildApplicationCommandManager} * ignored when using a {@link GuildApplicationCommandManager}
* @returns {Object} * @returns {Object}
* @private * @private
*/ */
commandPath({ id, guildId } = {}) { commandPath({ id, guildId } = {}) {
let path = this.client.api.applications(this.user.id); let path = this.client.api.applications(this.user.id);
if (this.guild ?? guildId) path = path.guilds(this.guild?.id ?? guildId); if (this.guild ?? guildId) path = path.guilds(this.guild?.id ?? guildId);
return id ? path.commands(id) : path.commands; return id ? path.commands(id) : path.commands;
} }
/** /**
* Data that resolves to give an ApplicationCommand object. This can be: * Data that resolves to give an ApplicationCommand object. This can be:
* * An ApplicationCommand object * * An ApplicationCommand object
* * A Snowflake * * A Snowflake
* @typedef {ApplicationCommand|Snowflake} ApplicationCommandResolvable * @typedef {ApplicationCommand|Snowflake} ApplicationCommandResolvable
*/ */
/** /**
* Options used to fetch data from Discord * Options used to fetch data from Discord
* @typedef {Object} BaseFetchOptions * @typedef {Object} BaseFetchOptions
* @property {boolean} [cache=true] Whether to cache the fetched data if it wasn't already * @property {boolean} [cache=true] Whether to cache the fetched data if it wasn't already
* @property {boolean} [force=false] Whether to skip the cache check and request the API * @property {boolean} [force=false] Whether to skip the cache check and request the API
*/ */
/** /**
* Options used to fetch Application Commands from Discord * Options used to fetch Application Commands from Discord
* @typedef {BaseFetchOptions} FetchApplicationCommandOptions * @typedef {BaseFetchOptions} FetchApplicationCommandOptions
* @property {Snowflake} [guildId] The guild's id to fetch commands for, for when the guild is not cached * @property {Snowflake} [guildId] The guild's id to fetch commands for, for when the guild is not cached
*/ */
/** /**
* Obtains one or multiple application commands from Discord, or the cache if it's already available. * Obtains one or multiple application commands from Discord, or the cache if it's already available.
* @param {Snowflake} [id] The application command's id * @param {Snowflake} [id] The application command's id
* @param {FetchApplicationCommandOptions} [options] Additional options for this fetch * @param {FetchApplicationCommandOptions} [options] Additional options for this fetch
* @returns {Promise<ApplicationCommand|Collection<Snowflake, ApplicationCommand>>} * @returns {Promise<ApplicationCommand|Collection<Snowflake, ApplicationCommand>>}
* @example * @example
* // Fetch a single command * // Fetch a single command
* client.application.commands.fetch('123456789012345678') * client.application.commands.fetch('123456789012345678')
* .then(command => console.log(`Fetched command ${command.name}`)) * .then(command => console.log(`Fetched command ${command.name}`))
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Fetch all commands * // Fetch all commands
* guild.commands.fetch() * guild.commands.fetch()
* .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 } = {}) { async fetch(id, { guildId, cache = true, force = false } = {}) {
// change from user.createDM to opcode (risky action) // change from user.createDM to opcode (risky action)
if (typeof id === 'object') { if (typeof id === 'object') {
({ guildId, cache = true } = id); ({ guildId, cache = true } = id);
} else if (id) { } else if (id) {
if (!force) { if (!force) {
const existing = this.cache.get(id); const existing = this.cache.get(id);
if (existing) return existing; if (existing) return existing;
} }
await this.user.createDM().catch(() => {}); await this.user.createDM().catch(() => {});
const command = await this.commandPath({ id, guildId }).get(); const command = await this.commandPath({ id, guildId }).get();
return this._add(command, cache); return this._add(command, cache);
} }
await this.user.createDM().catch(() => {}); await this.user.createDM().catch(() => {});
const data = await this.commandPath({ guildId }).get(); const data = await this.commandPath({ guildId }).get();
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());
} }
/** /**
* Creates an application command. * Creates an application command.
* @param {ApplicationCommandData|APIApplicationCommand} command The command * @param {ApplicationCommandData|APIApplicationCommand} command The command
* @param {Snowflake} [guildId] The guild's id to create this command in, * @param {Snowflake} [guildId] The guild's id to create this command in,
* ignored when using a {@link GuildApplicationCommandManager} * ignored when using a {@link GuildApplicationCommandManager}
* @returns {Promise<ApplicationCommand>} * @returns {Promise<ApplicationCommand>}
* @example * @example
* // Create a new command * // Create a new command
* client.application.commands.create({ * client.application.commands.create({
* name: 'test', * name: 'test',
* description: 'A test command', * description: 'A test command',
* }) * })
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async create(command, guildId) { async create(command, guildId) {
if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD"); if (!this.client.user.bot) throw new Error('INVALID_USER_METHOD');
const data = await this.commandPath({ guildId }).post({ const data = await this.commandPath({ guildId }).post({
data: this.constructor.transformCommand(command), data: this.constructor.transformCommand(command),
}); });
return this._add(data, true, guildId); return this._add(data, true, guildId);
} }
/** /**
* Sets all the commands for this application or guild. * Sets all the commands for this application or guild.
* @param {ApplicationCommandData[]|APIApplicationCommand[]} commands The commands * @param {ApplicationCommandData[]|APIApplicationCommand[]} commands The commands
* @param {Snowflake} [guildId] The guild's id to create the commands in, * @param {Snowflake} [guildId] The guild's id to create the commands in,
* ignored when using a {@link GuildApplicationCommandManager} * ignored when using a {@link GuildApplicationCommandManager}
* @returns {Promise<Collection<Snowflake, ApplicationCommand>>} * @returns {Promise<Collection<Snowflake, ApplicationCommand>>}
* @example * @example
* // Set all commands to just this one * // Set all commands to just this one
* client.application.commands.set([ * client.application.commands.set([
* { * {
* name: 'test', * name: 'test',
* description: 'A test command', * description: 'A test command',
* }, * },
* ]) * ])
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Remove all commands * // Remove all commands
* guild.commands.set([]) * guild.commands.set([])
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async set(commands, guildId) { async set(commands, guildId) {
if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD"); if (!this.client.user.bot) throw new Error('INVALID_USER_METHOD');
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());
} }
/** /**
* Edits an application command. * Edits an application command.
* @param {ApplicationCommandResolvable} command The command to edit * @param {ApplicationCommandResolvable} command The command to edit
* @param {ApplicationCommandData|APIApplicationCommand} data The data to update the command with * @param {ApplicationCommandData|APIApplicationCommand} data The data to update the command with
* @param {Snowflake} [guildId] The guild's id where the command registered, * @param {Snowflake} [guildId] The guild's id where the command registered,
* ignored when using a {@link GuildApplicationCommandManager} * ignored when using a {@link GuildApplicationCommandManager}
* @returns {Promise<ApplicationCommand>} * @returns {Promise<ApplicationCommand>}
* @example * @example
* // Edit an existing command * // Edit an existing command
* client.application.commands.edit('123456789012345678', { * client.application.commands.edit('123456789012345678', {
* description: 'New description', * description: 'New description',
* }) * })
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async edit(command, data, guildId) { async edit(command, data, guildId) {
if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD"); if (!this.client.user.bot) throw new Error('INVALID_USER_METHOD');
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),
}); });
return this._add(patched, true, guildId); return this._add(patched, true, guildId);
} }
/** /**
* Deletes an application command. * Deletes an application command.
* @param {ApplicationCommandResolvable} command The command to delete * @param {ApplicationCommandResolvable} command The command to delete
* @param {Snowflake} [guildId] The guild's id where the command is registered, * @param {Snowflake} [guildId] The guild's id where the command is registered,
* ignored when using a {@link GuildApplicationCommandManager} * ignored when using a {@link GuildApplicationCommandManager}
* @returns {Promise<?ApplicationCommand>} * @returns {Promise<?ApplicationCommand>}
* @example * @example
* // Delete a command * // Delete a command
* guild.commands.delete('123456789012345678') * guild.commands.delete('123456789012345678')
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async delete(command, guildId) { async delete(command, guildId) {
if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD"); if (!this.client.user.bot) throw new Error('INVALID_USER_METHOD');
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();
const cached = this.cache.get(id); const cached = this.cache.get(id);
this.cache.delete(id); this.cache.delete(id);
return cached ?? null; return cached ?? null;
} }
/** /**
* Transforms an {@link ApplicationCommandData} object into something that can be used with the API. * Transforms an {@link ApplicationCommandData} object into something that can be used with the API.
* @param {ApplicationCommandData|APIApplicationCommand} command The command to transform * @param {ApplicationCommandData|APIApplicationCommand} command The command to transform
* @returns {APIApplicationCommand} * @returns {APIApplicationCommand}
* @private * @private
*/ */
static transformCommand(command) { static transformCommand(command) {
return { return {
name: command.name, name: command.name,
description: command.description, description: command.description,
type: typeof command.type === 'number' ? command.type : ApplicationCommandTypes[command.type], type: typeof command.type === 'number' ? command.type : ApplicationCommandTypes[command.type],
options: command.options?.map(o => ApplicationCommand.transformOption(o)), options: command.options?.map(o => ApplicationCommand.transformOption(o)),
default_permission: command.defaultPermission ?? command.default_permission, default_permission: command.defaultPermission ?? command.default_permission,
}; };
} }
} }
module.exports = ApplicationCommandManager; module.exports = ApplicationCommandManager;

View File

@@ -1,422 +1,425 @@
'use strict'; 'use strict';
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.
* @extends {BaseManager} * @extends {BaseManager}
*/ */
class ApplicationCommandPermissionsManager extends BaseManager { class ApplicationCommandPermissionsManager extends BaseManager {
constructor(manager, user) { constructor(manager, user) {
super(manager.client); super(manager.client);
/** /**
* The manager or command that this manager belongs to * The manager or command that this manager belongs to
* @type {ApplicationCommandManager|ApplicationCommand} * @type {ApplicationCommandManager|ApplicationCommand}
* @private * @private
*/ */
this.manager = manager; this.manager = manager;
/** /**
* The guild that this manager acts on * The guild that this manager acts on
* @type {?Guild} * @type {?Guild}
*/ */
this.guild = manager.guild ?? null; this.guild = manager.guild ?? null;
/** /**
* The id of the guild that this manager acts on * The id of the guild that this manager acts on
* @type {?Snowflake} * @type {?Snowflake}
*/ */
this.guildId = manager.guildId ?? manager.guild?.id ?? null; this.guildId = manager.guildId ?? manager.guild?.id ?? null;
/** /**
* The id of the command this manager acts on * The id of the command this manager acts on
* @type {?Snowflake} * @type {?Snowflake}
*/ */
this.commandId = manager.id ?? null; this.commandId = manager.id ?? null;
this.user = user; this.user = user;
} }
/** /**
* The APIRouter path to the commands * The APIRouter path to the commands
* @param {Snowflake} guildId The guild's id to use in the path, * @param {Snowflake} guildId The guild's id to use in the path,
* @param {Snowflake} [commandId] The application command's id * @param {Snowflake} [commandId] The application command's id
* @returns {Object} * @returns {Object}
* @private * @private
*/ */
permissionsPath(guildId, commandId) { permissionsPath(guildId, commandId) {
return this.client.api.applications(typeof this.user == 'string' ? this.user : this.user.id).guilds(guildId).commands(commandId).permissions; return this.client.api
} .applications(typeof this.user == 'string' ? this.user : this.user.id)
.guilds(guildId)
/** .commands(commandId).permissions;
* Data for setting the permissions of an application command. }
* @typedef {Object} ApplicationCommandPermissionData
* @property {Snowflake} id The role or user's id /**
* @property {ApplicationCommandPermissionType|number} type Whether this permission is for a role or a user * Data for setting the permissions of an application command.
* @property {boolean} permission Whether the role or user has the permission to use this command * @typedef {Object} ApplicationCommandPermissionData
*/ * @property {Snowflake} id The role or user's id
* @property {ApplicationCommandPermissionType|number} type Whether this permission is for a role or a user
/** * @property {boolean} permission Whether the role or user has the permission to use this command
* The object returned when fetching permissions for an application command. */
* @typedef {Object} ApplicationCommandPermissions
* @property {Snowflake} id The role or user's id /**
* @property {ApplicationCommandPermissionType} type Whether this permission is for a role or a user * The object returned when fetching permissions for an application command.
* @property {boolean} permission Whether the role or user has the permission to use this command * @typedef {Object} ApplicationCommandPermissions
*/ * @property {Snowflake} id The role or user's id
* @property {ApplicationCommandPermissionType} type Whether this permission is for a role or a user
/** * @property {boolean} permission Whether the role or user has the permission to use this command
* Options for managing permissions for one or more Application Commands */
* <warn>When passing these options to a manager where `guildId` is `null`,
* `guild` is a required parameter</warn> /**
* @typedef {Object} BaseApplicationCommandPermissionsOptions * Options for managing permissions for one or more Application Commands
* @property {GuildResolvable} [guild] The guild to modify / check permissions for * <warn>When passing these options to a manager where `guildId` is `null`,
* <warn>Ignored when the manager has a non-null `guildId` property</warn> * `guild` is a required parameter</warn>
* @property {ApplicationCommandResolvable} [command] The command to modify / check permissions for * @typedef {Object} BaseApplicationCommandPermissionsOptions
* <warn>Ignored when the manager has a non-null `commandId` property</warn> * @property {GuildResolvable} [guild] The guild to modify / check permissions for
*/ * <warn>Ignored when the manager has a non-null `guildId` property</warn>
* @property {ApplicationCommandResolvable} [command] The command to modify / check permissions for
/** * <warn>Ignored when the manager has a non-null `commandId` property</warn>
* Fetches the permissions for one or multiple commands. */
* @param {BaseApplicationCommandPermissionsOptions} [options] Options used to fetch permissions
* @returns {Promise<ApplicationCommandPermissions[]|Collection<Snowflake, ApplicationCommandPermissions[]>>} /**
* @example * Fetches the permissions for one or multiple commands.
* // Fetch permissions for one command * @param {BaseApplicationCommandPermissionsOptions} [options] Options used to fetch permissions
* guild.commands.permissions.fetch({ command: '123456789012345678' }) * @returns {Promise<ApplicationCommandPermissions[]|Collection<Snowflake, ApplicationCommandPermissions[]>>}
* .then(perms => console.log(`Fetched permissions for ${perms.length} users`)) * @example
* .catch(console.error); * // Fetch permissions for one command
* @example * guild.commands.permissions.fetch({ command: '123456789012345678' })
* // Fetch permissions for all commands in a guild * .then(perms => console.log(`Fetched permissions for ${perms.length} users`))
* client.application.commands.permissions.fetch({ guild: '123456789012345678' }) * .catch(console.error);
* .then(perms => console.log(`Fetched permissions for ${perms.size} commands`)) * @example
* .catch(console.error); * // Fetch permissions for all commands in a guild
*/ * client.application.commands.permissions.fetch({ guild: '123456789012345678' })
async fetch({ guild, command } = {}) { * .then(perms => console.log(`Fetched permissions for ${perms.size} commands`))
const { guildId, commandId } = this._validateOptions(guild, command); * .catch(console.error);
if (commandId) { */
const data = await this.permissionsPath(guildId, commandId).get(); async fetch({ guild, command } = {}) {
return data.permissions.map(perm => this.constructor.transformPermissions(perm, true)); const { guildId, commandId } = this._validateOptions(guild, command);
} if (commandId) {
const data = await this.permissionsPath(guildId, commandId).get();
const data = await this.permissionsPath(guildId).get(); return data.permissions.map(perm => this.constructor.transformPermissions(perm, true));
return data.reduce( }
(coll, perm) =>
coll.set( const data = await this.permissionsPath(guildId).get();
perm.id, return data.reduce(
perm.permissions.map(p => this.constructor.transformPermissions(p, true)), (coll, perm) =>
), coll.set(
new Collection(), perm.id,
); perm.permissions.map(p => this.constructor.transformPermissions(p, true)),
} ),
new Collection(),
/** );
* Data used for overwriting the permissions for all application commands in a guild. }
* @typedef {Object} GuildApplicationCommandPermissionData
* @property {Snowflake} id The command's id /**
* @property {ApplicationCommandPermissionData[]} permissions The permissions for this command * Data used for overwriting the permissions for all application commands in a guild.
*/ * @typedef {Object} GuildApplicationCommandPermissionData
* @property {Snowflake} id The command's id
/** * @property {ApplicationCommandPermissionData[]} permissions The permissions for this command
* Options used to set permissions for one or more Application Commands in a guild */
* <warn>One of `command` AND `permissions`, OR `fullPermissions` is required.
* `fullPermissions` is not a valid option when passing to a manager where `commandId` is non-null</warn> /**
* @typedef {BaseApplicationCommandPermissionsOptions} SetApplicationCommandPermissionsOptions * Options used to set permissions for one or more Application Commands in a guild
* @property {ApplicationCommandPermissionData[]} [permissions] The new permissions for the command * <warn>One of `command` AND `permissions`, OR `fullPermissions` is required.
* @property {GuildApplicationCommandPermissionData[]} [fullPermissions] The new permissions for all commands * `fullPermissions` is not a valid option when passing to a manager where `commandId` is non-null</warn>
* in a guild <warn>When this parameter is set, `permissions` and `command` are ignored</warn> * @typedef {BaseApplicationCommandPermissionsOptions} SetApplicationCommandPermissionsOptions
*/ * @property {ApplicationCommandPermissionData[]} [permissions] The new permissions for the command
* @property {GuildApplicationCommandPermissionData[]} [fullPermissions] The new permissions for all commands
/** * in a guild <warn>When this parameter is set, `permissions` and `command` are ignored</warn>
* Sets the permissions for one or more commands. */
* @param {SetApplicationCommandPermissionsOptions} options Options used to set permissions
* @returns {Promise<ApplicationCommandPermissions[]|Collection<Snowflake, ApplicationCommandPermissions[]>>} /**
* @example * Sets the permissions for one or more commands.
* // Set the permissions for one command * @param {SetApplicationCommandPermissionsOptions} options Options used to set permissions
* client.application.commands.permissions.set({ guild: '892455839386304532', command: '123456789012345678', * @returns {Promise<ApplicationCommandPermissions[]|Collection<Snowflake, ApplicationCommandPermissions[]>>}
* permissions: [ * @example
* { * // Set the permissions for one command
* id: '876543210987654321', * client.application.commands.permissions.set({ guild: '892455839386304532', command: '123456789012345678',
* type: 'USER', * permissions: [
* permission: false, * {
* }, * id: '876543210987654321',
* ]}) * type: 'USER',
* .then(console.log) * permission: false,
* .catch(console.error); * },
* @example * ]})
* // Set the permissions for all commands * .then(console.log)
* guild.commands.permissions.set({ fullPermissions: [ * .catch(console.error);
* { * @example
* id: '123456789012345678', * // Set the permissions for all commands
* permissions: [{ * guild.commands.permissions.set({ fullPermissions: [
* id: '876543210987654321', * {
* type: 'USER', * id: '123456789012345678',
* permission: false, * permissions: [{
* }], * id: '876543210987654321',
* }, * type: 'USER',
* ]}) * permission: false,
* .then(console.log) * }],
* .catch(console.error); * },
*/ * ]})
async set({ guild, command, permissions, fullPermissions } = {}) { * .then(console.log)
if(!this.manager.client.user.bot) throw new Error("INVALID_USER_METHOD"); * .catch(console.error);
const { guildId, commandId } = this._validateOptions(guild, command); */
async set({ guild, command, permissions, fullPermissions } = {}) {
if (commandId) { if (!this.manager.client.user.bot) throw new Error('INVALID_USER_METHOD');
if (!Array.isArray(permissions)) { const { guildId, commandId } = this._validateOptions(guild, command);
throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissionData', true);
} if (commandId) {
const data = await this.permissionsPath(guildId, commandId).put({ if (!Array.isArray(permissions)) {
data: { permissions: permissions.map(perm => this.constructor.transformPermissions(perm)) }, throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissionData', true);
}); }
return data.permissions.map(perm => this.constructor.transformPermissions(perm, true)); const data = await this.permissionsPath(guildId, commandId).put({
} data: { permissions: permissions.map(perm => this.constructor.transformPermissions(perm)) },
});
if (!Array.isArray(fullPermissions)) { return data.permissions.map(perm => this.constructor.transformPermissions(perm, true));
throw new TypeError('INVALID_TYPE', 'fullPermissions', 'Array of GuildApplicationCommandPermissionData', true); }
}
if (!Array.isArray(fullPermissions)) {
const APIPermissions = []; throw new TypeError('INVALID_TYPE', 'fullPermissions', 'Array of GuildApplicationCommandPermissionData', true);
for (const perm of fullPermissions) { }
if (!Array.isArray(perm.permissions)) throw new TypeError('INVALID_ELEMENT', 'Array', 'fullPermissions', perm);
APIPermissions.push({ const APIPermissions = [];
id: perm.id, for (const perm of fullPermissions) {
permissions: perm.permissions.map(p => this.constructor.transformPermissions(p)), if (!Array.isArray(perm.permissions)) throw new TypeError('INVALID_ELEMENT', 'Array', 'fullPermissions', perm);
}); APIPermissions.push({
} id: perm.id,
const data = await this.permissionsPath(guildId).put({ permissions: perm.permissions.map(p => this.constructor.transformPermissions(p)),
data: APIPermissions, });
}); }
return data.reduce( const data = await this.permissionsPath(guildId).put({
(coll, perm) => data: APIPermissions,
coll.set( });
perm.id, return data.reduce(
perm.permissions.map(p => this.constructor.transformPermissions(p, true)), (coll, perm) =>
), coll.set(
new Collection(), perm.id,
); perm.permissions.map(p => this.constructor.transformPermissions(p, true)),
} ),
new Collection(),
/** );
* Options used to add permissions to a command }
* <warn>The `command` parameter is not optional when the managers `commandId` is `null`</warn>
* @typedef {BaseApplicationCommandPermissionsOptions} AddApplicationCommandPermissionsOptions /**
* @property {ApplicationCommandPermissionData[]} permissions The permissions to add to the command * Options used to add permissions to a command
*/ * <warn>The `command` parameter is not optional when the managers `commandId` is `null`</warn>
* @typedef {BaseApplicationCommandPermissionsOptions} AddApplicationCommandPermissionsOptions
/** * @property {ApplicationCommandPermissionData[]} permissions The permissions to add to the command
* Add permissions to a command. */
* @param {AddApplicationCommandPermissionsOptions} options Options used to add permissions
* @returns {Promise<ApplicationCommandPermissions[]>} /**
* @example * Add permissions to a command.
* // Block a role from the command permissions * @param {AddApplicationCommandPermissionsOptions} options Options used to add permissions
* guild.commands.permissions.add({ command: '123456789012345678', permissions: [ * @returns {Promise<ApplicationCommandPermissions[]>}
* { * @example
* id: '876543211234567890', * // Block a role from the command permissions
* type: 'ROLE', * guild.commands.permissions.add({ command: '123456789012345678', permissions: [
* permission: false * {
* }, * id: '876543211234567890',
* ]}) * type: 'ROLE',
* .then(console.log) * permission: false
* .catch(console.error); * },
*/ * ]})
async add({ guild, command, permissions }) { * .then(console.log)
if(!this.manager.client.user.bot) throw new Error("INVALID_USER_METHOD"); * .catch(console.error);
const { guildId, commandId } = this._validateOptions(guild, command); */
if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); async add({ guild, command, permissions }) {
if (!Array.isArray(permissions)) { if (!this.manager.client.user.bot) throw new Error('INVALID_USER_METHOD');
throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissionData', true); const { guildId, commandId } = this._validateOptions(guild, command);
} if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
if (!Array.isArray(permissions)) {
let existing = []; throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissionData', true);
try { }
existing = await this.fetch({ guild: guildId, command: commandId });
} catch (error) { let existing = [];
if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error; try {
} existing = await this.fetch({ guild: guildId, command: commandId });
} catch (error) {
const newPermissions = permissions.slice(); if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error;
for (const perm of existing) { }
if (!newPermissions.some(x => x.id === perm.id)) {
newPermissions.push(perm); const newPermissions = permissions.slice();
} for (const perm of existing) {
} if (!newPermissions.some(x => x.id === perm.id)) {
newPermissions.push(perm);
return this.set({ guild: guildId, command: commandId, permissions: newPermissions }); }
} }
/** return this.set({ guild: guildId, command: commandId, permissions: newPermissions });
* Options used to remove permissions from a command }
* <warn>The `command` parameter is not optional when the managers `commandId` is `null`</warn>
* @typedef {BaseApplicationCommandPermissionsOptions} RemoveApplicationCommandPermissionsOptions /**
* @property {UserResolvable|UserResolvable[]} [users] The user(s) to remove from the command permissions * Options used to remove permissions from a command
* <warn>One of `users` or `roles` is required</warn> * <warn>The `command` parameter is not optional when the managers `commandId` is `null`</warn>
* @property {RoleResolvable|RoleResolvable[]} [roles] The role(s) to remove from the command permissions * @typedef {BaseApplicationCommandPermissionsOptions} RemoveApplicationCommandPermissionsOptions
* <warn>One of `users` or `roles` is required</warn> * @property {UserResolvable|UserResolvable[]} [users] The user(s) to remove from the command permissions
*/ * <warn>One of `users` or `roles` is required</warn>
* @property {RoleResolvable|RoleResolvable[]} [roles] The role(s) to remove from the command permissions
/** * <warn>One of `users` or `roles` is required</warn>
* Remove permissions from a command. */
* @param {RemoveApplicationCommandPermissionsOptions} options Options used to remove permissions
* @returns {Promise<ApplicationCommandPermissions[]>} /**
* @example * Remove permissions from a command.
* // Remove a user permission from this command * @param {RemoveApplicationCommandPermissionsOptions} options Options used to remove permissions
* guild.commands.permissions.remove({ command: '123456789012345678', users: '876543210123456789' }) * @returns {Promise<ApplicationCommandPermissions[]>}
* .then(console.log) * @example
* .catch(console.error); * // Remove a user permission from this command
* @example * guild.commands.permissions.remove({ command: '123456789012345678', users: '876543210123456789' })
* // Remove multiple roles from this command * .then(console.log)
* guild.commands.permissions.remove({ * .catch(console.error);
* command: '123456789012345678', roles: ['876543210123456789', '765432101234567890'] * @example
* }) * // Remove multiple roles from this command
* .then(console.log) * guild.commands.permissions.remove({
* .catch(console.error); * command: '123456789012345678', roles: ['876543210123456789', '765432101234567890']
*/ * })
async remove({ guild, command, users, roles }) { * .then(console.log)
if(!this.manager.client.user.bot) throw new Error("INVALID_USER_METHOD"); * .catch(console.error);
const { guildId, commandId } = this._validateOptions(guild, command); */
if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); async remove({ guild, command, users, roles }) {
if (!this.manager.client.user.bot) throw new Error('INVALID_USER_METHOD');
if (!users && !roles) throw new TypeError('INVALID_TYPE', 'users OR roles', 'Array or Resolvable', true); const { guildId, commandId } = this._validateOptions(guild, command);
if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
let resolvedIds = [];
if (Array.isArray(users)) { if (!users && !roles) throw new TypeError('INVALID_TYPE', 'users OR roles', 'Array or Resolvable', true);
users.forEach(user => {
const userId = this.client.users.resolveId(user); const resolvedIds = [];
if (!userId) throw new TypeError('INVALID_ELEMENT', 'Array', 'users', user); if (Array.isArray(users)) {
resolvedIds.push(userId); users.forEach(user => {
}); const userId = this.client.users.resolveId(user);
} else if (users) { if (!userId) throw new TypeError('INVALID_ELEMENT', 'Array', 'users', user);
const userId = this.client.users.resolveId(users); resolvedIds.push(userId);
if (!userId) { });
throw new TypeError('INVALID_TYPE', 'users', 'Array or UserResolvable'); } else if (users) {
} const userId = this.client.users.resolveId(users);
resolvedIds.push(userId); if (!userId) {
} throw new TypeError('INVALID_TYPE', 'users', 'Array or UserResolvable');
}
if (Array.isArray(roles)) { resolvedIds.push(userId);
roles.forEach(role => { }
if (typeof role === 'string') {
resolvedIds.push(role); if (Array.isArray(roles)) {
return; roles.forEach(role => {
} if (typeof role === 'string') {
if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE'); resolvedIds.push(role);
const roleId = this.guild.roles.resolveId(role); return;
if (!roleId) throw new TypeError('INVALID_ELEMENT', 'Array', 'users', role); }
resolvedIds.push(roleId); if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE');
}); const roleId = this.guild.roles.resolveId(role);
} else if (roles) { if (!roleId) throw new TypeError('INVALID_ELEMENT', 'Array', 'users', role);
if (typeof roles === 'string') { resolvedIds.push(roleId);
resolvedIds.push(roles); });
} else { } else if (roles) {
if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE'); if (typeof roles === 'string') {
const roleId = this.guild.roles.resolveId(roles); resolvedIds.push(roles);
if (!roleId) { } else {
throw new TypeError('INVALID_TYPE', 'users', 'Array or RoleResolvable'); if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE');
} const roleId = this.guild.roles.resolveId(roles);
resolvedIds.push(roleId); if (!roleId) {
} throw new TypeError('INVALID_TYPE', 'users', 'Array or RoleResolvable');
} }
resolvedIds.push(roleId);
let existing = []; }
try { }
existing = await this.fetch({ guild: guildId, command: commandId });
} catch (error) { let existing = [];
if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error; try {
} existing = await this.fetch({ guild: guildId, command: commandId });
} catch (error) {
const permissions = existing.filter(perm => !resolvedIds.includes(perm.id)); if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error;
}
return this.set({ guild: guildId, command: commandId, permissions });
} const permissions = existing.filter(perm => !resolvedIds.includes(perm.id));
/** return this.set({ guild: guildId, command: commandId, permissions });
* Options used to check the existence of permissions on a command }
* <warn>The `command` parameter is not optional when the managers `commandId` is `null`</warn>
* @typedef {BaseApplicationCommandPermissionsOptions} HasApplicationCommandPermissionsOptions /**
* @property {UserResolvable|RoleResolvable} permissionId The user or role to check if a permission exists for * Options used to check the existence of permissions on a command
* on this command. * <warn>The `command` parameter is not optional when the managers `commandId` is `null`</warn>
*/ * @typedef {BaseApplicationCommandPermissionsOptions} HasApplicationCommandPermissionsOptions
* @property {UserResolvable|RoleResolvable} permissionId The user or role to check if a permission exists for
/** * on this command.
* Check whether a permission exists for a user or role */
* @param {AddApplicationCommandPermissionsOptions} options Options used to check permissions
* @returns {Promise<boolean>} /**
* @example * Check whether a permission exists for a user or role
* // Check whether a user has permission to use a command * @param {AddApplicationCommandPermissionsOptions} options Options used to check permissions
* guild.commands.permissions.has({ command: '123456789012345678', permissionId: '876543210123456789' }) * @returns {Promise<boolean>}
* .then(console.log) * @example
* .catch(console.error); * // Check whether a user has permission to use a command
*/ * guild.commands.permissions.has({ command: '123456789012345678', permissionId: '876543210123456789' })
async has({ guild, command, permissionId }) { * .then(console.log)
const { guildId, commandId } = this._validateOptions(guild, command); * .catch(console.error);
if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); */
async has({ guild, command, permissionId }) {
if (!permissionId) throw new TypeError('INVALID_TYPE', 'permissionId', 'UserResolvable or RoleResolvable'); const { guildId, commandId } = this._validateOptions(guild, command);
let resolvedId = permissionId; if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
if (typeof permissionId !== 'string') {
resolvedId = this.client.users.resolveId(permissionId); if (!permissionId) throw new TypeError('INVALID_TYPE', 'permissionId', 'UserResolvable or RoleResolvable');
if (!resolvedId) { let resolvedId = permissionId;
if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE'); if (typeof permissionId !== 'string') {
resolvedId = this.guild.roles.resolveId(permissionId); resolvedId = this.client.users.resolveId(permissionId);
} if (!resolvedId) {
if (!resolvedId) { if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE');
throw new TypeError('INVALID_TYPE', 'permissionId', 'UserResolvable or RoleResolvable'); resolvedId = this.guild.roles.resolveId(permissionId);
} }
} if (!resolvedId) {
throw new TypeError('INVALID_TYPE', 'permissionId', 'UserResolvable or RoleResolvable');
let existing = []; }
try { }
existing = await this.fetch({ guild: guildId, command: commandId });
} catch (error) { let existing = [];
if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error; try {
} existing = await this.fetch({ guild: guildId, command: commandId });
} catch (error) {
return existing.some(perm => perm.id === resolvedId); if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error;
} }
_validateOptions(guild, command) { return existing.some(perm => perm.id === resolvedId);
const guildId = this.guildId ?? this.client.guilds.resolveId(guild); }
if (!guildId) throw new Error('GLOBAL_COMMAND_PERMISSIONS');
let commandId = this.commandId; _validateOptions(guild, command) {
if (command && !commandId) { const guildId = this.guildId ?? this.client.guilds.resolveId(guild);
commandId = this.manager.resolveId?.(command); if (!guildId) throw new Error('GLOBAL_COMMAND_PERMISSIONS');
if (!commandId && this.guild) { let commandId = this.commandId;
commandId = this.guild.commands.resolveId(command); if (command && !commandId) {
} commandId = this.manager.resolveId?.(command);
commandId ??= this.client.application?.commands.resolveId(command); if (!commandId && this.guild) {
if (!commandId) { commandId = this.guild.commands.resolveId(command);
throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable', true); }
} commandId ??= this.client.application?.commands.resolveId(command);
} if (!commandId) {
return { guildId, commandId }; throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable', true);
} }
}
/** return { guildId, commandId };
* Transforms an {@link ApplicationCommandPermissionData} object into something that can be used with the API. }
* @param {ApplicationCommandPermissionData} permissions The permissions to transform
* @param {boolean} [received] Whether these permissions have been received from Discord /**
* @returns {APIApplicationCommandPermissions} * Transforms an {@link ApplicationCommandPermissionData} object into something that can be used with the API.
* @private * @param {ApplicationCommandPermissionData} permissions The permissions to transform
*/ * @param {boolean} [received] Whether these permissions have been received from Discord
static transformPermissions(permissions, received) { * @returns {APIApplicationCommandPermissions}
return { * @private
id: permissions.id, */
permission: permissions.permission, static transformPermissions(permissions, received) {
type: return {
typeof permissions.type === 'number' && !received id: permissions.id,
? permissions.type permission: permissions.permission,
: ApplicationCommandPermissionTypes[permissions.type], type:
}; typeof permissions.type === 'number' && !received
} ? permissions.type
} : ApplicationCommandPermissionTypes[permissions.type],
};
module.exports = ApplicationCommandPermissionsManager; }
}
/* eslint-disable max-len */
/** module.exports = ApplicationCommandPermissionsManager;
* @external APIApplicationCommandPermissions
* @see {@link https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure} /* eslint-disable max-len */
*/ /**
* @external APIApplicationCommandPermissions
* @see {@link https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure}
*/

View File

@@ -1,75 +1,75 @@
'use strict'; 'use strict';
const CachedManager = require('./CachedManager'); const CachedManager = require('./CachedManager');
const GuildMember = require('../structures/GuildMember'); const GuildMember = require('../structures/GuildMember');
const Message = require('../structures/Message'); const Message = require('../structures/Message');
const ThreadMember = require('../structures/ThreadMember'); const ThreadMember = require('../structures/ThreadMember');
const User = require('../structures/User'); const User = require('../structures/User');
/** /**
* Manages API methods for users and stores their cache. * Manages API methods for users and stores their cache.
* @extends {CachedManager} * @extends {CachedManager}
*/ */
class BlockedManager extends CachedManager { class BlockedManager extends CachedManager {
constructor(client, iterable) { constructor(client, iterable) {
super(client, User, iterable); super(client, User, iterable);
} }
/** /**
* The cache of this manager * The cache of this manager
* @type {Collection<Snowflake, User>} * @type {Collection<Snowflake, User>}
* @name BlockedManager#cache * @name BlockedManager#cache
*/ */
/** /**
* Data that resolves to give a User object. This can be: * Data that resolves to give a User object. This can be:
* * A User object * * A User object
* * A Snowflake * * A Snowflake
* * A Message object (resolves to the message author) * * A Message object (resolves to the message author)
* * A GuildMember object * * A GuildMember object
* * A ThreadMember object * * A ThreadMember object
* @typedef {User|Snowflake|Message|GuildMember|ThreadMember} UserResolvable * @typedef {User|Snowflake|Message|GuildMember|ThreadMember} UserResolvable
*/ */
/** /**
* Resolves a {@link UserResolvable} to a {@link User} object. * Resolves a {@link UserResolvable} to a {@link User} object.
* @param {UserResolvable} user The UserResolvable to identify * @param {UserResolvable} user The UserResolvable to identify
* @returns {?User} * @returns {?User}
*/ */
resolve(user) { resolve(user) {
if (user instanceof GuildMember || user instanceof ThreadMember) return user.user; if (user instanceof GuildMember || user instanceof ThreadMember) return user.user;
if (user instanceof Message) return user.author; if (user instanceof Message) return user.author;
return super.resolve(user); return super.resolve(user);
} }
/** /**
* Resolves a {@link UserResolvable} to a {@link User} id. * Resolves a {@link UserResolvable} to a {@link User} id.
* @param {UserResolvable} user The UserResolvable to identify * @param {UserResolvable} user The UserResolvable to identify
* @returns {?Snowflake} * @returns {?Snowflake}
*/ */
resolveId(user) { resolveId(user) {
if (user instanceof ThreadMember) return user.id; if (user instanceof ThreadMember) return user.id;
if (user instanceof GuildMember) return user.user.id; if (user instanceof GuildMember) return user.user.id;
if (user instanceof Message) return user.author.id; if (user instanceof Message) return user.author.id;
return super.resolveId(user); return super.resolveId(user);
} }
/** /**
* Obtains a user from Discord, or the user cache if it's already available. * Obtains a user from Discord, or the user cache if it's already available.
* @param {UserResolvable} user The user to fetch * @param {UserResolvable} user The user to fetch
* @param {BaseFetchOptions} [options] Additional options for this fetch * @param {BaseFetchOptions} [options] Additional options for this fetch
* @returns {Promise<User>} * @returns {Promise<User>}
*/ */
async fetch(user, { cache = true, force = false } = {}) { async fetch(user, { cache = true, force = false } = {}) {
const id = this.resolveId(user); const id = this.resolveId(user);
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;
} }
const data = await this.client.api.users(id).get(); const data = await this.client.api.users(id).get();
return this._add(data, cache); return this._add(data, cache);
} }
} }
module.exports = BlockedManager; module.exports = BlockedManager;

View File

@@ -132,8 +132,8 @@ class ChannelManager extends CachedManager {
console.log(recipients); console.log(recipients);
if (recipients.length < 2 || recipients.length > 9) throw new Error('Invalid Users length (2 - 9)'); if (recipients.length < 2 || recipients.length > 9) throw new Error('Invalid Users length (2 - 9)');
const data = await this.client.api.users['@me'].channels.post({ const data = await this.client.api.users['@me'].channels.post({
data: { recipients: recipients.map((r) => r.id) }, data: { recipients: recipients.map(r => r.id) },
}); });
return this._add(data, null, { cache: true, allowUnknownGuild: true }); return this._add(data, null, { cache: true, allowUnknownGuild: true });
} }
} }

View File

@@ -1,367 +1,339 @@
'use strict'; 'use strict';
const CachedManager = require('./CachedManager'); const CachedManager = require('./CachedManager');
const { default: Collection } = require('@discordjs/collection'); const { default: Collection } = require('@discordjs/collection');
const { Error, TypeError } = require('../errors/DJSError'); const { Error, TypeError } = require('../errors/DJSError');
const { remove } = require('lodash'); const { remove } = require('lodash');
const { localeObject, DMScanLevel, stickerAnimationMode } = require('../util/Constants') const { localeObject, DMScanLevel, stickerAnimationMode } = require('../util/Constants');
/** /**
* Manages API methods for users and stores their cache. * Manages API methods for users and stores their cache.
* @extends {CachedManager} * @extends {CachedManager}
*/ */
class ClientUserSettingManager extends CachedManager { class ClientUserSettingManager extends CachedManager {
constructor(client, iterable) { constructor(client, iterable) {
super(client); super(client);
// Raw data // Raw data
this.rawSetting = {}; this.rawSetting = {};
// Language // Language
this.locale = null; this.locale = null;
// Setting => ACTIVITY SETTINGS => Activity Status => Display current activity as a status message // Setting => ACTIVITY SETTINGS => Activity Status => Display current activity as a status message
this.activityDisplay = null; this.activityDisplay = null;
// //
this.disableDMfromServer = new Collection(); this.disableDMfromServer = new Collection();
// Allow direct messages from server members // Allow direct messages from server members
this.DMfromServerMode = null; this.DMfromServerMode = null;
// //
this.displayImage = null; this.displayImage = null;
// //
this.linkedImageDisplay = null; this.linkedImageDisplay = null;
// Setting => APP SETTINGS => Accessibility => Automatically play GIFs when Discord is focused. // Setting => APP SETTINGS => Accessibility => Automatically play GIFs when Discord is focused.
this.autoplayGIF = null; this.autoplayGIF = null;
// Show embeds and preview website links pasted into chat // Show embeds and preview website links pasted into chat
this.previewLink = null; this.previewLink = null;
// Setting => APP SETTINGS => Accessibility => Play Animated Emojis // Setting => APP SETTINGS => Accessibility => Play Animated Emojis
this.animatedEmojis = null; this.animatedEmojis = null;
// Setting => APP SETTINGS => Accessibility => Text-to-speech => Allow playback // Setting => APP SETTINGS => Accessibility => Text-to-speech => Allow playback
this.allowTTS = null; this.allowTTS = null;
// Setting => APP SETTINGS => Appearance => Message Display => Compact Mode [OK] // Setting => APP SETTINGS => Appearance => Message Display => Compact Mode [OK]
this.compactMode = null; this.compactMode = null;
// Setting => APP SETTINGS => Text & Images => Emoji => Convert Emoticons // Setting => APP SETTINGS => Text & Images => Emoji => Convert Emoticons
this.convertEmoticons = null; this.convertEmoticons = null;
// SAFE DIRECT MESSAGING // SAFE DIRECT MESSAGING
this.DMScanLevel = null; this.DMScanLevel = null;
// Setting => APP SETTINGS => Appearance => Theme [OK] // Setting => APP SETTINGS => Appearance => Theme [OK]
this.theme = ''; this.theme = '';
// //
this.developerMode = null; this.developerMode = null;
// //
this.afkTimeout = null; this.afkTimeout = null;
// //
this.stickerAnimationMode = null; this.stickerAnimationMode = null;
// WHO CAN ADD YOU AS A FRIEND ? // WHO CAN ADD YOU AS A FRIEND ?
this.addFriendFrom = { this.addFriendFrom = {
all: null, all: null,
mutual_friends: null, mutual_friends: null,
mutual_guilds: null, mutual_guilds: null,
}; };
// Setting => APP SETTINGS => Text & Images => Emoji => Show emoji reactions // Setting => APP SETTINGS => Text & Images => Emoji => Show emoji reactions
this.showEmojiReactions = null; this.showEmojiReactions = null;
// Custom Stauts [It's not working now] // Custom Stauts [It's not working now]
this.customStatus = null; this.customStatus = null;
// Guild folder and position // Guild folder and position
this.guildMetadata = new Collection(); this.guildMetadata = new Collection();
// Todo: add new method from Discum // Todo: add new method from Discum
} }
/** /**
* *
* @param {Object} data Raw Data to patch * @param {Object} data Raw Data to patch
* @extends https://github.com/Merubokkusu/Discord-S.C.U.M/blob/master/discum/user/user.py * @extends https://github.com/Merubokkusu/Discord-S.C.U.M/blob/master/discum/user/user.py
* @private * @private
*/ */
_patch(data) { _patch(data) {
this.rawSetting = Object.assign(this.rawSetting, data); this.rawSetting = Object.assign(this.rawSetting, data);
if ('locale' in data) { if ('locale' in data) {
this.locale = localeObject[data.locale]; this.locale = localeObject[data.locale];
} }
if ('show_current_game' in data) { if ('show_current_game' in data) {
this.activityDisplay = data.show_current_game; this.activityDisplay = data.show_current_game;
} }
if ('default_guilds_restricted' in data) { if ('default_guilds_restricted' in data) {
this.DMfromServerMode = data.default_guilds_restricted; this.DMfromServerMode = data.default_guilds_restricted;
} }
if ('inline_attachment_media' in data) { if ('inline_attachment_media' in data) {
this.displayImage = data.inline_attachment_media; this.displayImage = data.inline_attachment_media;
} }
if ('inline_embed_media' in data) { if ('inline_embed_media' in data) {
this.linkedImageDisplay = data.inline_embed_media; this.linkedImageDisplay = data.inline_embed_media;
} }
if ('gif_auto_play' in data) { if ('gif_auto_play' in data) {
this.autoplayGIF = data.gif_auto_play; this.autoplayGIF = data.gif_auto_play;
} }
if ('render_embeds' in data) { if ('render_embeds' in data) {
this.previewLink = data.render_embeds; this.previewLink = data.render_embeds;
} }
if ('animate_emoji' in data) { if ('animate_emoji' in data) {
this.animatedEmojis = data.animate_emoji; this.animatedEmojis = data.animate_emoji;
} }
if ('enable_tts_command' in data) { if ('enable_tts_command' in data) {
this.allowTTS = data.enable_tts_command; this.allowTTS = data.enable_tts_command;
} }
if ('message_display_compact' in data) { if ('message_display_compact' in data) {
this.compactMode = data.message_display_compact; this.compactMode = data.message_display_compact;
} }
if ('convert_emoticons' in data) { if ('convert_emoticons' in data) {
this.convertEmoticons = data.convert_emoticons; this.convertEmoticons = data.convert_emoticons;
} }
if ('explicit_content_filter' in data) { if ('explicit_content_filter' in data) {
this.DMScanLevel = DMScanLevel[data.explicit_content_filter]; this.DMScanLevel = DMScanLevel[data.explicit_content_filter];
} }
if ('theme' in data) { if ('theme' in data) {
this.theme = data.theme; this.theme = data.theme;
} }
if ('developer_mode' in data) { if ('developer_mode' in data) {
this.developerMode = data.developer_mode; this.developerMode = data.developer_mode;
} }
if ('afk_timeout' in data) { if ('afk_timeout' in data) {
this.afkTimeout = data.afk_timeout * 1000; // second => milisecond this.afkTimeout = data.afk_timeout * 1000; // second => milisecond
} }
if ('animate_stickers' in data) { if ('animate_stickers' in data) {
this.stickerAnimationMode = stickerAnimationMode[data.animate_stickers]; this.stickerAnimationMode = stickerAnimationMode[data.animate_stickers];
} }
if ('render_reactions' in data) { if ('render_reactions' in data) {
this.showEmojiReactions = data.render_reactions; this.showEmojiReactions = data.render_reactions;
} }
if ('custom_status' in data) { if ('custom_status' in data) {
this.customStatus = data.custom_status || {}; // Thanks PinkDuwc._#3443 reported this issue this.customStatus = data.custom_status || {}; // Thanks PinkDuwc._#3443 reported this issue
this.customStatus.status = data.status; this.customStatus.status = data.status;
} }
if ('friend_source_flags' in data) { if ('friend_source_flags' in data) {
this.addFriendFrom = { this.addFriendFrom = {
all: data.friend_source_flags.all || false, all: data.friend_source_flags.all || false,
mutual_friends: mutual_friends: data.friend_source_flags.all ? true : data.friend_source_flags.mutual_friends,
data.friend_source_flags.all ? true : data.friend_source_flags.mutual_friends, mutual_guilds: data.friend_source_flags.all ? true : data.friend_source_flags.mutual_guilds,
mutual_guilds: };
data.friend_source_flags.all ? true : data.friend_source_flags.mutual_guilds, }
}; if ('guild_folders' in data) {
} const data_ = data.guild_positions.map((guildId, i) => {
if ('guild_folders' in data) { // Find folder
const data_ = data.guild_positions.map((guildId, i) => { const folderIndex = data.guild_folders.findIndex(obj => obj.guild_ids.includes(guildId));
// Find folder const metadata = {
const folderIndex = data.guild_folders.findIndex((obj) => guildId: guildId,
obj.guild_ids.includes(guildId), guildIndex: i,
); folderId: data.guild_folders[folderIndex]?.id,
const metadata = { folderIndex,
guildId: guildId, folderName: data.guild_folders[folderIndex]?.name,
guildIndex: i, folderColor: data.guild_folders[folderIndex]?.color,
folderId: data.guild_folders[folderIndex]?.id, folderGuilds: data.guild_folders[folderIndex]?.guild_ids,
folderIndex, };
folderName: data.guild_folders[folderIndex]?.name, return [guildId, metadata];
folderColor: data.guild_folders[folderIndex]?.color, });
folderGuilds: data.guild_folders[folderIndex]?.guild_ids, this.guildMetadata = new Collection(data_);
}; }
return [guildId, metadata]; if ('restricted_guilds' in data) {
}); data.restricted_guilds.map(guildId => {
this.guildMetadata = new Collection(data_); const guild = this.client.guilds.cache.get(guildId);
} if (!guild) return;
if ('restricted_guilds' in data) { guild.disableDM = true;
data.restricted_guilds.map((guildId) => { this.disableDMfromServer.set(guildId, true);
const guild = this.client.guilds.cache.get(guildId); });
if (!guild) return; }
guild.disableDM = true; }
this.disableDMfromServer.set(guildId, true); async fetch() {
}); if (this.client.bot) throw new Error('INVALID_BOT_METHOD');
} try {
} const data = await this.client.api.users('@me').settings.get();
async fetch() { this._patch(data);
if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); return this;
try { } catch (e) {
const data = await this.client.api.users('@me').settings.get(); throw e;
this._patch(data); }
return this; }
} catch (e) { /**
throw e; * Edit data
} * @param {Object} data Data to edit
} * @private
/** */
* Edit data async edit(data) {
* @param {Object} data Data to edit if (this.client.bot) throw new Error('INVALID_BOT_METHOD');
* @private try {
*/ const res = await this.client.api.users('@me').settings.patch({ data });
async edit(data) { this._patch(res);
if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); return this;
try { } catch (e) {
const res = await this.client.api.users('@me').settings.patch({ data }); throw e;
this._patch(res); }
return this; }
} catch (e) { /**
throw e; * Set compact mode
} * @param {Boolean | null} value Compact mode enable or disable
} * @returns {Boolean}
/** */
* Set compact mode async setDisplayCompactMode(value) {
* @param {Boolean | null} value Compact mode enable or disable if (this.client.bot) throw new Error('INVALID_BOT_METHOD');
* @returns {Boolean} if (typeof value !== 'boolean' && typeof value !== 'null' && typeof value !== 'undefined') {
*/ throw new TypeError('INVALID_TYPE', 'value', 'boolean | null | undefined', true);
async setDisplayCompactMode(value) { }
if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); if (!value) value = !this.compactMode;
if ( if (value !== this.compactMode) {
typeof value !== 'boolean' && await this.edit({ message_display_compact: value });
typeof value !== 'null' && }
typeof value !== 'undefined' return this.compactMode;
) }
throw new TypeError( /**
'INVALID_TYPE', * Discord Theme
'value', * @param {null |dark |light} value Theme to set
'boolean | null | undefined', * @returns {theme}
true, */
); async setTheme(value) {
if (!value) value = !this.compactMode; if (this.client.bot) throw new Error('INVALID_BOT_METHOD');
if (value !== this.compactMode) { const validValues = ['dark', 'light'];
await this.edit({ message_display_compact: value }); if (typeof value !== 'string' && typeof value !== 'null' && typeof value !== 'undefined') {
} throw new TypeError('INVALID_TYPE', 'value', 'string | null | undefined', true);
return this.compactMode; }
} if (!validValues.includes(value)) {
/** value == validValues[0] ? (value = validValues[1]) : (value = validValues[0]);
* Discord Theme }
* @param {null |dark |light} value Theme to set if (value !== this.theme) {
* @returns {theme} await this.edit({ theme: value });
*/ }
async setTheme(value) { return this.theme;
if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); }
const validValues = ['dark', 'light']; /**
if ( * * Locale Setting, must be one of:
typeof value !== 'string' && * * `DANISH`
typeof value !== 'null' && * * `GERMAN`
typeof value !== 'undefined' * * `ENGLISH_UK`
) * * `ENGLISH_US`
throw new TypeError( * * `SPANISH`
'INVALID_TYPE', * * `FRENCH`
'value', * * `CROATIAN`
'string | null | undefined', * * `ITALIAN`
true, * * `LITHUANIAN`
); * * `HUNGARIAN`
if (!validValues.includes(value)) { * * `DUTCH`
value == validValues[0] * * `NORWEGIAN`
? (value = validValues[1]) * * `POLISH`
: (value = validValues[0]); * * `BRAZILIAN_PORTUGUESE`
} * * `ROMANIA_ROMANIAN`
if (value !== this.theme) { * * `FINNISH`
await this.edit({ theme: value }); * * `SWEDISH`
} * * `VIETNAMESE`
return this.theme; * * `TURKISH`
} * * `CZECH`
/** * * `GREEK`
* * Locale Setting, must be one of: * * `BULGARIAN`
* * `DANISH` * * `RUSSIAN`
* * `GERMAN` * * `UKRAINIAN`
* * `ENGLISH_UK` * * `HINDI`
* * `ENGLISH_US` * * `THAI`
* * `SPANISH` * * `CHINA_CHINESE`
* * `FRENCH` * * `JAPANESE`
* * `CROATIAN` * * `TAIWAN_CHINESE`
* * `ITALIAN` * * `KOREAN`
* * `LITHUANIAN` * @param {string} value
* * `HUNGARIAN` * @returns {locale}
* * `DUTCH` */
* * `NORWEGIAN` async setLocale(value) {
* * `POLISH` if (this.client.bot) throw new Error('INVALID_BOT_METHOD');
* * `BRAZILIAN_PORTUGUESE` if (typeof value !== 'string') {
* * `ROMANIA_ROMANIAN` throw new TypeError('INVALID_TYPE', 'value', 'string', true);
* * `FINNISH` }
* * `SWEDISH` if (!localeObject[value]) throw new Error('INVALID_LOCALE');
* * `VIETNAMESE` if (localeObject[value] !== this.locale) {
* * `TURKISH` await this.edit({ locale: localeObject[value] });
* * `CZECH` }
* * `GREEK` return this.locale;
* * `BULGARIAN` }
* * `RUSSIAN` // TODO: Guild positions & folders
* * `UKRAINIAN` // Change Index in Array [Hidden]
* * `HINDI` /**
* * `THAI` *
* * `CHINA_CHINESE` * @param {Array} array Array
* * `JAPANESE` * @param {Number} from Index1
* * `TAIWAN_CHINESE` * @param {Number} to Index2
* * `KOREAN` * @returns {Array}
* @param {string} value * @private
* @returns {locale} */
*/ _move(array, from, to) {
async setLocale(value) { array.splice(to, 0, array.splice(from, 1)[0]);
if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); return array;
if (typeof value !== 'string') }
throw new TypeError('INVALID_TYPE', 'value', 'string', true); // TODO: Move Guild
if (!localeObject[value]) throw new Error('INVALID_LOCALE'); // folder to folder
if (localeObject[value] !== this.locale) { // folder to home
await this.edit({ locale: localeObject[value] }); // home to home
} // home to folder
return this.locale; /**
} * Change Guild Position (from * to Folder or Home)
// TODO: Guild positions & folders * @param {GuildIDResolve} guildId guild.id
// Change Index in Array [Hidden] * @param {Number} newPosition Guild Position
/** * * **WARNING**: Type = `FOLDER`, newPosition is the guild's index in the Folder.
* * @param {number} type Move to folder or home
* @param {Array} array Array * * `FOLDER`: 1
* @param {Number} from Index1 * * `HOME`: 2
* @param {Number} to Index2 * @param {FolderID} folderId If you want to move to folder
* @returns {Array} * @private
* @private */
*/ async guildChangePosition(guildId, newPosition, type, folderId) {
_move(array, from, to) { // get Guild default position
array.splice(to, 0, array.splice(from, 1)[0]); // Escape
return array; const oldGuildFolderPosition = this.rawSetting.guild_folders.findIndex(value => value.guild_ids.includes(guildId));
} const newGuildFolderPosition = this.rawSetting.guild_folders.findIndex(value =>
// TODO: Move Guild value.guild_ids.includes(this.rawSetting.guild_positions[newPosition]),
// folder to folder );
// folder to home if (type == 2 || `${type}`.toUpperCase() == 'HOME') {
// home to home // Delete GuildID from Folder and create new Folder
// home to folder // Check it is folder
/** const folder = this.rawSetting.guild_folders[oldGuildFolderPosition];
* Change Guild Position (from * to Folder or Home) if (folder.id) {
* @param {GuildIDResolve} guildId guild.id this.rawSetting.guild_folders[oldGuildFolderPosition].guild_ids = this.rawSetting.guild_folders[
* @param {Number} newPosition Guild Position oldGuildFolderPosition
* * **WARNING**: Type = `FOLDER`, newPosition is the guild's index in the Folder. ].guild_ids.filter(v => v !== guildId);
* @param {number} type Move to folder or home }
* * `FOLDER`: 1 this.rawSetting.guild_folders = this._move(
* * `HOME`: 2 this.rawSetting.guild_folders,
* @param {FolderID} folderId If you want to move to folder oldGuildFolderPosition,
* @private newGuildFolderPosition,
*/ );
async guildChangePosition(guildId, newPosition, type, folderId) { this.rawSetting.guild_folders[newGuildFolderPosition].id = null;
// get Guild default position } else if (type == 1 || `${type}`.toUpperCase() == 'FOLDER') {
// Escape // Delete GuildID from oldFolder
const oldGuildFolderPosition = this.rawSetting.guild_folders.findIndex( this.rawSetting.guild_folders[oldGuildFolderPosition].guild_ids = this.rawSetting.guild_folders[
(value) => value.guild_ids.includes(guildId), oldGuildFolderPosition
); ].guild_ids.filter(v => v !== guildId);
const newGuildFolderPosition = this.rawSetting.guild_folders.findIndex( // Index new Folder
(value) => const folderIndex = this.rawSetting.guild_folders.findIndex(value => value.id == folderId);
value.guild_ids.includes(this.rawSetting.guild_positions[newPosition]), const folder = this.rawSetting.guild_folders[folderIndex];
); folder.guild_ids.push(guildId);
if (type == 2 || `${type}`.toUpperCase() == 'HOME') { folder.guild_ids = [...new Set(folder.guild_ids)];
// Delete GuildID from Folder and create new Folder folder.guild_ids = this._move(
// Check it is folder folder.guild_ids,
const folder = this.rawSetting.guild_folders[oldGuildFolderPosition]; folder.guild_ids.findIndex(v => v == guildId),
if (folder.id) { newPosition,
this.rawSetting.guild_folders[oldGuildFolderPosition].guild_ids = );
this.rawSetting.guild_folders[ }
oldGuildFolderPosition this.edit({ guild_folders: this.rawSetting.guild_folders });
].guild_ids.filter((v) => v !== guildId); }
} }
this.rawSetting.guild_folders = this._move(
this.rawSetting.guild_folders, module.exports = ClientUserSettingManager;
oldGuildFolderPosition,
newGuildFolderPosition,
);
this.rawSetting.guild_folders[newGuildFolderPosition].id = null;
} else if (type == 1 || `${type}`.toUpperCase() == 'FOLDER') {
// Delete GuildID from oldFolder
this.rawSetting.guild_folders[oldGuildFolderPosition].guild_ids =
this.rawSetting.guild_folders[oldGuildFolderPosition].guild_ids.filter(
(v) => v !== guildId,
);
// Index new Folder
const folderIndex = this.rawSetting.guild_folders.findIndex(
(value) => value.id == folderId,
);
const folder = this.rawSetting.guild_folders[folderIndex];
folder.guild_ids.push(guildId);
folder.guild_ids = [...new Set(folder.guild_ids)];
folder.guild_ids = this._move(
folder.guild_ids,
folder.guild_ids.findIndex((v) => v == guildId),
newPosition,
);
}
this.edit({ guild_folders: this.rawSetting.guild_folders });
}
}
module.exports = ClientUserSettingManager;

View File

@@ -1,75 +1,75 @@
'use strict'; 'use strict';
const CachedManager = require('./CachedManager'); const CachedManager = require('./CachedManager');
const GuildMember = require('../structures/GuildMember'); const GuildMember = require('../structures/GuildMember');
const Message = require('../structures/Message'); const Message = require('../structures/Message');
const ThreadMember = require('../structures/ThreadMember'); const ThreadMember = require('../structures/ThreadMember');
const User = require('../structures/User'); const User = require('../structures/User');
/** /**
* Manages API methods for users and stores their cache. * Manages API methods for users and stores their cache.
* @extends {CachedManager} * @extends {CachedManager}
*/ */
class FriendsManager extends CachedManager { class FriendsManager extends CachedManager {
constructor(client, iterable) { constructor(client, iterable) {
super(client, User, iterable); super(client, User, iterable);
} }
/** /**
* The cache of this manager * The cache of this manager
* @type {Collection<Snowflake, User>} * @type {Collection<Snowflake, User>}
* @name FriendsManager#cache * @name FriendsManager#cache
*/ */
/** /**
* Data that resolves to give a User object. This can be: * Data that resolves to give a User object. This can be:
* * A User object * * A User object
* * A Snowflake * * A Snowflake
* * A Message object (resolves to the message author) * * A Message object (resolves to the message author)
* * A GuildMember object * * A GuildMember object
* * A ThreadMember object * * A ThreadMember object
* @typedef {User|Snowflake|Message|GuildMember|ThreadMember} UserResolvable * @typedef {User|Snowflake|Message|GuildMember|ThreadMember} UserResolvable
*/ */
/** /**
* Resolves a {@link UserResolvable} to a {@link User} object. * Resolves a {@link UserResolvable} to a {@link User} object.
* @param {UserResolvable} user The UserResolvable to identify * @param {UserResolvable} user The UserResolvable to identify
* @returns {?User} * @returns {?User}
*/ */
resolve(user) { resolve(user) {
if (user instanceof GuildMember || user instanceof ThreadMember) return user.user; if (user instanceof GuildMember || user instanceof ThreadMember) return user.user;
if (user instanceof Message) return user.author; if (user instanceof Message) return user.author;
return super.resolve(user); return super.resolve(user);
} }
/** /**
* Resolves a {@link UserResolvable} to a {@link User} id. * Resolves a {@link UserResolvable} to a {@link User} id.
* @param {UserResolvable} user The UserResolvable to identify * @param {UserResolvable} user The UserResolvable to identify
* @returns {?Snowflake} * @returns {?Snowflake}
*/ */
resolveId(user) { resolveId(user) {
if (user instanceof ThreadMember) return user.id; if (user instanceof ThreadMember) return user.id;
if (user instanceof GuildMember) return user.user.id; if (user instanceof GuildMember) return user.user.id;
if (user instanceof Message) return user.author.id; if (user instanceof Message) return user.author.id;
return super.resolveId(user); return super.resolveId(user);
} }
/** /**
* Obtains a user from Discord, or the user cache if it's already available. * Obtains a user from Discord, or the user cache if it's already available.
* @param {UserResolvable} user The user to fetch * @param {UserResolvable} user The user to fetch
* @param {BaseFetchOptions} [options] Additional options for this fetch * @param {BaseFetchOptions} [options] Additional options for this fetch
* @returns {Promise<User>} * @returns {Promise<User>}
*/ */
async fetch(user, { cache = true, force = false } = {}) { async fetch(user, { cache = true, force = false } = {}) {
const id = this.resolveId(user); const id = this.resolveId(user);
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;
} }
const data = await this.client.api.users(id).get(); const data = await this.client.api.users(id).get();
return this._add(data, cache); return this._add(data, cache);
} }
} }
module.exports = FriendsManager; module.exports = FriendsManager;

View File

@@ -419,7 +419,12 @@ class GuildMemberManager extends CachedManager {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!query && !user_ids) query = ''; if (!query && !user_ids) query = '';
if (nonce.length > 32) throw new RangeError('MEMBER_FETCH_NONCE_LENGTH'); if (nonce.length > 32) throw new RangeError('MEMBER_FETCH_NONCE_LENGTH');
if (this.guild.me.permissions.has('ADMINISTRATOR') || this.guild.me.permissions.has('KICK_MEMBERS') || this.guild.me.permissions.has('BAN_MEMBERS') || this.guild.me.permissions.has('MANAGE_ROLES')) { if (
this.guild.me.permissions.has('ADMINISTRATOR') ||
this.guild.me.permissions.has('KICK_MEMBERS') ||
this.guild.me.permissions.has('BAN_MEMBERS') ||
this.guild.me.permissions.has('MANAGE_ROLES')
) {
this.guild.shard.send({ this.guild.shard.send({
op: Opcodes.REQUEST_GUILD_MEMBERS, op: Opcodes.REQUEST_GUILD_MEMBERS,
d: { d: {
@@ -435,20 +440,28 @@ class GuildMemberManager extends CachedManager {
let channel; let channel;
let channels = this.guild.channels.cache.filter(c => c.isText()); let channels = this.guild.channels.cache.filter(c => c.isText());
channels = channels.filter(c => c.permissionsFor(this.guild.me).has('VIEW_CHANNEL')); channels = channels.filter(c => c.permissionsFor(this.guild.me).has('VIEW_CHANNEL'));
if (!channels.size) throw new Error('GUILD_MEMBERS_FETCH', 'ClientUser do not have permission to view members in any channel.'); if (!channels.size)
const channels_allowed_everyone = channels.filter((c) => throw new Error('GUILD_MEMBERS_FETCH', 'ClientUser do not have permission to view members in any channel.');
const channels_allowed_everyone = channels.filter(c =>
c.permissionsFor(this.guild.roles.everyone).has('VIEW_CHANNEL'), c.permissionsFor(this.guild.roles.everyone).has('VIEW_CHANNEL'),
); );
channel = channels_allowed_everyone.first() ?? channels.first(); channel = channels_allowed_everyone.first() ?? channels.first();
// create array limit [0, 99] // create array limit [0, 99]
const list = []; const list = [];
let allMember = this.guild.memberCount; const allMember = this.guild.memberCount;
if (allMember < 100) { if (allMember < 100) {
list.push([[0, 99]]); list.push([[0, 99]]);
} else if (allMember < 200) { } else if (allMember < 200) {
list.push([[0, 99], [100, 199]]); list.push([
[0, 99],
[100, 199],
]);
} else if (allMember < 300) { } else if (allMember < 300) {
list.push([[0, 99], [100, 199], [200, 299]]); list.push([
[0, 99],
[100, 199],
[200, 299],
]);
} else { } else {
let x = 100; let x = 100;
for (let i = 0; i < allMember; i++) { for (let i = 0; i < allMember; i++) {
@@ -464,20 +477,22 @@ class GuildMemberManager extends CachedManager {
x = x + 200; x = x + 200;
} }
} }
Promise.all(list.map(async (l) => { Promise.all(
this.guild.shard.send({ list.map(async l => {
op: Opcodes.LAZY_REQUEST, this.guild.shard.send({
d: { op: Opcodes.LAZY_REQUEST,
guild_id: this.guild.id, d: {
typing: true, guild_id: this.guild.id,
threads: false, typing: true,
activities: true, threads: false,
channels: { activities: true,
[channel.id]: l, channels: {
[channel.id]: l,
},
}, },
}, });
}); }),
})) );
} }
const fetchedMembers = new Collection(); const fetchedMembers = new Collection();
let i = 0; let i = 0;

View File

@@ -268,7 +268,7 @@ class GuildScheduledEventManager extends CachedManager {
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; const { 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 },

View File

@@ -1,247 +1,244 @@
'use strict'; 'use strict';
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
const CachedManager = require('./CachedManager'); const CachedManager = require('./CachedManager');
const { TypeError, Error } = require('../errors'); const { TypeError, Error } = require('../errors');
const { Message } = require('../structures/Message'); const { Message } = require('../structures/Message');
const MessagePayload = require('../structures/MessagePayload'); const MessagePayload = require('../structures/MessagePayload');
const Util = require('../util/Util'); const Util = require('../util/Util');
const BigNumber = require('bignumber.js'); const BigNumber = require('bignumber.js');
/** /**
* Manages API methods for Messages and holds their cache. * Manages API methods for Messages and holds their cache.
* @extends {CachedManager} * @extends {CachedManager}
*/ */
class MessageManager extends CachedManager { class MessageManager extends CachedManager {
constructor(channel, iterable) { constructor(channel, iterable) {
super(channel.client, Message, iterable); super(channel.client, Message, iterable);
/** /**
* The channel that the messages belong to * The channel that the messages belong to
* @type {TextBasedChannels} * @type {TextBasedChannels}
*/ */
this.channel = channel; this.channel = channel;
} }
/** /**
* The cache of Messages * The cache of Messages
* @type {Collection<Snowflake, Message>} * @type {Collection<Snowflake, Message>}
* @name MessageManager#cache * @name MessageManager#cache
*/ */
_add(data, cache) { _add(data, cache) {
return super._add(data, cache); return super._add(data, cache);
} }
/** /**
* The parameters to pass in when requesting previous messages from a channel. `around`, `before` and * The parameters to pass in when requesting previous messages from a channel. `around`, `before` and
* `after` are mutually exclusive. All the parameters are optional. * `after` are mutually exclusive. All the parameters are optional.
* @typedef {Object} ChannelLogsQueryOptions * @typedef {Object} ChannelLogsQueryOptions
* @property {number} [limit=50] Number of messages to acquire * @property {number} [limit=50] Number of messages to acquire
* @property {Snowflake} [before] The message's id to get the messages that were posted before it * @property {Snowflake} [before] The message's id to get the messages that were posted before it
* @property {Snowflake} [after] The message's id to get the messages that were posted after it * @property {Snowflake} [after] The message's id to get the messages that were posted after it
* @property {Snowflake} [around] The message's id to get the messages that were posted around it * @property {Snowflake} [around] The message's id to get the messages that were posted around it
*/ */
/** /**
* Gets a message, or messages, from this channel. * Gets a message, or messages, from this channel.
* <info>The returned Collection does not contain reaction users of the messages if they were not cached. * <info>The returned Collection does not contain reaction users of the messages if they were not cached.
* Those need to be fetched separately in such a case.</info> * Those need to be fetched separately in such a case.</info>
* @param {Snowflake|ChannelLogsQueryOptions} [message] The id of the message to fetch, or query parameters. * @param {Snowflake|ChannelLogsQueryOptions} [message] The id of the message to fetch, or query parameters.
* @param {BaseFetchOptions} [options] Additional options for this fetch * @param {BaseFetchOptions} [options] Additional options for this fetch
* @returns {Promise<Message|Collection<Snowflake, Message>>} * @returns {Promise<Message|Collection<Snowflake, Message>>}
* @example * @example
* // Get message * // Get message
* channel.messages.fetch('99539446449315840') * channel.messages.fetch('99539446449315840')
* .then(message => console.log(message.content)) * .then(message => console.log(message.content))
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Get messages * // Get messages
* channel.messages.fetch({ limit: 10 }) * channel.messages.fetch({ limit: 10 })
* .then(messages => console.log(`Received ${messages.size} messages`)) * .then(messages => console.log(`Received ${messages.size} messages`))
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Get messages and filter by user id * // Get messages and filter by user id
* channel.messages.fetch() * channel.messages.fetch()
* .then(messages => console.log(`${messages.filter(m => m.author.id === '84484653687267328').size} messages`)) * .then(messages => console.log(`${messages.filter(m => m.author.id === '84484653687267328').size} messages`))
* .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);
} }
/** /**
* Fetches the pinned messages of this channel and returns a collection of them. * Fetches the pinned messages of this channel and returns a collection of them.
* <info>The returned Collection does not contain any reaction data of the messages. * <info>The returned Collection does not contain any reaction data of the messages.
* Those need to be fetched separately.</info> * Those need to be fetched separately.</info>
* @param {boolean} [cache=true] Whether to cache the message(s) * @param {boolean} [cache=true] Whether to cache the message(s)
* @returns {Promise<Collection<Snowflake, Message>>} * @returns {Promise<Collection<Snowflake, Message>>}
* @example * @example
* // Get pinned messages * // Get pinned messages
* channel.messages.fetchPinned() * channel.messages.fetchPinned()
* .then(messages => console.log(`Received ${messages.size} messages`)) * .then(messages => console.log(`Received ${messages.size} messages`))
* .catch(console.error); * .catch(console.error);
*/ */
async fetchPinned(cache = true) { async fetchPinned(cache = true) {
const data = await this.client.api.channels[this.channel.id].pins.get(); const data = await this.client.api.channels[this.channel.id].pins.get();
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;
} }
/** /**
* Data that can be resolved to a Message object. This can be: * Data that can be resolved to a Message object. This can be:
* * A Message * * A Message
* * A Snowflake * * A Snowflake
* @typedef {Message|Snowflake} MessageResolvable * @typedef {Message|Snowflake} MessageResolvable
*/ */
/** /**
* Resolves a {@link MessageResolvable} to a {@link Message} object. * Resolves a {@link MessageResolvable} to a {@link Message} object.
* @method resolve * @method resolve
* @memberof MessageManager * @memberof MessageManager
* @instance * @instance
* @param {MessageResolvable} message The message resolvable to resolve * @param {MessageResolvable} message The message resolvable to resolve
* @returns {?Message} * @returns {?Message}
*/ */
/** /**
* Resolves a {@link MessageResolvable} to a {@link Message} id. * Resolves a {@link MessageResolvable} to a {@link Message} id.
* @method resolveId * @method resolveId
* @memberof MessageManager * @memberof MessageManager
* @instance * @instance
* @param {MessageResolvable} message The message resolvable to resolve * @param {MessageResolvable} message The message resolvable to resolve
* @returns {?Snowflake} * @returns {?Snowflake}
*/ */
/** /**
* Edits a message, even if it's not cached. * Edits a message, even if it's not cached.
* @param {MessageResolvable} message The message to edit * @param {MessageResolvable} message The message to edit
* @param {string|MessageEditOptions|MessagePayload} options The options to edit the message * @param {string|MessageEditOptions|MessagePayload} options The options to edit the message
* @returns {Promise<Message>} * @returns {Promise<Message>}
*/ */
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');
let messagePayload; let messagePayload;
if (options instanceof MessagePayload) { if (options instanceof MessagePayload) {
messagePayload = await options.resolveData(); messagePayload = await options.resolveData();
} else { } else {
messagePayload = await MessagePayload.create( messagePayload = await MessagePayload.create(message instanceof Message ? message : this, options).resolveData();
message instanceof Message ? message : this, }
options, const { data, files } = await messagePayload.resolveFiles();
).resolveData(); const d = await this.client.api.channels[this.channel.id].messages[messageId].patch({ data, files });
}
const { data, files } = await messagePayload.resolveFiles(); const existing = this.cache.get(messageId);
const d = await this.client.api.channels[this.channel.id].messages[messageId].patch({ data, files }); if (existing) {
const clone = existing._clone();
const existing = this.cache.get(messageId); clone._patch(d);
if (existing) { return clone;
const clone = existing._clone(); }
clone._patch(d); return this._add(d);
return clone; }
}
return this._add(d); /**
} * Publishes a message in an announcement channel to all channels following it, even if it's not cached.
* @param {MessageResolvable} message The message to publish
/** * @returns {Promise<Message>}
* Publishes a message in an announcement channel to all channels following it, even if it's not cached. */
* @param {MessageResolvable} message The message to publish async crosspost(message) {
* @returns {Promise<Message>} message = this.resolveId(message);
*/ if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
async crosspost(message) {
message = this.resolveId(message); const data = await this.client.api.channels(this.channel.id).messages(message).crosspost.post();
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); return this.cache.get(data.id) ?? this._add(data);
}
const data = await this.client.api.channels(this.channel.id).messages(message).crosspost.post();
return this.cache.get(data.id) ?? this._add(data); /**
} * Pins a message to the channel's pinned messages, even if it's not cached.
* @param {MessageResolvable} message The message to pin
/** * @returns {Promise<void>}
* Pins a message to the channel's pinned messages, even if it's not cached. */
* @param {MessageResolvable} message The message to pin async pin(message) {
* @returns {Promise<void>} message = this.resolveId(message);
*/ if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
async pin(message) {
message = this.resolveId(message); await this.client.api.channels(this.channel.id).pins(message).put();
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); }
await this.client.api.channels(this.channel.id).pins(message).put(); /**
} * Unpins a message from the channel's pinned messages, even if it's not cached.
* @param {MessageResolvable} message The message to unpin
/** * @returns {Promise<void>}
* Unpins a message from the channel's pinned messages, even if it's not cached. */
* @param {MessageResolvable} message The message to unpin async unpin(message) {
* @returns {Promise<void>} message = this.resolveId(message);
*/ if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
async unpin(message) {
message = this.resolveId(message); await this.client.api.channels(this.channel.id).pins(message).delete();
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); }
await this.client.api.channels(this.channel.id).pins(message).delete(); /**
} * Adds a reaction to a message, even if it's not cached.
* @param {MessageResolvable} message The message to react to
/** * @param {EmojiIdentifierResolvable} emoji The emoji to react with
* Adds a reaction to a message, even if it's not cached. * @returns {Promise<void>}
* @param {MessageResolvable} message The message to react to */
* @param {EmojiIdentifierResolvable} emoji The emoji to react with async react(message, emoji) {
* @returns {Promise<void>} message = this.resolveId(message);
*/ if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
async react(message, emoji) {
message = this.resolveId(message); emoji = Util.resolvePartialEmoji(emoji);
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); if (!emoji) throw new TypeError('EMOJI_TYPE', 'emoji', 'EmojiIdentifierResolvable');
emoji = Util.resolvePartialEmoji(emoji); const emojiId = emoji.id
if (!emoji) throw new TypeError('EMOJI_TYPE', 'emoji', 'EmojiIdentifierResolvable'); ? `${emoji.animated ? 'a:' : ''}${emoji.name}:${emoji.id}`
: encodeURIComponent(emoji.name);
const emojiId = emoji.id
? `${emoji.animated ? 'a:' : ''}${emoji.name}:${emoji.id}` // eslint-disable-next-line newline-per-chained-call
: encodeURIComponent(emoji.name); await this.client.api.channels(this.channel.id).messages(message).reactions(emojiId, '@me').put();
}
// eslint-disable-next-line newline-per-chained-call
await this.client.api.channels(this.channel.id).messages(message).reactions(emojiId, '@me').put(); /**
} * Deletes a message, even if it's not cached.
* @param {MessageResolvable} message The message to delete
/** * @returns {Promise<void>}
* Deletes a message, even if it's not cached. */
* @param {MessageResolvable} message The message to delete async delete(message) {
* @returns {Promise<void>} message = this.resolveId(message);
*/ if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
async delete(message) {
message = this.resolveId(message); await this.client.api.channels(this.channel.id).messages(message).delete();
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); }
await this.client.api.channels(this.channel.id).messages(message).delete(); async _fetchId(messageId, cache, force) {
} if (!force) {
const existing = this.cache.get(messageId);
async _fetchId(messageId, cache, force) { if (existing && !existing.partial) return existing;
if (!force) { }
const existing = this.cache.get(messageId);
if (existing && !existing.partial) return existing; // const data = await this.client.api.channels[this.channel.id].messages[messageId].get(); // Discord Block
} // https://canary.discord.com/api/v9/guilds/809133733591384155/messages/search?channel_id=840225732902518825&max_id=957254525360697375&min_id=957254525360697373
const data = (
// const data = await this.client.api.channels[this.channel.id].messages[messageId].get(); // Discord Block await this.client.api.guilds[this.channel.guild.id].messages.search.get({
// https://canary.discord.com/api/v9/guilds/809133733591384155/messages/search?channel_id=840225732902518825&max_id=957254525360697375&min_id=957254525360697373 query: {
const data = ( channel_id: this.channel.id,
await this.client.api.guilds[this.channel.guild.id].messages.search.get({ max_id: new BigNumber.BigNumber(messageId).plus(1).toString(),
query: { min_id: new BigNumber.BigNumber(messageId).minus(1).toString(),
channel_id: this.channel.id, },
max_id: new BigNumber.BigNumber(messageId).plus(1).toString(), })
min_id: new BigNumber.BigNumber(messageId).minus(1).toString(), ).messages[0];
}, if (data) return this._add(data[0], cache);
}) else throw new Error('MESSAGE_ID_NOT_FOUND');
).messages[0] }
if (data) return this._add(data[0], cache);
else throw new Error('MESSAGE_ID_NOT_FOUND'); async _fetchMany(options = {}, cache) {
} const data = await this.client.api.channels[this.channel.id].messages.get({ query: options });
const messages = new Collection();
async _fetchMany(options = {}, cache) { for (const message of data) messages.set(message.id, this._add(message, cache));
const data = await this.client.api.channels[this.channel.id].messages.get({ query: options }); return messages;
const messages = new Collection(); }
for (const message of data) messages.set(message.id, this._add(message, cache)); }
return messages;
} module.exports = MessageManager;
}
module.exports = MessageManager;

View File

@@ -89,7 +89,7 @@ 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); const 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 = this.channel.guild.roles.resolve(userOrRole) ?? this.client.users.resolve(userOrRole);

View File

@@ -3,7 +3,7 @@
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
const CachedManager = require('./CachedManager'); const CachedManager = require('./CachedManager');
const { Error } = require('../errors'); const { Error } = require('../errors');
const Discord = require("discord.js-selfbot-v13") const Discord = require('discord.js-selfbot-v13');
/** /**
* Manages API methods for users who reacted to a reaction and stores their cache. * Manages API methods for users who reacted to a reaction and stores their cache.
* @extends {CachedManager} * @extends {CachedManager}

View File

@@ -11,105 +11,103 @@ const { RelationshipTypes } = require('../util/Constants');
* Manages API methods for users and stores their cache. * Manages API methods for users and stores their cache.
*/ */
class RelationshipsManager { class RelationshipsManager {
constructor(client, users) { constructor(client, users) {
this.client = client; this.client = client;
this.cache = new Collection(); this.cache = new Collection();
this._setup(users); this._setup(users);
}
_setup(users) {
if (!Array.isArray(users)) return;
for (const relationShip of users) {
this.cache.set(relationShip.id, relationShip.type);
}
}
/**
* Resolves a {@link UserResolvable} to a {@link User} id.
* @param {UserResolvable} user The UserResolvable to identify
* @returns {?Snowflake}
*/
resolveId(user) {
if (user instanceof ThreadMember) return user.id;
if (user instanceof GuildMember) return user.user.id;
if (user instanceof Message) return user.author.id;
if (user instanceof User) return user.id;
return user;
}
/**
* Obtains a user from Discord, or the user cache if it's already available.
* @param {UserResolvable} user The user to fetch
* @param {BaseFetchOptions} [options] Additional options for this fetch
* @returns {Promise<User>}
*/
async fetch(user, { cache = true, force = false } = {}) {
const id = this.resolveId(user);
if (!force) {
const existing = this.cache.get(id);
if (existing && !existing.partial) return existing;
} }
_setup(users) { const data = await this.client.api.users['@me'].relationships.get();
if (!Array.isArray(users)) return; await this._setup(data);
for (const relationShip of users) { return this.cache.get(id);
this.cache.set(relationShip.id, relationShip.type); }
}
}
/** // some option .-.
* Resolves a {@link UserResolvable} to a {@link User} id.
* @param {UserResolvable} user The UserResolvable to identify
* @returns {?Snowflake}
*/
resolveId(user) {
if (user instanceof ThreadMember) return user.id;
if (user instanceof GuildMember) return user.user.id;
if (user instanceof Message) return user.author.id;
if (user instanceof User) return user.id;
return user;
}
/** async deleteFriend(user) {
* Obtains a user from Discord, or the user cache if it's already available. const id = this.resolveId(user);
* @param {UserResolvable} user The user to fetch // check if already friends
* @param {BaseFetchOptions} [options] Additional options for this fetch if (this.cache.get(id) !== RelationshipTypes.FRIEND) return false;
* @returns {Promise<User>} await this.client.api.users['@me'].relationships[id].delete(); // 204 status and no data
*/ return true;
async fetch(user, { cache = true, force = false } = {}) { }
const id = this.resolveId(user);
if (!force) {
const existing = this.cache.get(id);
if (existing && !existing.partial) return existing;
}
const data = await this.client.api.users['@me'].relationships.get(); async deleteBlocked(user) {
await this._setup(data); const id = this.resolveId(user);
return this.cache.get(id); // check if already blocked
} if (this.cache.get(id) !== RelationshipTypes.BLOCKED) return false;
await this.client.api.users['@me'].relationships[id].delete(); // 204 status and no data
return true;
}
// some option .-. async sendFriendRequest(username, discriminator) {
await this.client.api.users('@me').relationships.post({
data: {
username,
discriminator: parseInt(discriminator),
},
});
return true;
}
async deleteFriend(user) { async addFriend(user) {
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 false; if (this.cache.get(id) === RelationshipTypes.FRIEND) return false;
await this.client.api.users['@me'].relationships[id].delete(); // 204 status and no data // check if outgoing request
return true; if (this.cache.get(id) === RelationshipTypes.OUTGOING_REQUEST) return false;
} await this.client.api.users['@me'].relationships[id].put({
data: {
type: RelationshipTypes.FRIEND,
},
});
return true;
}
async deleteBlocked(user) { async addBlocked(user) {
const id = this.resolveId(user); const id = this.resolveId(user);
// check if already blocked // check
if (this.cache.get(id) !== RelationshipTypes.BLOCKED) return false; if (this.cache.get(id) === RelationshipTypes.BLOCKED) return false;
await this.client.api.users['@me'].relationships[id].delete(); // 204 status and no data await this.client.api.users['@me'].relationships[id].put({
return true; data: {
} type: RelationshipTypes.BLOCKED,
},
async sendFriendRequest(username, discriminator) { });
await this.client.api.users('@me').relationships.post({ return true;
data: { }
username,
discriminator: parseInt(discriminator),
},
});
return true;
}
async addFriend(user) {
const id = this.resolveId(user);
// check if already friends
if (this.cache.get(id) === RelationshipTypes.FRIEND) return false;
// check if outgoing request
if (this.cache.get(id) === RelationshipTypes.OUTGOING_REQUEST) return false;
await this.client.api
.users['@me'].relationships[id].put({
data: {
type: RelationshipTypes.FRIEND,
},
});
return true;
}
async addBlocked(user) {
const id = this.resolveId(user);
// check
if (this.cache.get(id) === RelationshipTypes.BLOCKED) return false;
await this.client.api
.users['@me'].relationships[id].put({
data: {
type: RelationshipTypes.BLOCKED,
},
});
return true;
}
} }
module.exports = RelationshipsManager; module.exports = RelationshipsManager;

View File

@@ -93,7 +93,10 @@ class UserManager extends CachedManager {
const data = await this.client.api.users(id).get(); const data = await this.client.api.users(id).get();
const userObject = this._add(data, cache); const userObject = this._add(data, cache);
await userObject.getProfile().catch(() => {}); await userObject.getProfile().catch(() => {});
const noteObject = await this.client.api.users['@me'].notes(id).get().catch(() => null); const noteObject = await this.client.api.users['@me']
.notes(id)
.get()
.catch(() => null);
userObject.note = noteObject?.note ?? null; userObject.note = noteObject?.note ?? null;
return userObject; return userObject;
} }

View File

@@ -11,8 +11,8 @@ const {
} = require('../util/Constants'); } = require('../util/Constants');
function parseResponse(res) { function parseResponse(res) {
if (res.headers.get('content-type').startsWith('application/json')) return res.json(); if (res.headers.get('content-type').startsWith('application/json')) return res.json();
return res.arrayBuffer(); // Cre: TheDevYellowy return res.arrayBuffer(); // Cre: TheDevYellowy
} }
function getAPIOffset(serverDate) { function getAPIOffset(serverDate) {

File diff suppressed because it is too large Load Diff

View File

@@ -1,103 +1,103 @@
'use strict'; 'use strict';
const { TypeError } = require('../errors'); const { TypeError } = require('../errors');
const { MessageComponentTypes, Events } = require('../util/Constants'); const { MessageComponentTypes, Events } = require('../util/Constants');
/** /**
* Represents an interactive component of a Message. It should not be necessary to construct this directly. * Represents an interactive component of a Message. It should not be necessary to construct this directly.
* See {@link MessageComponent} * See {@link MessageComponent}
*/ */
class BaseMessageComponent { class BaseMessageComponent {
/** /**
* Options for a BaseMessageComponent * Options for a BaseMessageComponent
* @typedef {Object} BaseMessageComponentOptions * @typedef {Object} BaseMessageComponentOptions
* @property {MessageComponentTypeResolvable} type The type of this component * @property {MessageComponentTypeResolvable} type The type of this component
*/ */
/** /**
* Data that can be resolved into options for a MessageComponent. This can be: * Data that can be resolved into options for a MessageComponent. This can be:
* * MessageActionRowOptions * * MessageActionRowOptions
* * MessageButtonOptions * * MessageButtonOptions
* * MessageSelectMenuOptions * * MessageSelectMenuOptions
* @typedef {MessageActionRowOptions|MessageButtonOptions|MessageSelectMenuOptions} MessageComponentOptions * @typedef {MessageActionRowOptions|MessageButtonOptions|MessageSelectMenuOptions} MessageComponentOptions
*/ */
/** /**
* Components that can be sent in a message. These can be: * Components that can be sent in a message. These can be:
* * MessageActionRow * * MessageActionRow
* * MessageButton * * MessageButton
* * MessageSelectMenu * * MessageSelectMenu
* @typedef {MessageActionRow|MessageButton|MessageSelectMenu} MessageComponent * @typedef {MessageActionRow|MessageButton|MessageSelectMenu} MessageComponent
* @see {@link https://discord.com/developers/docs/interactions/message-components#component-object-component-types} * @see {@link https://discord.com/developers/docs/interactions/message-components#component-object-component-types}
*/ */
/** /**
* Data that can be resolved to a MessageComponentType. This can be: * Data that can be resolved to a MessageComponentType. This can be:
* * MessageComponentType * * MessageComponentType
* * string * * string
* * number * * number
* @typedef {string|number|MessageComponentType} MessageComponentTypeResolvable * @typedef {string|number|MessageComponentType} MessageComponentTypeResolvable
*/ */
/** /**
* @param {BaseMessageComponent|BaseMessageComponentOptions} [data={}] The options for this component * @param {BaseMessageComponent|BaseMessageComponentOptions} [data={}] The options for this component
*/ */
constructor(data) { constructor(data) {
/** /**
* The type of this component * The type of this component
* @type {?MessageComponentType} * @type {?MessageComponentType}
*/ */
this.type = 'type' in data ? BaseMessageComponent.resolveType(data.type) : null; this.type = 'type' in data ? BaseMessageComponent.resolveType(data.type) : null;
} }
/** /**
* Constructs a MessageComponent based on the type of the incoming data * Constructs a MessageComponent based on the type of the incoming data
* @param {MessageComponentOptions} data Data for a MessageComponent * @param {MessageComponentOptions} data Data for a MessageComponent
* @param {Client|WebhookClient} [client] Client constructing this component * @param {Client|WebhookClient} [client] Client constructing this component
* @returns {?MessageComponent} * @returns {?MessageComponent}
* @private * @private
*/ */
static create(data, client) { static create(data, client) {
let component; let component;
let type = data.type; let type = data.type;
if (typeof type === 'string') type = MessageComponentTypes[type]; if (typeof type === 'string') type = MessageComponentTypes[type];
switch (type) { switch (type) {
case MessageComponentTypes.ACTION_ROW: { case MessageComponentTypes.ACTION_ROW: {
const MessageActionRow = require('./MessageActionRow'); const MessageActionRow = require('./MessageActionRow');
component = data instanceof MessageActionRow ? data : new MessageActionRow(data, client); component = data instanceof MessageActionRow ? data : new MessageActionRow(data, client);
break; break;
} }
case MessageComponentTypes.BUTTON: { case MessageComponentTypes.BUTTON: {
const MessageButton = require('./MessageButton'); const MessageButton = require('./MessageButton');
component = data instanceof MessageButton ? data : new MessageButton(data); component = data instanceof MessageButton ? data : new MessageButton(data);
break; break;
} }
case MessageComponentTypes.SELECT_MENU: { case MessageComponentTypes.SELECT_MENU: {
const MessageSelectMenu = require('./MessageSelectMenu'); const MessageSelectMenu = require('./MessageSelectMenu');
component = data instanceof MessageSelectMenu ? data : new MessageSelectMenu(data); component = data instanceof MessageSelectMenu ? data : new MessageSelectMenu(data);
break; break;
} }
default: default:
if (client) { if (client) {
client.emit(Events.DEBUG, `[BaseMessageComponent] Received component with unknown type: ${data.type}`); client.emit(Events.DEBUG, `[BaseMessageComponent] Received component with unknown type: ${data.type}`);
} else { } else {
throw new TypeError('INVALID_TYPE', 'data.type', 'valid MessageComponentType'); throw new TypeError('INVALID_TYPE', 'data.type', 'valid MessageComponentType');
} }
} }
return component; return component;
} }
/** /**
* Resolves the type of a MessageComponent * Resolves the type of a MessageComponent
* @param {MessageComponentTypeResolvable} type The type to resolve * @param {MessageComponentTypeResolvable} type The type to resolve
* @returns {MessageComponentType} * @returns {MessageComponentType}
* @private * @private
*/ */
static resolveType(type) { static resolveType(type) {
return typeof type === 'string' ? type : MessageComponentTypes[type]; return typeof type === 'string' ? type : MessageComponentTypes[type];
} }
} }
module.exports = BaseMessageComponent; module.exports = BaseMessageComponent;

View File

@@ -1,275 +1,284 @@
'use strict'; 'use strict';
const process = require('node:process'); const process = require('node:process');
const Base = require('./Base'); const Base = require('./Base');
let CategoryChannel; let CategoryChannel;
let DMChannel; let DMChannel;
let NewsChannel; let NewsChannel;
let StageChannel; let StageChannel;
let StoreChannel; let StoreChannel;
let TextChannel; let TextChannel;
let ThreadChannel; let ThreadChannel;
let VoiceChannel; let VoiceChannel;
const { ChannelTypes, ThreadChannelTypes, VoiceBasedChannelTypes } = require('../util/Constants'); const { ChannelTypes, ThreadChannelTypes, VoiceBasedChannelTypes } = require('../util/Constants');
const SnowflakeUtil = require('../util/SnowflakeUtil'); const SnowflakeUtil = require('../util/SnowflakeUtil');
const { Message } = require('discord.js'); const { Message } = require('discord.js');
//const { ApplicationCommand } = require('discord.js-selfbot-v13'); - Not being used in this file, not necessary. // const { ApplicationCommand } = require('discord.js-selfbot-v13'); - Not being used in this file, not necessary.
/** /**
* @type {WeakSet<Channel>} * @type {WeakSet<Channel>}
* @private * @private
* @internal * @internal
*/ */
const deletedChannels = new WeakSet(); const deletedChannels = new WeakSet();
let deprecationEmittedForDeleted = false; let deprecationEmittedForDeleted = false;
/** /**
* Represents any channel on Discord. * Represents any channel on Discord.
* @extends {Base} * @extends {Base}
* @abstract * @abstract
*/ */
class Channel extends Base { class Channel extends Base {
constructor(client, data, immediatePatch = true) { constructor(client, data, immediatePatch = true) {
super(client); super(client);
const type = ChannelTypes[data?.type]; const type = ChannelTypes[data?.type];
/** /**
* The type of the channel * The type of the channel
* @type {ChannelType} * @type {ChannelType}
*/ */
this.type = type ?? 'UNKNOWN'; this.type = type ?? 'UNKNOWN';
if (data && immediatePatch) this._patch(data); if (data && immediatePatch) this._patch(data);
} }
_patch(data) { _patch(data) {
/** /**
* The channel's id * The channel's id
* @type {Snowflake} * @type {Snowflake}
*/ */
this.id = data.id; this.id = data.id;
} }
/** /**
* The timestamp the channel was created at * The timestamp the channel was created at
* @type {number} * @type {number}
* @readonly * @readonly
*/ */
get createdTimestamp() { get createdTimestamp() {
return SnowflakeUtil.timestampFrom(this.id); return SnowflakeUtil.timestampFrom(this.id);
} }
/** /**
* The time the channel was created at * The time the channel was created at
* @type {Date} * @type {Date}
* @readonly * @readonly
*/ */
get createdAt() { get createdAt() {
return new Date(this.createdTimestamp); return new Date(this.createdTimestamp);
} }
/** /**
* Whether or not the structure has been deleted * Whether or not the structure has been deleted
* @type {boolean} * @type {boolean}
* @deprecated This will be removed in the next major version, see https://github.com/discordjs/discord.js/issues/7091 * @deprecated This will be removed in the next major version, see https://github.com/discordjs/discord.js/issues/7091
*/ */
get deleted() { get deleted() {
if (!deprecationEmittedForDeleted) { if (!deprecationEmittedForDeleted) {
deprecationEmittedForDeleted = true; deprecationEmittedForDeleted = true;
process.emitWarning( process.emitWarning(
'Channel#deleted is deprecated, see https://github.com/discordjs/discord.js/issues/7091.', 'Channel#deleted is deprecated, see https://github.com/discordjs/discord.js/issues/7091.',
'DeprecationWarning', 'DeprecationWarning',
); );
} }
return deletedChannels.has(this); return deletedChannels.has(this);
} }
set deleted(value) { set deleted(value) {
if (!deprecationEmittedForDeleted) { if (!deprecationEmittedForDeleted) {
deprecationEmittedForDeleted = true; deprecationEmittedForDeleted = true;
process.emitWarning( process.emitWarning(
'Channel#deleted is deprecated, see https://github.com/discordjs/discord.js/issues/7091.', 'Channel#deleted is deprecated, see https://github.com/discordjs/discord.js/issues/7091.',
'DeprecationWarning', 'DeprecationWarning',
); );
} }
if (value) deletedChannels.add(this); if (value) deletedChannels.add(this);
else deletedChannels.delete(this); else deletedChannels.delete(this);
} }
/** /**
* Whether this Channel is a partial * Whether this Channel is a partial
* <info>This is always false outside of DM channels.</info> * <info>This is always false outside of DM channels.</info>
* @type {boolean} * @type {boolean}
* @readonly * @readonly
*/ */
get partial() { get partial() {
return false; return false;
} }
/** /**
* When concatenated with a string, this automatically returns the channel's mention instead of the Channel object. * When concatenated with a string, this automatically returns the channel's mention instead of the Channel object.
* @returns {string} * @returns {string}
* @example * @example
* // Logs: Hello from <#123456789012345678>! * // Logs: Hello from <#123456789012345678>!
* console.log(`Hello from ${channel}!`); * console.log(`Hello from ${channel}!`);
*/ */
toString() { toString() {
return `<#${this.id}>`; return `<#${this.id}>`;
} }
/** /**
* Deletes this channel. * Deletes this channel.
* @returns {Promise<Channel>} * @returns {Promise<Channel>}
* @example * @example
* // Delete the channel * // Delete the channel
* channel.delete() * channel.delete()
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async delete() { async delete() {
await this.client.api.channels(this.id).delete(); await this.client.api.channels(this.id).delete();
return this; return this;
} }
/** /**
* Fetches this channel. * Fetches this channel.
* @param {boolean} [force=true] Whether to skip the cache check and request the API * @param {boolean} [force=true] Whether to skip the cache check and request the API
* @returns {Promise<Channel>} * @returns {Promise<Channel>}
*/ */
fetch(force = true) { fetch(force = true) {
return this.client.channels.fetch(this.id, { force }); return this.client.channels.fetch(this.id, { force });
} }
/** /**
* Indicates whether this channel is {@link TextBasedChannels text-based}. * Indicates whether this channel is {@link TextBasedChannels text-based}.
* @returns {boolean} * @returns {boolean}
*/ */
isText() { isText() {
return 'messages' in this; return 'messages' in this;
} }
/** /**
* Indicates whether this channel is {@link BaseGuildVoiceChannel voice-based}. * Indicates whether this channel is {@link BaseGuildVoiceChannel voice-based}.
* @returns {boolean} * @returns {boolean}
*/ */
isVoice() { isVoice() {
return VoiceBasedChannelTypes.includes(this.type); return VoiceBasedChannelTypes.includes(this.type);
} }
/** /**
* Indicates whether this channel is a {@link ThreadChannel}. * Indicates whether this channel is a {@link ThreadChannel}.
* @returns {boolean} * @returns {boolean}
*/ */
isThread() { isThread() {
return ThreadChannelTypes.includes(this.type); return ThreadChannelTypes.includes(this.type);
} }
static create(client, data, guild, { allowUnknownGuild, fromInteraction } = {}) { static create(client, data, guild, { allowUnknownGuild, fromInteraction } = {}) {
CategoryChannel ??= require('./CategoryChannel'); CategoryChannel ??= require('./CategoryChannel');
DMChannel ??= require('./DMChannel'); DMChannel ??= require('./DMChannel');
NewsChannel ??= require('./NewsChannel'); NewsChannel ??= require('./NewsChannel');
StageChannel ??= require('./StageChannel'); StageChannel ??= require('./StageChannel');
StoreChannel ??= require('./StoreChannel'); StoreChannel ??= require('./StoreChannel');
TextChannel ??= require('./TextChannel'); TextChannel ??= require('./TextChannel');
ThreadChannel ??= require('./ThreadChannel'); ThreadChannel ??= require('./ThreadChannel');
VoiceChannel ??= require('./VoiceChannel'); VoiceChannel ??= require('./VoiceChannel');
let channel; let channel;
if (!data.guild_id && !guild) { if (!data.guild_id && !guild) {
if ((data.recipients && data.type !== ChannelTypes.GROUP_DM) || data.type === ChannelTypes.DM) { if ((data.recipients && data.type !== ChannelTypes.GROUP_DM) || data.type === ChannelTypes.DM) {
channel = new DMChannel(client, data); channel = new DMChannel(client, data);
} else if (data.type === ChannelTypes.GROUP_DM) { } else if (data.type === ChannelTypes.GROUP_DM) {
const PartialGroupDMChannel = require('./PartialGroupDMChannel'); const PartialGroupDMChannel = require('./PartialGroupDMChannel');
channel = new PartialGroupDMChannel(client, data); channel = new PartialGroupDMChannel(client, data);
} }
} else { } else {
guild ??= client.guilds.cache.get(data.guild_id); guild ??= client.guilds.cache.get(data.guild_id);
if (guild || allowUnknownGuild) { if (guild || allowUnknownGuild) {
switch (data.type) { switch (data.type) {
case ChannelTypes.GUILD_TEXT: { case ChannelTypes.GUILD_TEXT: {
channel = new TextChannel(guild, data, client); channel = new TextChannel(guild, data, client);
break; break;
} }
case ChannelTypes.GUILD_VOICE: { case ChannelTypes.GUILD_VOICE: {
channel = new VoiceChannel(guild, data, client); channel = new VoiceChannel(guild, data, client);
break; break;
} }
case ChannelTypes.GUILD_CATEGORY: { case ChannelTypes.GUILD_CATEGORY: {
channel = new CategoryChannel(guild, data, client); channel = new CategoryChannel(guild, data, client);
break; break;
} }
case ChannelTypes.GUILD_NEWS: { case ChannelTypes.GUILD_NEWS: {
channel = new NewsChannel(guild, data, client); channel = new NewsChannel(guild, data, client);
break; break;
} }
case ChannelTypes.GUILD_STORE: { case ChannelTypes.GUILD_STORE: {
channel = new StoreChannel(guild, data, client); channel = new StoreChannel(guild, data, client);
break; break;
} }
case ChannelTypes.GUILD_STAGE_VOICE: { case ChannelTypes.GUILD_STAGE_VOICE: {
channel = new StageChannel(guild, data, client); channel = new StageChannel(guild, data, client);
break; break;
} }
case ChannelTypes.GUILD_NEWS_THREAD: case ChannelTypes.GUILD_NEWS_THREAD:
case ChannelTypes.GUILD_PUBLIC_THREAD: case ChannelTypes.GUILD_PUBLIC_THREAD:
case ChannelTypes.GUILD_PRIVATE_THREAD: { case ChannelTypes.GUILD_PRIVATE_THREAD: {
channel = new ThreadChannel(guild, data, client, fromInteraction); channel = new ThreadChannel(guild, data, client, fromInteraction);
if (!allowUnknownGuild) channel.parent?.threads.cache.set(channel.id, channel); if (!allowUnknownGuild) channel.parent?.threads.cache.set(channel.id, channel);
break; break;
} }
} }
if (channel && !allowUnknownGuild) guild.channels?.cache.set(channel.id, channel); if (channel && !allowUnknownGuild) guild.channels?.cache.set(channel.id, channel);
} }
} }
return channel; return channel;
} }
toJSON(...props) { toJSON(...props) {
return super.toJSON({ createdTimestamp: true }, ...props); return super.toJSON({ createdTimestamp: true }, ...props);
} }
// Send Slash // Send Slash
/** /**
* Send Slash to this channel * Send Slash to this channel
* @param {DiscordBot} botID Bot ID * @param {DiscordBot} botID Bot ID
* @param {String<ApplicationCommand.name>} commandName Command name * @param {String<ApplicationCommand.name>} commandName Command name
* @param {Array<ApplicationCommand.options>} args Command arguments * @param {Array<ApplicationCommand.options>} args Command arguments
* @returns {Promise<pending>} * @returns {Promise<pending>}
*/ */
async sendSlash(botID, commandName, args = []) { async sendSlash(botID, commandName, args = []) {
if (!this.isText()) throw new Error('This channel is not text-based.'); if (!this.isText()) throw new Error('This channel is not text-based.');
if(!botID) throw new Error('Bot ID is required'); if (!botID) throw new Error('Bot ID is required');
const user = await this.client.users.fetch(botID).catch(() => {}); const user = await this.client.users.fetch(botID).catch(() => {});
if (!user || !user.bot || !user.applications) throw new Error('BotID is not a bot or does not have an application slash command'); if (!user || !user.bot || !user.applications)
if (!commandName || typeof commandName !== 'string') throw new Error('Command name is required'); throw new Error('BotID is not a bot or does not have an application slash command');
const listApplication = user.applications.cache.size == 0 ? await user.applications.fetch() : user.applications.cache; if (!commandName || typeof commandName !== 'string') throw new Error('Command name is required');
let slashCommand; const listApplication =
await Promise.all(listApplication.map(async application => { user.applications.cache.size == 0 ? await user.applications.fetch() : user.applications.cache;
if (commandName == application.name && application.type == 'CHAT_INPUT') slashCommand = application; let slashCommand;
})); await Promise.all(
if (!slashCommand) throw new Error( listApplication.map(async application => {
`Command ${commandName} is not found\nList command avalible: ${listApplication.filter(a => a.type == 'CHAT_INPUT').map(a => a.name).join(', ')}`, if (commandName == application.name && application.type == 'CHAT_INPUT') slashCommand = application;
); }),
return slashCommand.sendSlashCommand( );
new Message(this.client, { if (!slashCommand) {
channel_id: this.id, throw new Error(
guild_id: this.guild?.id || null, `Command ${commandName} is not found\nList command avalible: ${listApplication
author: this.client.user, .filter(a => a.type == 'CHAT_INPUT')
content: '', .map(a => a.name)
id: this.client.user.id .join(', ')}`,
}), );
args }
); return slashCommand.sendSlashCommand(
} new Message(this.client, {
} channel_id: this.id,
guild_id: this.guild?.id || null,
exports.Channel = Channel; author: this.client.user,
exports.deletedChannels = deletedChannels; content: '',
id: this.client.user.id,
/** }),
* @external APIChannel args,
* @see {@link https://discord.com/developers/docs/resources/channel#channel-object} );
*/ }
}
exports.Channel = Channel;
exports.deletedChannels = deletedChannels;
/**
* @external APIChannel
* @see {@link https://discord.com/developers/docs/resources/channel#channel-object}
*/

View File

@@ -18,7 +18,7 @@ class ClientApplication extends Application {
* The application command manager for this application * The application command manager for this application
* @type {ApplicationCommandManager} * @type {ApplicationCommandManager}
*/ */
this.commands = null // Selfbot this.commands = null; // Selfbot
} }
_patch(data) { _patch(data) {
@@ -97,7 +97,7 @@ class ClientApplication extends Application {
* @returns {Promise<ClientApplication>} * @returns {Promise<ClientApplication>}
*/ */
async fetch() { async fetch() {
if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD"); if (!this.client.user.bot) throw new Error('INVALID_USER_METHOD');
const app = await this.client.api.oauth2.applications('@me').get(); const app = await this.client.api.oauth2.applications('@me').get();
this._patch(app); this._patch(app);
return this; return this;

View File

@@ -54,14 +54,14 @@ class ClientPresence extends Presence {
} }
} else if (!activities && (status || afk || since) && this.activities.length) { } else if (!activities && (status || afk || since) && this.activities.length) {
data.activities.push( data.activities.push(
...this.activities.map((a) => ...this.activities.map(a =>
Object.assign(a, { Object.assign(a, {
name: a.name, name: a.name,
type: a.type, type: a.type,
url: a.url ?? undefined, url: a.url ?? undefined,
}), }),
), ),
); );
} }
return data; return data;

View File

@@ -1,364 +1,368 @@
'use strict'; 'use strict';
const User = require('./User'); const User = require('./User');
const DataResolver = require('../util/DataResolver'); const DataResolver = require('../util/DataResolver');
const { HypeSquadOptions } = require('../util/Constants'); const { HypeSquadOptions } = require('../util/Constants');
const { Util } = require('..'); const { Util } = require('..');
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
/** /**
* Represents the logged in client's Discord user. * Represents the logged in client's Discord user.
* @extends {User} * @extends {User}
*/ */
class ClientUser extends User { class ClientUser extends User {
_patch(data) { _patch(data) {
super._patch(data); super._patch(data);
/* /*
Add: notes Add: notes
*/ */
this.notes = new Collection(); this.notes = new Collection();
// this.messageMentions = new Collection(); // this.messageMentions = new Collection();
if ('verified' in data) { if ('verified' in data) {
/** /**
* Whether or not this account has been verified * Whether or not this account has been verified
* @type {boolean} * @type {boolean}
*/ */
this.verified = data.verified; this.verified = data.verified;
} }
if ('mfa_enabled' in data) { if ('mfa_enabled' in data) {
/** /**
* If the bot's {@link ClientApplication#owner Owner} has MFA enabled on their account * If the bot's {@link ClientApplication#owner Owner} has MFA enabled on their account
* @type {?boolean} * @type {?boolean}
*/ */
this.mfaEnabled = this.mfaEnabled = typeof data.mfa_enabled === 'boolean' ? data.mfa_enabled : null;
typeof data.mfa_enabled === 'boolean' ? data.mfa_enabled : null; } else {
} else { this.mfaEnabled ??= null;
this.mfaEnabled ??= null; }
}
if ('token' in data) this.client.token = data.token;
if ('token' in data) this.client.token = data.token;
// Add (Selfbot)
// Add (Selfbot) if ('premium' in data) this.nitro = data.premium;
if ('premium' in data) this.nitro = data.premium; /**
/** * Nitro Status
* Nitro Status * `0`: None
* `0`: None * `1`: Classic
* `1`: Classic * `2`: Boost
* `2`: Boost * @external
* @external * https://discord.com/developers/docs/resources/user#user-object-premium-types
* https://discord.com/developers/docs/resources/user#user-object-premium-types * @type {Number}
* @type {Number} */
*/ if ('purchased_flags' in data) this.nitroType = data.purchased_flags;
if ('purchased_flags' in data) this.nitroType = data.purchased_flags; if ('phone' in data) this.phoneNumber = data.phone;
if ('phone' in data) this.phoneNumber = data.phone; if ('nsfw_allowed' in data) this.nsfwAllowed = data.nsfw_allowed;
if ('nsfw_allowed' in data) this.nsfwAllowed = data.nsfw_allowed; if ('email' in data) this.emailAddress = data.email;
if ('email' in data) this.emailAddress = data.email; }
}
/**
/** * Represents the client user's presence
* Represents the client user's presence * @type {ClientPresence}
* @type {ClientPresence} * @readonly
* @readonly */
*/ get presence() {
get presence() { return this.client.presence;
return this.client.presence; }
}
/**
/** * Data used to edit the logged in client
* Data used to edit the logged in client * @typedef {Object} ClientUserEditData
* @typedef {Object} ClientUserEditData * @property {string} [username] The new username
* @property {string} [username] The new username * @property {?(BufferResolvable|Base64Resolvable)} [avatar] The new avatar
* @property {?(BufferResolvable|Base64Resolvable)} [avatar] The new avatar */
*/
/**
/** * Edits the logged in client.
* Edits the logged in client. * @param {ClientUserEditData} data The new data
* @param {ClientUserEditData} data The new data * @returns {Promise<ClientUser>}
* @returns {Promise<ClientUser>} */
*/ async edit(data) {
async edit(data) { if (typeof data.avatar !== 'undefined') {
if (typeof data.avatar !== 'undefined') data.avatar = await DataResolver.resolveImage(data.avatar);
data.avatar = await DataResolver.resolveImage(data.avatar); }
if (typeof data.banner !== 'undefined') if (typeof data.banner !== 'undefined') {
data.banner = await DataResolver.resolveImage(data.banner); data.banner = await DataResolver.resolveImage(data.banner);
const newData = await this.client.api.users('@me').patch({ data }); }
this.client.token = newData.token; const newData = await this.client.api.users('@me').patch({ data });
this.client.password = data?.password this.client.token = newData.token;
? data?.password this.client.password = data?.password ? data?.password : this.client.password;
: this.client.password; const { updated } = this.client.actions.UserUpdate.handle(newData);
const { updated } = this.client.actions.UserUpdate.handle(newData); return updated ?? this;
return updated ?? this; }
}
/**
/** * Sets the username of the logged in client.
* Sets the username of the logged in client. * <info>Changing usernames in Discord is heavily rate limited, with only 2 requests
* <info>Changing usernames in Discord is heavily rate limited, with only 2 requests * every hour. Use this sparingly!</info>
* every hour. Use this sparingly!</info> * @param {string} username The new username
* @param {string} username The new username * @param {string} password The password of the account
* @param {string} password The password of the account * @returns {Promise<ClientUser>}
* @returns {Promise<ClientUser>} * @example
* @example * // Set username
* // Set username * client.user.setUsername('discordjs')
* client.user.setUsername('discordjs') * .then(user => console.log(`My new username is ${user.username}`))
* .then(user => console.log(`My new username is ${user.username}`)) * .catch(console.error);
* .catch(console.error); */
*/ setUsername(username, password) {
setUsername(username, password) { if (!password && !this.client.password) {
if (!password && !this.client.password) throw new Error('A password is required to change a username.');
throw new Error('A password is required to change a username.'); }
return this.edit({ return this.edit({
username, username,
password: this.client.password ? this.client.password : password, password: this.client.password ? this.client.password : password,
}); });
} }
/** /**
* Sets the avatar of the logged in client. * Sets the avatar of the logged in client.
* @param {?(BufferResolvable|Base64Resolvable)} avatar The new avatar * @param {?(BufferResolvable|Base64Resolvable)} avatar The new avatar
* @returns {Promise<ClientUser>} * @returns {Promise<ClientUser>}
* @example * @example
* // Set avatar * // Set avatar
* client.user.setAvatar('./avatar.png') * client.user.setAvatar('./avatar.png')
* .then(user => console.log(`New avatar set!`)) * .then(user => console.log(`New avatar set!`))
* .catch(console.error); * .catch(console.error);
*/ */
setAvatar(avatar) { setAvatar(avatar) {
return this.edit({ avatar }); return this.edit({ avatar });
} }
/** /**
* Sets the banner of the logged in client. * Sets the banner of the logged in client.
* @param {?(BufferResolvable|Base64Resolvable)} banner The new banner * @param {?(BufferResolvable|Base64Resolvable)} banner The new banner
* @returns {Promise<ClientUser>} * @returns {Promise<ClientUser>}
* @example * @example
* // Set banner * // Set banner
* client.user.setBanner('./banner.png') * client.user.setBanner('./banner.png')
* .then(user => console.log(`New banner set!`)) * .then(user => console.log(`New banner set!`))
* .catch(console.error); * .catch(console.error);
*/ */
setBanner(banner) { setBanner(banner) {
if (this.nitroType !== 2) if (this.nitroType !== 2) {
throw new Error( throw new Error('You must be a Nitro Boosted User to change your banner.');
'You must be a Nitro Boosted User to change your banner.', }
); return this.edit({ banner });
return this.edit({ banner }); }
}
/**
/** * Set HyperSquad House
* Set HyperSquad House * @param {HypeSquadOptions<Number|String>} type
* @param {HypeSquadOptions<Number|String>} type * `LEAVE`: 0
* `LEAVE`: 0 * `HOUSE_BRAVERY`: 1
* `HOUSE_BRAVERY`: 1 * `HOUSE_BRILLIANCE`: 2
* `HOUSE_BRILLIANCE`: 2 * `HOUSE_BALANCE`: 3
* `HOUSE_BALANCE`: 3 * @returns {Promise<void>}
* @returns {Promise<void>} * @example
* @example * // Set HyperSquad HOUSE_BRAVERY
* // Set HyperSquad HOUSE_BRAVERY * client.user.setHypeSquad(1); || client.user.setHypeSquad('HOUSE_BRAVERY');
* client.user.setHypeSquad(1); || client.user.setHypeSquad('HOUSE_BRAVERY'); * // Leave
* // Leave * client.user.setHypeSquad(0);
* client.user.setHypeSquad(0); */
*/ async setHypeSquad(type) {
async setHypeSquad(type) { const id = typeof type === 'string' ? HypeSquadOptions[type] : type;
const id = typeof type === 'string' ? HypeSquadOptions[type] : type; if (!id && id !== 0) throw new Error('Invalid HypeSquad type.');
if (!id && id !== 0) throw new Error('Invalid HypeSquad type.'); if (id !== 0) {
if (id !== 0) return await this.client.api.hypesquad.online.post({ return await this.client.api.hypesquad.online.post({
data: { house_id: id }, data: { house_id: id },
}); });
else return await this.client.api.hypesquad.online.delete(); } else {
} return await this.client.api.hypesquad.online.delete();
}
/** }
* Set Accent color
* @param {ColorResolvable} color Color to set /**
* @returns {Promise} * Set Accent color
*/ * @param {ColorResolvable} color Color to set
setAccentColor(color = null) { * @returns {Promise}
return this.edit({ accent_color: color ? Util.resolveColor(color) : null }); */
} setAccentColor(color = null) {
return this.edit({ accent_color: color ? Util.resolveColor(color) : null });
/** }
* Set discriminator
* @param {User.discriminator} discriminator It is #1234 /**
* @param {string} password The password of the account * Set discriminator
* @returns {Promise} * @param {User.discriminator} discriminator It is #1234
*/ * @param {string} password The password of the account
setDiscriminator(discriminator, password) { * @returns {Promise}
if (!this.nitro) throw new Error('You must be a Nitro User to change your discriminator.'); */
if (!password && !this.client.password) setDiscriminator(discriminator, password) {
throw new Error('A password is required to change a discriminator.'); if (!this.nitro) throw new Error('You must be a Nitro User to change your discriminator.');
return this.edit({ if (!password && !this.client.password) {
discriminator, throw new Error('A password is required to change a discriminator.');
password: this.client.password ? this.client.password : password, }
}); return this.edit({
} discriminator,
password: this.client.password ? this.client.password : password,
/** });
* Set About me }
* @param {String} bio Bio to set
* @returns {Promise} /**
*/ * Set About me
setAboutMe(bio = null) { * @param {String} bio Bio to set
return this.edit({ * @returns {Promise}
bio, */
}); setAboutMe(bio = null) {
} return this.edit({
bio,
/** });
* Change the email }
* @param {Email<string>} email Email to change
* @param {string} password Password of the account /**
* @returns {Promise} * Change the email
*/ * @param {Email<string>} email Email to change
setEmail(email, password) { * @param {string} password Password of the account
if (!password && !this.client.password) * @returns {Promise}
throw new Error('A password is required to change a email.'); */
return this.edit({ setEmail(email, password) {
email, if (!password && !this.client.password) {
password: this.client.password ? this.client.password : password, throw new Error('A password is required to change a email.');
}); }
} return this.edit({
email,
/** password: this.client.password ? this.client.password : password,
* Set new password });
* @param {string} oldPassword Old password }
* @param {string} newPassword New password to set
* @returns {Promise} /**
*/ * Set new password
setPassword(oldPassword, newPassword) { * @param {string} oldPassword Old password
if (!oldPassword && !this.client.password) * @param {string} newPassword New password to set
throw new Error('A password is required to change a password.'); * @returns {Promise}
if (!newPassword) throw new Error('New password is required.'); */
return this.edit({ setPassword(oldPassword, newPassword) {
password: this.client.password ? this.client.password : oldPassword, if (!oldPassword && !this.client.password) {
new_password: newPassword, throw new Error('A password is required to change a password.');
}); }
} if (!newPassword) throw new Error('New password is required.');
return this.edit({
/** password: this.client.password ? this.client.password : oldPassword,
* Disable account new_password: newPassword,
* @param {string} password Password of the account });
* @returns {Promise} }
*/
async disableAccount(password) { /**
if (!password && !this.client.password) * Disable account
throw new Error('A password is required to disable an account.'); * @param {string} password Password of the account
return await this.client.api.users['@me'].disable.post({ * @returns {Promise}
data: { */
password: this.client.password ? this.client.password : password, async disableAccount(password) {
}, if (!password && !this.client.password) {
}); throw new Error('A password is required to disable an account.');
} }
return await this.client.api.users['@me'].disable.post({
/** data: {
* Delete account. Warning: Cannot be changed once used! password: this.client.password ? this.client.password : password,
* @param {string} password Password of the account },
* @returns {Promise} });
*/ }
async deleteAccount(password) {
if (!password && !this.client.password) /**
throw new Error('A password is required to delete an account.'); * Delete account. Warning: Cannot be changed once used!
return await this.client.api.users['@me'].delete.post({ * @param {string} password Password of the account
data: { * @returns {Promise}
password: this.client.password ? this.client.password : password, */
}, async deleteAccount(password) {
}); if (!password && !this.client.password) {
} throw new Error('A password is required to delete an account.');
}
/** return await this.client.api.users['@me'].delete.post({
* Options for setting activities data: {
* @typedef {Object} ActivitiesOptions password: this.client.password ? this.client.password : password,
* @property {string} [name] Name of the activity },
* @property {ActivityType|number} [type] Type of the activity });
* @property {string} [url] Twitch / YouTube stream URL }
*/
/**
/** * Options for setting activities
* Data resembling a raw Discord presence. * @typedef {Object} ActivitiesOptions
* @typedef {Object} PresenceData * @property {string} [name] Name of the activity
* @property {PresenceStatusData} [status] Status of the user * @property {ActivityType|number} [type] Type of the activity
* @property {boolean} [afk] Whether the user is AFK * @property {string} [url] Twitch / YouTube stream URL
* @property {ActivitiesOptions[]} [activities] Activity the user is playing */
* @property {number|number[]} [shardId] Shard id(s) to have the activity set on
*/ /**
* Data resembling a raw Discord presence.
/** * @typedef {Object} PresenceData
* Sets the full presence of the client user. * @property {PresenceStatusData} [status] Status of the user
* @param {PresenceData} data Data for the presence * @property {boolean} [afk] Whether the user is AFK
* @returns {ClientPresence} * @property {ActivitiesOptions[]} [activities] Activity the user is playing
* @example * @property {number|number[]} [shardId] Shard id(s) to have the activity set on
* // Set the client user's presence */
* client.user.setPresence({ activities: [{ name: 'with discord.js' }], status: 'idle' });
*/ /**
setPresence(data) { * Sets the full presence of the client user.
return this.client.presence.set(data); * @param {PresenceData} data Data for the presence
} * @returns {ClientPresence}
* @example
/** * // Set the client user's presence
* A user's status. Must be one of: * client.user.setPresence({ activities: [{ name: 'with discord.js' }], status: 'idle' });
* * `online` */
* * `idle` setPresence(data) {
* * `invisible` return this.client.presence.set(data);
* * `dnd` (do not disturb) }
* @typedef {string} PresenceStatusData
*/ /**
* A user's status. Must be one of:
/** * * `online`
* Sets the status of the client user. * * `idle`
* @param {PresenceStatusData} status Status to change to * * `invisible`
* @param {number|number[]} [shardId] Shard id(s) to have the activity set on * * `dnd` (do not disturb)
* @returns {ClientPresence} * @typedef {string} PresenceStatusData
* @example */
* // Set the client user's status
* client.user.setStatus('idle'); /**
*/ * Sets the status of the client user.
setStatus(status, shardId) { * @param {PresenceStatusData} status Status to change to
return this.setPresence({ status, shardId }); * @param {number|number[]} [shardId] Shard id(s) to have the activity set on
} * @returns {ClientPresence}
* @example
/** * // Set the client user's status
* Options for setting an activity. * client.user.setStatus('idle');
* @typedef {Object} ActivityOptions */
* @property {string} [name] Name of the activity setStatus(status, shardId) {
* @property {string} [url] Twitch / YouTube stream URL return this.setPresence({ status, shardId });
* @property {ActivityType|number} [type] Type of the activity }
* @property {number|number[]} [shardId] Shard Id(s) to have the activity set on
*/ /**
* Options for setting an activity.
/** * @typedef {Object} ActivityOptions
* Sets the activity the client user is playing. * @property {string} [name] Name of the activity
* @param {string|ActivityOptions} [name] Activity being played, or options for setting the activity * @property {string} [url] Twitch / YouTube stream URL
* @param {ActivityOptions} [options] Options for setting the activity * @property {ActivityType|number} [type] Type of the activity
* @returns {ClientPresence} * @property {number|number[]} [shardId] Shard Id(s) to have the activity set on
* @example */
* // Set the client user's activity
* client.user.setActivity('discord.js', { type: 'WATCHING' }); /**
*/ * Sets the activity the client user is playing.
setActivity(name, options = {}) { * @param {string|ActivityOptions} [name] Activity being played, or options for setting the activity
if (!name) * @param {ActivityOptions} [options] Options for setting the activity
return this.setPresence({ activities: [], shardId: options.shardId }); * @returns {ClientPresence}
* @example
const activity = Object.assign( * // Set the client user's activity
{}, * client.user.setActivity('discord.js', { type: 'WATCHING' });
options, */
typeof name === 'object' ? name : { name }, setActivity(name, options = {}) {
); if (!name) {
return this.setPresence({ return this.setPresence({ activities: [], shardId: options.shardId });
activities: [activity], }
shardId: activity.shardId,
}); const activity = Object.assign({}, options, typeof name === 'object' ? name : { name });
} return this.setPresence({
activities: [activity],
/** shardId: activity.shardId,
* Sets/removes the AFK flag for the client user. });
* @param {boolean} [afk=true] Whether or not the user is AFK }
* @param {number|number[]} [shardId] Shard Id(s) to have the AFK flag set on
* @returns {ClientPresence} /**
*/ * Sets/removes the AFK flag for the client user.
setAFK(afk = true, shardId) { * @param {boolean} [afk=true] Whether or not the user is AFK
return this.setPresence({ afk, shardId }); * @param {number|number[]} [shardId] Shard Id(s) to have the AFK flag set on
} * @returns {ClientPresence}
} */
setAFK(afk = true, shardId) {
module.exports = ClientUser; return this.setPresence({ afk, shardId });
}
}
module.exports = ClientUser;

View File

@@ -1,101 +1,101 @@
'use strict'; 'use strict';
const { Channel } = require('./Channel'); const { Channel } = require('./Channel');
const TextBasedChannel = require('./interfaces/TextBasedChannel'); const TextBasedChannel = require('./interfaces/TextBasedChannel');
const MessageManager = require('../managers/MessageManager'); const MessageManager = require('../managers/MessageManager');
/** /**
* Represents a direct message channel between two users. * Represents a direct message channel between two users.
* @extends {Channel} * @extends {Channel}
* @implements {TextBasedChannel} * @implements {TextBasedChannel}
*/ */
class DMChannel extends Channel { class DMChannel extends Channel {
constructor(client, data) { constructor(client, data) {
super(client, data); super(client, data);
// Override the channel type so partials have a known type // Override the channel type so partials have a known type
this.type = 'DM'; this.type = 'DM';
/** /**
* A manager of the messages belonging to this channel * A manager of the messages belonging to this channel
* @type {MessageManager} * @type {MessageManager}
*/ */
this.messages = new MessageManager(this); this.messages = new MessageManager(this);
} }
_patch(data) { _patch(data) {
super._patch(data); super._patch(data);
if (data.recipients) { if (data.recipients) {
/** /**
* The recipient on the other end of the DM * The recipient on the other end of the DM
* @type {User} * @type {User}
*/ */
this.recipient = this.client.users._add(data.recipients[0]); this.recipient = this.client.users._add(data.recipients[0]);
} }
if ('last_message_id' in data) { if ('last_message_id' in data) {
/** /**
* The channel's last message id, if one was sent * The channel's last message id, if one was sent
* @type {?Snowflake} * @type {?Snowflake}
*/ */
this.lastMessageId = data.last_message_id; this.lastMessageId = data.last_message_id;
} }
if ('last_pin_timestamp' in data) { if ('last_pin_timestamp' in data) {
/** /**
* The timestamp when the last pinned message was pinned, if there was one * The timestamp when the last pinned message was pinned, if there was one
* @type {?number} * @type {?number}
*/ */
this.lastPinTimestamp = new Date(data.last_pin_timestamp).getTime(); this.lastPinTimestamp = new Date(data.last_pin_timestamp).getTime();
} else { } else {
this.lastPinTimestamp ??= null; this.lastPinTimestamp ??= null;
} }
} }
/** /**
* Whether this DMChannel is a partial * Whether this DMChannel is a partial
* @type {boolean} * @type {boolean}
* @readonly * @readonly
*/ */
get partial() { get partial() {
return typeof this.lastMessageId === 'undefined'; return typeof this.lastMessageId === 'undefined';
} }
/** /**
* Fetch this DMChannel. * Fetch this DMChannel.
* @param {boolean} [force=true] Whether to skip the cache check and request the API * @param {boolean} [force=true] Whether to skip the cache check and request the API
* @returns {Promise<DMChannel>} * @returns {Promise<DMChannel>}
*/ */
fetch(force = true) { fetch(force = true) {
return this.recipient.createDM(force); return this.recipient.createDM(force);
} }
/** /**
* When concatenated with a string, this automatically returns the recipient's mention instead of the * When concatenated with a string, this automatically returns the recipient's mention instead of the
* DMChannel object. * DMChannel object.
* @returns {string} * @returns {string}
* @example * @example
* // Logs: Hello from <@123456789012345678>! * // Logs: Hello from <@123456789012345678>!
* console.log(`Hello from ${channel}!`); * console.log(`Hello from ${channel}!`);
*/ */
toString() { toString() {
return this.recipient.toString(); return this.recipient.toString();
} }
// These are here only for documentation purposes - they are implemented by TextBasedChannel // These are here only for documentation purposes - they are implemented by TextBasedChannel
/* eslint-disable no-empty-function */ /* eslint-disable no-empty-function */
get lastMessage() {} get lastMessage() {}
get lastPinAt() {} get lastPinAt() {}
send() {} send() {}
sendTyping() {} sendTyping() {}
createMessageCollector() {} createMessageCollector() {}
awaitMessages() {} awaitMessages() {}
createMessageComponentCollector() {} createMessageComponentCollector() {}
awaitMessageComponent() {} awaitMessageComponent() {}
// Doesn't work on DM channels; bulkDelete() {} // Doesn't work on DM channels; bulkDelete() {}
} }
TextBasedChannel.applyToClass(DMChannel, true, ['bulkDelete']); TextBasedChannel.applyToClass(DMChannel, true, ['bulkDelete']);
module.exports = DMChannel; module.exports = DMChannel;

File diff suppressed because it is too large Load Diff

View File

@@ -327,8 +327,8 @@ class Invite extends Base {
await this.client.api.invites(this.code).post({}); await this.client.api.invites(this.code).post({});
if (autoVerify) { if (autoVerify) {
const getForm = await this.client.api const getForm = await this.client.api
.guilds(this.guild.id)['member-verification'] .guilds(this.guild.id)
.get({ query: { with_guild: false, invite_code: this.code } }) ['member-verification'].get({ query: { with_guild: false, invite_code: this.code } })
.catch(() => {}); .catch(() => {});
if (!getForm) return void 0; if (!getForm) return void 0;
const form = Object.assign(getForm.form_fields[0], { response: true }); const form = Object.assign(getForm.form_fields[0], { response: true });
@@ -338,7 +338,6 @@ class Invite extends Base {
} }
return void 0; return void 0;
} }
} }
/** /**

File diff suppressed because it is too large Load Diff

View File

@@ -1,101 +1,101 @@
'use strict'; 'use strict';
const BaseMessageComponent = require('./BaseMessageComponent'); const BaseMessageComponent = require('./BaseMessageComponent');
const { MessageComponentTypes } = require('../util/Constants'); const { MessageComponentTypes } = require('../util/Constants');
/** /**
* Represents an action row containing message components. * Represents an action row containing message components.
* @extends {BaseMessageComponent} * @extends {BaseMessageComponent}
*/ */
class MessageActionRow extends BaseMessageComponent { class MessageActionRow extends BaseMessageComponent {
/** /**
* Components that can be placed in an action row * Components that can be placed in an action row
* * MessageButton * * MessageButton
* * MessageSelectMenu * * MessageSelectMenu
* @typedef {MessageButton|MessageSelectMenu} MessageActionRowComponent * @typedef {MessageButton|MessageSelectMenu} MessageActionRowComponent
*/ */
/** /**
* Options for components that can be placed in an action row * Options for components that can be placed in an action row
* * MessageButtonOptions * * MessageButtonOptions
* * MessageSelectMenuOptions * * MessageSelectMenuOptions
* @typedef {MessageButtonOptions|MessageSelectMenuOptions} MessageActionRowComponentOptions * @typedef {MessageButtonOptions|MessageSelectMenuOptions} MessageActionRowComponentOptions
*/ */
/** /**
* Data that can be resolved into components that can be placed in an action row * Data that can be resolved into components that can be placed in an action row
* * MessageActionRowComponent * * MessageActionRowComponent
* * MessageActionRowComponentOptions * * MessageActionRowComponentOptions
* @typedef {MessageActionRowComponent|MessageActionRowComponentOptions} MessageActionRowComponentResolvable * @typedef {MessageActionRowComponent|MessageActionRowComponentOptions} MessageActionRowComponentResolvable
*/ */
/** /**
* @typedef {BaseMessageComponentOptions} MessageActionRowOptions * @typedef {BaseMessageComponentOptions} MessageActionRowOptions
* @property {MessageActionRowComponentResolvable[]} [components] * @property {MessageActionRowComponentResolvable[]} [components]
* The components to place in this action row * The components to place in this action row
*/ */
/** /**
* @param {MessageActionRow|MessageActionRowOptions} [data={}] MessageActionRow to clone or raw data * @param {MessageActionRow|MessageActionRowOptions} [data={}] MessageActionRow to clone or raw data
* @param {Client} [client] The client constructing this MessageActionRow, if provided * @param {Client} [client] The client constructing this MessageActionRow, if provided
*/ */
constructor(data = {}, client = null) { constructor(data = {}, client = null) {
super({ type: 'ACTION_ROW' }); super({ type: 'ACTION_ROW' });
/** /**
* The components in this action row * The components in this action row
* @type {MessageActionRowComponent[]} * @type {MessageActionRowComponent[]}
*/ */
this.components = data.components?.map(c => BaseMessageComponent.create(c, client)) ?? []; this.components = data.components?.map(c => BaseMessageComponent.create(c, client)) ?? [];
} }
/** /**
* Adds components to the action row. * Adds components to the action row.
* @param {...MessageActionRowComponentResolvable[]} components The components to add * @param {...MessageActionRowComponentResolvable[]} components The components to add
* @returns {MessageActionRow} * @returns {MessageActionRow}
*/ */
addComponents(...components) { addComponents(...components) {
this.components.push(...components.flat(Infinity).map(c => BaseMessageComponent.create(c))); this.components.push(...components.flat(Infinity).map(c => BaseMessageComponent.create(c)));
return this; return this;
} }
/** /**
* Sets the components of the action row. * Sets the components of the action row.
* @param {...MessageActionRowComponentResolvable[]} components The components to set * @param {...MessageActionRowComponentResolvable[]} components The components to set
* @returns {MessageActionRow} * @returns {MessageActionRow}
*/ */
setComponents(...components) { setComponents(...components) {
this.spliceComponents(0, this.components.length, components); this.spliceComponents(0, this.components.length, components);
return this; return this;
} }
/** /**
* Removes, replaces, and inserts components in the action row. * Removes, replaces, and inserts components in the action row.
* @param {number} index The index to start at * @param {number} index The index to start at
* @param {number} deleteCount The number of components to remove * @param {number} deleteCount The number of components to remove
* @param {...MessageActionRowComponentResolvable[]} [components] The replacing components * @param {...MessageActionRowComponentResolvable[]} [components] The replacing components
* @returns {MessageActionRow} * @returns {MessageActionRow}
*/ */
spliceComponents(index, deleteCount, ...components) { spliceComponents(index, deleteCount, ...components) {
this.components.splice(index, deleteCount, ...components.flat(Infinity).map(c => BaseMessageComponent.create(c))); this.components.splice(index, deleteCount, ...components.flat(Infinity).map(c => BaseMessageComponent.create(c)));
return this; return this;
} }
/** /**
* Transforms the action row to a plain object. * Transforms the action row to a plain object.
* @returns {APIMessageComponent} The raw data of this action row * @returns {APIMessageComponent} The raw data of this action row
*/ */
toJSON() { toJSON() {
return { return {
components: this.components.map(c => c.toJSON()), components: this.components.map(c => c.toJSON()),
type: MessageComponentTypes[this.type], type: MessageComponentTypes[this.type],
}; };
} }
} }
module.exports = MessageActionRow; module.exports = MessageActionRow;
/** /**
* @external APIMessageComponent * @external APIMessageComponent
* @see {@link https://discord.com/developers/docs/interactions/message-components#component-object} * @see {@link https://discord.com/developers/docs/interactions/message-components#component-object}
*/ */

View File

@@ -1,193 +1,191 @@
'use strict'; 'use strict';
const BaseMessageComponent = require('./BaseMessageComponent'); const BaseMessageComponent = require('./BaseMessageComponent');
const { Message } = require('./Message'); const { Message } = require('./Message');
const { RangeError } = require('../errors'); const { RangeError } = require('../errors');
const { MessageButtonStyles, MessageComponentTypes } = require('../util/Constants'); const { MessageButtonStyles, MessageComponentTypes } = require('../util/Constants');
const Util = require('../util/Util'); const Util = require('../util/Util');
/** /**
* Represents a button message component. * Represents a button message component.
* @extends {BaseMessageComponent} * @extends {BaseMessageComponent}
*/ */
class MessageButton extends BaseMessageComponent { class MessageButton extends BaseMessageComponent {
/** /**
* @typedef {BaseMessageComponentOptions} MessageButtonOptions * @typedef {BaseMessageComponentOptions} MessageButtonOptions
* @property {string} [label] The text to be displayed on this button * @property {string} [label] The text to be displayed on this button
* @property {string} [customId] A unique string to be sent in the interaction when clicked * @property {string} [customId] A unique string to be sent in the interaction when clicked
* @property {MessageButtonStyleResolvable} [style] The style of this button * @property {MessageButtonStyleResolvable} [style] The style of this button
* @property {EmojiIdentifierResolvable} [emoji] The emoji to be displayed to the left of the text * @property {EmojiIdentifierResolvable} [emoji] The emoji to be displayed to the left of the text
* @property {string} [url] Optional URL for link-style buttons * @property {string} [url] Optional URL for link-style buttons
* @property {boolean} [disabled=false] Disables the button to prevent interactions * @property {boolean} [disabled=false] Disables the button to prevent interactions
*/ */
/** /**
* @param {MessageButton|MessageButtonOptions} [data={}] MessageButton to clone or raw data * @param {MessageButton|MessageButtonOptions} [data={}] MessageButton to clone or raw data
*/ */
constructor(data = {}) { constructor(data = {}) {
super({ type: 'BUTTON' }); super({ type: 'BUTTON' });
this.setup(data); this.setup(data);
} }
setup(data) { setup(data) {
/** /**
* The text to be displayed on this button * The text to be displayed on this button
* @type {?string} * @type {?string}
*/ */
this.label = data.label ?? null; this.label = data.label ?? null;
/** /**
* A unique string to be sent in the interaction when clicked * A unique string to be sent in the interaction when clicked
* @type {?string} * @type {?string}
*/ */
this.customId = data.custom_id ?? data.customId ?? null; this.customId = data.custom_id ?? data.customId ?? null;
/** /**
* The style of this button * The style of this button
* @type {?MessageButtonStyle} * @type {?MessageButtonStyle}
*/ */
this.style = data.style ? MessageButton.resolveStyle(data.style) : null; this.style = data.style ? MessageButton.resolveStyle(data.style) : null;
/** /**
* Emoji for this button * Emoji for this button
* @type {?RawEmoji} * @type {?RawEmoji}
*/ */
this.emoji = data.emoji ? Util.resolvePartialEmoji(data.emoji) : null; this.emoji = data.emoji ? Util.resolvePartialEmoji(data.emoji) : null;
/** /**
* The URL this button links to, if it is a Link style button * The URL this button links to, if it is a Link style button
* @type {?string} * @type {?string}
*/ */
this.url = data.url ?? null; this.url = data.url ?? null;
/** /**
* Whether this button is currently disabled * Whether this button is currently disabled
* @type {boolean} * @type {boolean}
*/ */
this.disabled = data.disabled ?? false; this.disabled = data.disabled ?? false;
} }
/** /**
* Sets the custom id for this button * Sets the custom id for this button
* @param {string} customId A unique string to be sent in the interaction when clicked * @param {string} customId A unique string to be sent in the interaction when clicked
* @returns {MessageButton} * @returns {MessageButton}
*/ */
setCustomId(customId) { setCustomId(customId) {
this.customId = Util.verifyString(customId, RangeError, 'BUTTON_CUSTOM_ID'); this.customId = Util.verifyString(customId, RangeError, 'BUTTON_CUSTOM_ID');
return this; return this;
} }
/** /**
* Sets the interactive status of the button * Sets the interactive status of the button
* @param {boolean} [disabled=true] Whether this button should be disabled * @param {boolean} [disabled=true] Whether this button should be disabled
* @returns {MessageButton} * @returns {MessageButton}
*/ */
setDisabled(disabled = true) { setDisabled(disabled = true) {
this.disabled = disabled; this.disabled = disabled;
return this; return this;
} }
/** /**
* Set the emoji of this button * Set the emoji of this button
* @param {EmojiIdentifierResolvable} emoji The emoji to be displayed on this button * @param {EmojiIdentifierResolvable} emoji The emoji to be displayed on this button
* @returns {MessageButton} * @returns {MessageButton}
*/ */
setEmoji(emoji) { setEmoji(emoji) {
this.emoji = Util.resolvePartialEmoji(emoji); this.emoji = Util.resolvePartialEmoji(emoji);
return this; return this;
} }
/** /**
* Sets the label of this button * Sets the label of this button
* @param {string} label The text to be displayed on this button * @param {string} label The text to be displayed on this button
* @returns {MessageButton} * @returns {MessageButton}
*/ */
setLabel(label) { setLabel(label) {
this.label = Util.verifyString(label, RangeError, 'BUTTON_LABEL'); this.label = Util.verifyString(label, RangeError, 'BUTTON_LABEL');
return this; return this;
} }
/** /**
* Sets the style of this button * Sets the style of this button
* @param {MessageButtonStyleResolvable} style The style of this button * @param {MessageButtonStyleResolvable} style The style of this button
* @returns {MessageButton} * @returns {MessageButton}
*/ */
setStyle(style) { setStyle(style) {
this.style = MessageButton.resolveStyle(style); this.style = MessageButton.resolveStyle(style);
return this; return this;
} }
/** /**
* Sets the URL of this button. * Sets the URL of this button.
* <info>MessageButton#style must be LINK when setting a URL</info> * <info>MessageButton#style must be LINK when setting a URL</info>
* @param {string} url The URL of this button * @param {string} url The URL of this button
* @returns {MessageButton} * @returns {MessageButton}
*/ */
setURL(url) { setURL(url) {
this.url = Util.verifyString(url, RangeError, 'BUTTON_URL'); this.url = Util.verifyString(url, RangeError, 'BUTTON_URL');
return this; return this;
} }
/** /**
* Transforms the button to a plain object. * Transforms the button to a plain object.
* @returns {APIMessageButton} The raw data of this button * @returns {APIMessageButton} The raw data of this button
*/ */
toJSON() { toJSON() {
return { return {
custom_id: this.customId, custom_id: this.customId,
disabled: this.disabled, disabled: this.disabled,
emoji: this.emoji, emoji: this.emoji,
label: this.label, label: this.label,
style: MessageButtonStyles[this.style], style: MessageButtonStyles[this.style],
type: MessageComponentTypes[this.type], type: MessageComponentTypes[this.type],
url: this.url, url: this.url,
}; };
} }
/** /**
* Data that can be resolved to a MessageButtonStyle. This can be * Data that can be resolved to a MessageButtonStyle. This can be
* * MessageButtonStyle * * MessageButtonStyle
* * number * * number
* @typedef {number|MessageButtonStyle} MessageButtonStyleResolvable * @typedef {number|MessageButtonStyle} MessageButtonStyleResolvable
*/ */
/** /**
* Resolves the style of a button * Resolves the style of a button
* @param {MessageButtonStyleResolvable} style The style to resolve * @param {MessageButtonStyleResolvable} style The style to resolve
* @returns {MessageButtonStyle} * @returns {MessageButtonStyle}
* @private * @private
*/ */
static resolveStyle(style) { static resolveStyle(style) {
return typeof style === 'string' ? style : MessageButtonStyles[style]; return typeof style === 'string' ? style : MessageButtonStyles[style];
} }
// Patch Click // Patch Click
/** /**
* Click the button * Click the button
* @param {Message} message Discord Message * @param {Message} message Discord Message
* @returns {boolean} * @returns {boolean}
*/ */
async click(message) { async click(message) {
if (!message instanceof Message) throw new Error("[UNKNOWN_MESSAGE] Please pass a valid Message"); if (!message instanceof Message) throw new Error('[UNKNOWN_MESSAGE] Please pass a valid Message');
if (!this.customId || this.style == 5 || this.disabled) return false; // Button URL, Disabled if (!this.customId || this.style == 5 || this.disabled) return false; // Button URL, Disabled
await message.client.api.interactions.post( await message.client.api.interactions.post({
{ data: {
data: { type: 3, // ?
type: 3, // ? guild_id: message.guild?.id ?? null, // In DMs
guild_id: message.guild?.id ?? null, // In DMs channel_id: message.channel.id,
channel_id: message.channel.id, message_id: message.id,
message_id: message.id, application_id: message.author.id,
application_id: message.author.id, session_id: message.client.session_id,
session_id: message.client.session_id, data: {
data: { component_type: 2, // Button
component_type: 2, // Button custom_id: this.customId,
custom_id: this.customId },
}, },
} });
} return true;
) }
return true; }
}
} module.exports = MessageButton;
module.exports = MessageButton;

View File

@@ -1,235 +1,235 @@
'use strict'; 'use strict';
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
const { ChannelTypes } = require('../util/Constants'); const { ChannelTypes } = require('../util/Constants');
const Util = require('../util/Util'); const Util = require('../util/Util');
/** /**
* Keeps track of mentions in a {@link Message}. * Keeps track of mentions in a {@link Message}.
*/ */
class MessageMentions { class MessageMentions {
constructor(message, users, roles, everyone, crosspostedChannels, repliedUser) { constructor(message, users, roles, everyone, crosspostedChannels, repliedUser) {
/** /**
* The client the message is from * The client the message is from
* @type {Client} * @type {Client}
* @readonly * @readonly
*/ */
Object.defineProperty(this, 'client', { value: message.client }); Object.defineProperty(this, 'client', { value: message.client });
/** /**
* The guild the message is in * The guild the message is in
* @type {?Guild} * @type {?Guild}
* @readonly * @readonly
*/ */
Object.defineProperty(this, 'guild', { value: message.guild }); Object.defineProperty(this, 'guild', { value: message.guild });
/** /**
* The initial message content * The initial message content
* @type {string} * @type {string}
* @readonly * @readonly
* @private * @private
*/ */
Object.defineProperty(this, '_content', { value: message.content }); Object.defineProperty(this, '_content', { value: message.content });
/** /**
* Whether `@everyone` or `@here` were mentioned * Whether `@everyone` or `@here` were mentioned
* @type {boolean} * @type {boolean}
*/ */
this.everyone = Boolean(everyone); this.everyone = Boolean(everyone);
if (users) { if (users) {
if (users instanceof Collection) { if (users instanceof Collection) {
/** /**
* Any users that were mentioned * Any users that were mentioned
* <info>Order as received from the API, not as they appear in the message content</info> * <info>Order as received from the API, not as they appear in the message content</info>
* @type {Collection<Snowflake, User>} * @type {Collection<Snowflake, User>}
*/ */
this.users = new Collection(users); this.users = new Collection(users);
} else { } else {
this.users = new Collection(); this.users = new Collection();
for (const mention of users) { for (const mention of users) {
if (mention.member && message.guild) { if (mention.member && message.guild) {
message.guild.members._add(Object.assign(mention.member, { user: mention })); message.guild.members._add(Object.assign(mention.member, { user: mention }));
} }
const user = message.client.users._add(mention); const user = message.client.users._add(mention);
this.users.set(user.id, user); this.users.set(user.id, user);
} }
} }
} else { } else {
this.users = new Collection(); this.users = new Collection();
} }
if (roles instanceof Collection) { if (roles instanceof Collection) {
/** /**
* Any roles that were mentioned * Any roles that were mentioned
* <info>Order as received from the API, not as they appear in the message content</info> * <info>Order as received from the API, not as they appear in the message content</info>
* @type {Collection<Snowflake, Role>} * @type {Collection<Snowflake, Role>}
*/ */
this.roles = new Collection(roles); this.roles = new Collection(roles);
} else if (roles) { } else if (roles) {
this.roles = new Collection(); this.roles = new Collection();
const guild = message.guild; const guild = message.guild;
if (guild) { if (guild) {
for (const mention of roles) { for (const mention of roles) {
const role = guild.roles.cache.get(mention); const role = guild.roles.cache.get(mention);
if (role) this.roles.set(role.id, role); if (role) this.roles.set(role.id, role);
} }
} }
} else { } else {
this.roles = new Collection(); this.roles = new Collection();
} }
/** /**
* Cached members for {@link MessageMentions#members} * Cached members for {@link MessageMentions#members}
* @type {?Collection<Snowflake, GuildMember>} * @type {?Collection<Snowflake, GuildMember>}
* @private * @private
*/ */
this._members = null; this._members = null;
/** /**
* Cached channels for {@link MessageMentions#channels} * Cached channels for {@link MessageMentions#channels}
* @type {?Collection<Snowflake, Channel>} * @type {?Collection<Snowflake, Channel>}
* @private * @private
*/ */
this._channels = null; this._channels = null;
/** /**
* Crossposted channel data. * Crossposted channel data.
* @typedef {Object} CrosspostedChannel * @typedef {Object} CrosspostedChannel
* @property {Snowflake} channelId The mentioned channel's id * @property {Snowflake} channelId The mentioned channel's id
* @property {Snowflake} guildId The id of the guild that has the channel * @property {Snowflake} guildId The id of the guild that has the channel
* @property {ChannelType} type The channel's type * @property {ChannelType} type The channel's type
* @property {string} name The channel's name * @property {string} name The channel's name
*/ */
if (crosspostedChannels) { if (crosspostedChannels) {
if (crosspostedChannels instanceof Collection) { if (crosspostedChannels instanceof Collection) {
/** /**
* A collection of crossposted channels * A collection of crossposted channels
* <info>Order as received from the API, not as they appear in the message content</info> * <info>Order as received from the API, not as they appear in the message content</info>
* @type {Collection<Snowflake, CrosspostedChannel>} * @type {Collection<Snowflake, CrosspostedChannel>}
*/ */
this.crosspostedChannels = new Collection(crosspostedChannels); this.crosspostedChannels = new Collection(crosspostedChannels);
} else { } else {
this.crosspostedChannels = new Collection(); this.crosspostedChannels = new Collection();
const channelTypes = Object.keys(ChannelTypes); const channelTypes = Object.keys(ChannelTypes);
for (const d of crosspostedChannels) { for (const d of crosspostedChannels) {
const type = channelTypes[d.type]; const type = channelTypes[d.type];
this.crosspostedChannels.set(d.id, { this.crosspostedChannels.set(d.id, {
channelId: d.id, channelId: d.id,
guildId: d.guild_id, guildId: d.guild_id,
type: type ?? 'UNKNOWN', type: type ?? 'UNKNOWN',
name: d.name, name: d.name,
}); });
} }
} }
} else { } else {
this.crosspostedChannels = new Collection(); this.crosspostedChannels = new Collection();
} }
/** /**
* The author of the message that this message is a reply to * The author of the message that this message is a reply to
* @type {?User} * @type {?User}
*/ */
this.repliedUser = repliedUser ? this.client.users._add(repliedUser) : null; this.repliedUser = repliedUser ? this.client.users._add(repliedUser) : null;
} }
/** /**
* Any members that were mentioned (only in {@link Guild}s) * Any members that were mentioned (only in {@link Guild}s)
* <info>Order as received from the API, not as they appear in the message content</info> * <info>Order as received from the API, not as they appear in the message content</info>
* @type {?Collection<Snowflake, GuildMember>} * @type {?Collection<Snowflake, GuildMember>}
* @readonly * @readonly
*/ */
get members() { get members() {
if (this._members) return this._members; if (this._members) return this._members;
if (!this.guild) return null; if (!this.guild) return null;
this._members = new Collection(); this._members = new Collection();
this.users.forEach(user => { this.users.forEach(user => {
const member = this.guild.members.resolve(user); const member = this.guild.members.resolve(user);
if (member) this._members.set(member.user.id, member); if (member) this._members.set(member.user.id, member);
}); });
return this._members; return this._members;
} }
/** /**
* Any channels that were mentioned * Any channels that were mentioned
* <info>Order as they appear first in the message content</info> * <info>Order as they appear first in the message content</info>
* @type {Collection<Snowflake, Channel>} * @type {Collection<Snowflake, Channel>}
* @readonly * @readonly
*/ */
get channels() { get channels() {
if (this._channels) return this._channels; if (this._channels) return this._channels;
this._channels = new Collection(); this._channels = new Collection();
let matches; let matches;
while ((matches = this.constructor.CHANNELS_PATTERN.exec(this._content)) !== null) { while ((matches = this.constructor.CHANNELS_PATTERN.exec(this._content)) !== null) {
const chan = this.client.channels.cache.get(matches[1]); const chan = this.client.channels.cache.get(matches[1]);
if (chan) this._channels.set(chan.id, chan); if (chan) this._channels.set(chan.id, chan);
} }
return this._channels; return this._channels;
} }
/** /**
* Options used to check for a mention. * Options used to check for a mention.
* @typedef {Object} MessageMentionsHasOptions * @typedef {Object} MessageMentionsHasOptions
* @property {boolean} [ignoreDirect=false] Whether to ignore direct mentions to the item * @property {boolean} [ignoreDirect=false] Whether to ignore direct mentions to the item
* @property {boolean} [ignoreRoles=false] Whether to ignore role mentions to a guild member * @property {boolean} [ignoreRoles=false] Whether to ignore role mentions to a guild member
* @property {boolean} [ignoreEveryone=false] Whether to ignore everyone/here mentions * @property {boolean} [ignoreEveryone=false] Whether to ignore everyone/here mentions
*/ */
/** /**
* Checks if a user, guild member, role, or channel is mentioned. * Checks if a user, guild member, role, or channel is mentioned.
* Takes into account user mentions, role mentions, and `@everyone`/`@here` mentions. * Takes into account user mentions, role mentions, and `@everyone`/`@here` mentions.
* @param {UserResolvable|RoleResolvable|ChannelResolvable} data The User/Role/Channel to check for * @param {UserResolvable|RoleResolvable|ChannelResolvable} data The User/Role/Channel to check for
* @param {MessageMentionsHasOptions} [options] The options for the check * @param {MessageMentionsHasOptions} [options] The options for the check
* @returns {boolean} * @returns {boolean}
*/ */
has(data, { ignoreDirect = false, ignoreRoles = false, ignoreEveryone = false } = {}) { has(data, { ignoreDirect = false, ignoreRoles = false, ignoreEveryone = false } = {}) {
if (!ignoreEveryone && this.everyone) return true; if (!ignoreEveryone && this.everyone) return true;
const { GuildMember } = require('./GuildMember'); const { GuildMember } = require('./GuildMember');
if (!ignoreRoles && data instanceof GuildMember) { if (!ignoreRoles && data instanceof GuildMember) {
for (const role of this.roles.values()) if (data.roles.cache.has(role.id)) return true; for (const role of this.roles.values()) if (data.roles.cache.has(role.id)) return true;
} }
if (!ignoreDirect) { if (!ignoreDirect) {
const id = const id =
this.guild?.roles.resolveId(data) ?? this.client.channels.resolveId(data) ?? this.client.users.resolveId(data); this.guild?.roles.resolveId(data) ?? this.client.channels.resolveId(data) ?? this.client.users.resolveId(data);
return typeof id === 'string' && (this.users.has(id) || this.channels.has(id) || this.roles.has(id)); return typeof id === 'string' && (this.users.has(id) || this.channels.has(id) || this.roles.has(id));
} }
return false; return false;
} }
toJSON() { toJSON() {
return Util.flatten(this, { return Util.flatten(this, {
members: true, members: true,
channels: true, channels: true,
}); });
} }
} }
/** /**
* Regular expression that globally matches `@everyone` and `@here` * Regular expression that globally matches `@everyone` and `@here`
* @type {RegExp} * @type {RegExp}
*/ */
MessageMentions.EVERYONE_PATTERN = /@(everyone|here)/g; MessageMentions.EVERYONE_PATTERN = /@(everyone|here)/g;
/** /**
* Regular expression that globally matches user mentions like `<@81440962496172032>` * Regular expression that globally matches user mentions like `<@81440962496172032>`
* @type {RegExp} * @type {RegExp}
*/ */
MessageMentions.USERS_PATTERN = /<@!?(\d{17,19})>/g; MessageMentions.USERS_PATTERN = /<@!?(\d{17,19})>/g;
/** /**
* Regular expression that globally matches role mentions like `<@&297577916114403338>` * Regular expression that globally matches role mentions like `<@&297577916114403338>`
* @type {RegExp} * @type {RegExp}
*/ */
MessageMentions.ROLES_PATTERN = /<@&(\d{17,19})>/g; MessageMentions.ROLES_PATTERN = /<@&(\d{17,19})>/g;
/** /**
* Regular expression that globally matches channel mentions like `<#222079895583457280>` * Regular expression that globally matches channel mentions like `<#222079895583457280>`
* @type {RegExp} * @type {RegExp}
*/ */
MessageMentions.CHANNELS_PATTERN = /<#(\d{17,19})>/g; MessageMentions.CHANNELS_PATTERN = /<#(\d{17,19})>/g;
module.exports = MessageMentions; module.exports = MessageMentions;

View File

@@ -1,314 +1,313 @@
'use strict'; 'use strict';
const { Buffer } = require('node:buffer'); const { Buffer } = require('node:buffer');
const BaseMessageComponent = require('./BaseMessageComponent'); const BaseMessageComponent = require('./BaseMessageComponent');
const MessageEmbed = require('./MessageEmbed'); const MessageEmbed = require('./MessageEmbed');
const WebEmbed = require('./WebEmbed'); const WebEmbed = require('./WebEmbed');
const { RangeError } = require('../errors'); const { RangeError } = require('../errors');
const DataResolver = require('../util/DataResolver'); const DataResolver = require('../util/DataResolver');
const MessageFlags = require('../util/MessageFlags'); const MessageFlags = require('../util/MessageFlags');
const Util = require('../util/Util'); const Util = require('../util/Util');
/** /**
* Represents a message to be sent to the API. * Represents a message to be sent to the API.
*/ */
class MessagePayload { class MessagePayload {
/** /**
* @param {MessageTarget} target The target for this message to be sent to * @param {MessageTarget} target The target for this message to be sent to
* @param {MessageOptions|WebhookMessageOptions} options Options passed in from send * @param {MessageOptions|WebhookMessageOptions} options Options passed in from send
*/ */
constructor(target, options) { constructor(target, options) {
/** /**
* The target for this message to be sent to * The target for this message to be sent to
* @type {MessageTarget} * @type {MessageTarget}
*/ */
this.target = target; this.target = target;
/** /**
* Options passed in from send * Options passed in from send
* @type {MessageOptions|WebhookMessageOptions} * @type {MessageOptions|WebhookMessageOptions}
*/ */
this.options = options; this.options = options;
/** /**
* Data sendable to the API * Data sendable to the API
* @type {?APIMessage} * @type {?APIMessage}
*/ */
this.data = null; this.data = null;
/** /**
* @typedef {Object} MessageFile * @typedef {Object} MessageFile
* @property {Buffer|string|Stream} attachment The original attachment that generated this file * @property {Buffer|string|Stream} attachment The original attachment that generated this file
* @property {string} name The name of this file * @property {string} name The name of this file
* @property {Buffer|Stream} file The file to be sent to the API * @property {Buffer|Stream} file The file to be sent to the API
*/ */
/** /**
* Files sendable to the API * Files sendable to the API
* @type {?MessageFile[]} * @type {?MessageFile[]}
*/ */
this.files = null; this.files = null;
} }
/** /**
* Whether or not the target is a {@link Webhook} or a {@link WebhookClient} * Whether or not the target is a {@link Webhook} or a {@link WebhookClient}
* @type {boolean} * @type {boolean}
* @readonly * @readonly
*/ */
get isWebhook() { get isWebhook() {
const Webhook = require('./Webhook'); const Webhook = require('./Webhook');
const WebhookClient = require('../client/WebhookClient'); const WebhookClient = require('../client/WebhookClient');
return this.target instanceof Webhook || this.target instanceof WebhookClient; return this.target instanceof Webhook || this.target instanceof WebhookClient;
} }
/** /**
* Whether or not the target is a {@link User} * Whether or not the target is a {@link User}
* @type {boolean} * @type {boolean}
* @readonly * @readonly
*/ */
get isUser() { get isUser() {
const User = require('./User'); const User = require('./User');
const { GuildMember } = require('./GuildMember'); const { GuildMember } = require('./GuildMember');
return this.target instanceof User || this.target instanceof GuildMember; return this.target instanceof User || this.target instanceof GuildMember;
} }
/** /**
* Whether or not the target is a {@link Message} * Whether or not the target is a {@link Message}
* @type {boolean} * @type {boolean}
* @readonly * @readonly
*/ */
get isMessage() { get isMessage() {
const { Message } = require('./Message'); const { Message } = require('./Message');
return this.target instanceof Message; return this.target instanceof Message;
} }
/** /**
* Whether or not the target is a {@link MessageManager} * Whether or not the target is a {@link MessageManager}
* @type {boolean} * @type {boolean}
* @readonly * @readonly
*/ */
get isMessageManager() { get isMessageManager() {
const MessageManager = require('../managers/MessageManager'); const MessageManager = require('../managers/MessageManager');
return this.target instanceof MessageManager; return this.target instanceof MessageManager;
} }
/** /**
* Whether or not the target is an {@link Interaction} or an {@link InteractionWebhook} * Whether or not the target is an {@link Interaction} or an {@link InteractionWebhook}
* @type {boolean} * @type {boolean}
* @readonly * @readonly
*/ */
get isInteraction() { get isInteraction() {
const Interaction = require('./Interaction'); const Interaction = require('./Interaction');
const InteractionWebhook = require('./InteractionWebhook'); const InteractionWebhook = require('./InteractionWebhook');
return this.target instanceof Interaction || this.target instanceof InteractionWebhook; return this.target instanceof Interaction || this.target instanceof InteractionWebhook;
} }
/** /**
* Makes the content of this message. * Makes the content of this message.
* @returns {?string} * @returns {?string}
*/ */
makeContent() { makeContent() {
let content; let content;
if (this.options.content === null) { if (this.options.content === null) {
content = ''; content = '';
} else if (typeof this.options.content !== 'undefined') { } else if (typeof this.options.content !== 'undefined') {
content = Util.verifyString(this.options.content, RangeError, 'MESSAGE_CONTENT_TYPE', false); content = Util.verifyString(this.options.content, RangeError, 'MESSAGE_CONTENT_TYPE', false);
} }
return content; return content;
} }
/** /**
* Resolves data. * Resolves data.
* @returns {MessagePayload} * @returns {MessagePayload}
*/ */
async resolveData() { async resolveData() {
if (this.data) return this; if (this.data) return this;
const isInteraction = this.isInteraction; const isInteraction = this.isInteraction;
const isWebhook = this.isWebhook; const isWebhook = this.isWebhook;
let content = this.makeContent(); let content = this.makeContent();
const tts = Boolean(this.options.tts); const tts = Boolean(this.options.tts);
let nonce; let nonce;
if (typeof this.options.nonce !== 'undefined') { if (typeof this.options.nonce !== 'undefined') {
nonce = this.options.nonce; nonce = this.options.nonce;
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
if (typeof nonce === 'number' ? !Number.isInteger(nonce) : typeof nonce !== 'string') { if (typeof nonce === 'number' ? !Number.isInteger(nonce) : typeof nonce !== 'string') {
throw new RangeError('MESSAGE_NONCE_TYPE'); throw new RangeError('MESSAGE_NONCE_TYPE');
} }
} }
const components = this.options.components?.map(c => BaseMessageComponent.create(c).toJSON()); const components = this.options.components?.map(c => BaseMessageComponent.create(c).toJSON());
let username; let username;
let avatarURL; let avatarURL;
if (isWebhook) { if (isWebhook) {
username = this.options.username ?? this.target.name; username = this.options.username ?? this.target.name;
if (this.options.avatarURL) avatarURL = this.options.avatarURL; if (this.options.avatarURL) avatarURL = this.options.avatarURL;
} }
let flags; let flags;
if (this.isMessage || this.isMessageManager) { if (this.isMessage || this.isMessageManager) {
// eslint-disable-next-line eqeqeq // eslint-disable-next-line eqeqeq
flags = this.options.flags != null ? new MessageFlags(this.options.flags).bitfield : this.target.flags?.bitfield; flags = this.options.flags != null ? new MessageFlags(this.options.flags).bitfield : this.target.flags?.bitfield;
} else if (isInteraction && this.options.ephemeral) { } else if (isInteraction && this.options.ephemeral) {
flags = MessageFlags.FLAGS.EPHEMERAL; flags = MessageFlags.FLAGS.EPHEMERAL;
} }
let allowedMentions = let allowedMentions =
typeof this.options.allowedMentions === 'undefined' typeof this.options.allowedMentions === 'undefined'
? this.target.client.options.allowedMentions ? this.target.client.options.allowedMentions
: this.options.allowedMentions; : this.options.allowedMentions;
if (allowedMentions) { if (allowedMentions) {
allowedMentions = Util.cloneObject(allowedMentions); allowedMentions = Util.cloneObject(allowedMentions);
allowedMentions.replied_user = allowedMentions.repliedUser; allowedMentions.replied_user = allowedMentions.repliedUser;
delete allowedMentions.repliedUser; delete allowedMentions.repliedUser;
} }
let message_reference; let message_reference;
if (typeof this.options.reply === 'object') { if (typeof this.options.reply === 'object') {
const reference = this.options.reply.messageReference; const reference = this.options.reply.messageReference;
const message_id = this.isMessage ? reference.id ?? reference : this.target.messages.resolveId(reference); const message_id = this.isMessage ? reference.id ?? reference : this.target.messages.resolveId(reference);
if (message_id) { if (message_id) {
message_reference = { message_reference = {
message_id, message_id,
fail_if_not_exists: this.options.reply.failIfNotExists ?? this.target.client.options.failIfNotExists, fail_if_not_exists: this.options.reply.failIfNotExists ?? this.target.client.options.failIfNotExists,
}; };
} }
} }
const attachments = this.options.files?.map((file, index) => ({ const attachments = this.options.files?.map((file, index) => ({
id: index.toString(), id: index.toString(),
description: file.description, description: file.description,
})); }));
if (Array.isArray(this.options.attachments)) { if (Array.isArray(this.options.attachments)) {
this.options.attachments.push(...(attachments ?? [])); this.options.attachments.push(...(attachments ?? []));
} else { } else {
this.options.attachments = attachments; this.options.attachments = attachments;
} }
if (this.options.embeds) { if (this.options.embeds) {
if (!Array.isArray(this.options.embeds)) { if (!Array.isArray(this.options.embeds)) {
this.options.embeds = [this.options.embeds]; this.options.embeds = [this.options.embeds];
} }
const webembeds = this.options.embeds.filter( const webembeds = this.options.embeds.filter(e => e instanceof WebEmbed);
(e) => e instanceof WebEmbed, this.options.embeds = this.options.embeds.filter(e => e instanceof MessageEmbed);
);
this.options.embeds = this.options.embeds.filter(e => e instanceof MessageEmbed); if (webembeds.length > 0) {
if (!content) content = '';
if (webembeds.length > 0) { // add hidden embed link
if (!content) content = ''; content += `\n${WebEmbed.hiddenEmbed} \n`;
// add hidden embed link if (webembeds.length > 1) {
content += `\n${WebEmbed.hiddenEmbed} \n`; console.warn('Multiple webembeds are not supported, this will be ignored.');
if (webembeds.length > 1) { }
console.warn('Multiple webembeds are not supported, this will be ignored.'); // const embed = webembeds[0];
} for (const webE of webembeds) {
// const embed = webembeds[0]; const data = await webE.toMessage();
for (const webE of webembeds) { content += `\n${data}`;
const data = await webE.toMessage(); }
content += `\n${data}`; }
} // Check content
} if (content.length > 2000) {
// Check content console.warn('[WARN] Content is longer than 2000 characters.');
if (content.length > 2000) { }
console.warn(`[WARN] Content is longer than 2000 characters.`); if (content.length > 4000) {
} // Max length if user has nitro boost
if (content.length > 4000) { // Max length if user has nitro boost throw new RangeError('MESSAGE_EMBED_LINK_LENGTH');
throw new RangeError('MESSAGE_EMBED_LINK_LENGTH'); }
} }
}
this.data = {
this.data = { content,
content, tts,
tts, nonce,
nonce, embeds: this.options.embeds?.map(embed => new MessageEmbed(embed).toJSON()),
embeds: this.options.embeds?.map(embed => new MessageEmbed(embed).toJSON()), components,
components, username,
username, avatar_url: avatarURL,
avatar_url: avatarURL, allowed_mentions:
allowed_mentions: typeof content === 'undefined' && typeof message_reference === 'undefined' ? undefined : allowedMentions,
typeof content === 'undefined' && typeof message_reference === 'undefined' ? undefined : allowedMentions, flags,
flags, message_reference,
message_reference, attachments: this.options.attachments,
attachments: this.options.attachments, sticker_ids: this.options.stickers?.map(sticker => sticker.id ?? sticker),
sticker_ids: this.options.stickers?.map(sticker => sticker.id ?? sticker), };
}; return this;
return this; }
}
/**
/** * Resolves files.
* Resolves files. * @returns {Promise<MessagePayload>}
* @returns {Promise<MessagePayload>} */
*/ async resolveFiles() {
async resolveFiles() { if (this.files) return this;
if (this.files) return this;
this.files = await Promise.all(this.options.files?.map(file => this.constructor.resolveFile(file)) ?? []);
this.files = await Promise.all(this.options.files?.map(file => this.constructor.resolveFile(file)) ?? []); return this;
return this; }
}
/**
/** * Resolves a single file into an object sendable to the API.
* Resolves a single file into an object sendable to the API. * @param {BufferResolvable|Stream|FileOptions|MessageAttachment} fileLike Something that could be resolved to a file
* @param {BufferResolvable|Stream|FileOptions|MessageAttachment} fileLike Something that could be resolved to a file * @returns {Promise<MessageFile>}
* @returns {Promise<MessageFile>} */
*/ static async resolveFile(fileLike) {
static async resolveFile(fileLike) { let attachment;
let attachment; let name;
let name;
const findName = thing => {
const findName = thing => { if (typeof thing === 'string') {
if (typeof thing === 'string') { return Util.basename(thing);
return Util.basename(thing); }
}
if (thing.path) {
if (thing.path) { return Util.basename(thing.path);
return Util.basename(thing.path); }
}
return 'file.jpg';
return 'file.jpg'; };
};
const ownAttachment =
const ownAttachment = typeof fileLike === 'string' || fileLike instanceof Buffer || typeof fileLike.pipe === 'function';
typeof fileLike === 'string' || fileLike instanceof Buffer || typeof fileLike.pipe === 'function'; if (ownAttachment) {
if (ownAttachment) { attachment = fileLike;
attachment = fileLike; name = findName(attachment);
name = findName(attachment); } else {
} else { attachment = fileLike.attachment;
attachment = fileLike.attachment; name = fileLike.name ?? findName(attachment);
name = fileLike.name ?? findName(attachment); }
}
const resource = await DataResolver.resolveFile(attachment);
const resource = await DataResolver.resolveFile(attachment); return { attachment, name, file: resource };
return { attachment, name, file: resource }; }
}
/**
/** * Creates a {@link MessagePayload} from user-level arguments.
* Creates a {@link MessagePayload} from user-level arguments. * @param {MessageTarget} target Target to send to
* @param {MessageTarget} target Target to send to * @param {string|MessageOptions|WebhookMessageOptions} options Options or content to use
* @param {string|MessageOptions|WebhookMessageOptions} options Options or content to use * @param {MessageOptions|WebhookMessageOptions} [extra={}] Extra options to add onto specified options
* @param {MessageOptions|WebhookMessageOptions} [extra={}] Extra options to add onto specified options * @returns {MessagePayload}
* @returns {MessagePayload} */
*/ static create(target, options, extra = {}) {
static create(target, options, extra = {}) { return new this(
return new this( target,
target, typeof options !== 'object' || options === null ? { content: options, ...extra } : { ...options, ...extra },
typeof options !== 'object' || options === null ? { content: options, ...extra } : { ...options, ...extra }, );
); }
} }
}
module.exports = MessagePayload;
module.exports = MessagePayload;
/**
/** * A target for a message.
* A target for a message. * @typedef {TextChannel|DMChannel|User|GuildMember|Webhook|WebhookClient|Interaction|InteractionWebhook|
* @typedef {TextChannel|DMChannel|User|GuildMember|Webhook|WebhookClient|Interaction|InteractionWebhook| * Message|MessageManager} MessageTarget
* Message|MessageManager} MessageTarget */
*/
/**
/** * @external APIMessage
* @external APIMessage * @see {@link https://discord.com/developers/docs/resources/channel#message-object}
* @see {@link https://discord.com/developers/docs/resources/channel#message-object} */
*/

View File

@@ -1,256 +1,262 @@
'use strict'; 'use strict';
const BaseMessageComponent = require('./BaseMessageComponent'); const BaseMessageComponent = require('./BaseMessageComponent');
const { MessageComponentTypes } = require('../util/Constants'); const { MessageComponentTypes } = require('../util/Constants');
const Util = require('../util/Util'); const Util = require('../util/Util');
const { Message } = require('./Message'); const { Message } = require('./Message');
/** /**
* Represents a select menu message component * Represents a select menu message component
* @extends {BaseMessageComponent} * @extends {BaseMessageComponent}
*/ */
class MessageSelectMenu extends BaseMessageComponent { class MessageSelectMenu extends BaseMessageComponent {
/** /**
* @typedef {BaseMessageComponentOptions} MessageSelectMenuOptions * @typedef {BaseMessageComponentOptions} MessageSelectMenuOptions
* @property {string} [customId] A unique string to be sent in the interaction when clicked * @property {string} [customId] A unique string to be sent in the interaction when clicked
* @property {string} [placeholder] Custom placeholder text to display when nothing is selected * @property {string} [placeholder] Custom placeholder text to display when nothing is selected
* @property {number} [minValues] The minimum number of selections required * @property {number} [minValues] The minimum number of selections required
* @property {number} [maxValues] The maximum number of selections allowed * @property {number} [maxValues] The maximum number of selections allowed
* @property {MessageSelectOption[]} [options] Options for the select menu * @property {MessageSelectOption[]} [options] Options for the select menu
* @property {boolean} [disabled=false] Disables the select menu to prevent interactions * @property {boolean} [disabled=false] Disables the select menu to prevent interactions
*/ */
/** /**
* @typedef {Object} MessageSelectOption * @typedef {Object} MessageSelectOption
* @property {string} label The text to be displayed on this option * @property {string} label The text to be displayed on this option
* @property {string} value The value to be sent for this option * @property {string} value The value to be sent for this option
* @property {?string} description Optional description to show for this option * @property {?string} description Optional description to show for this option
* @property {?RawEmoji} emoji Emoji to display for this option * @property {?RawEmoji} emoji Emoji to display for this option
* @property {boolean} default Render this option as the default selection * @property {boolean} default Render this option as the default selection
*/ */
/** /**
* @typedef {Object} MessageSelectOptionData * @typedef {Object} MessageSelectOptionData
* @property {string} label The text to be displayed on this option * @property {string} label The text to be displayed on this option
* @property {string} value The value to be sent for this option * @property {string} value The value to be sent for this option
* @property {string} [description] Optional description to show for this option * @property {string} [description] Optional description to show for this option
* @property {EmojiIdentifierResolvable} [emoji] Emoji to display for this option * @property {EmojiIdentifierResolvable} [emoji] Emoji to display for this option
* @property {boolean} [default] Render this option as the default selection * @property {boolean} [default] Render this option as the default selection
*/ */
/** /**
* @param {MessageSelectMenu|MessageSelectMenuOptions} [data={}] MessageSelectMenu to clone or raw data * @param {MessageSelectMenu|MessageSelectMenuOptions} [data={}] MessageSelectMenu to clone or raw data
*/ */
constructor(data = {}) { constructor(data = {}) {
super({ type: 'SELECT_MENU' }); super({ type: 'SELECT_MENU' });
this.setup(data); this.setup(data);
} }
setup(data) { setup(data) {
/** /**
* A unique string to be sent in the interaction when clicked * A unique string to be sent in the interaction when clicked
* @type {?string} * @type {?string}
*/ */
this.customId = data.custom_id ?? data.customId ?? null; this.customId = data.custom_id ?? data.customId ?? null;
/** /**
* Custom placeholder text to display when nothing is selected * Custom placeholder text to display when nothing is selected
* @type {?string} * @type {?string}
*/ */
this.placeholder = data.placeholder ?? null; this.placeholder = data.placeholder ?? null;
/** /**
* The minimum number of selections required * The minimum number of selections required
* @type {?number} * @type {?number}
*/ */
this.minValues = data.min_values ?? data.minValues ?? null; this.minValues = data.min_values ?? data.minValues ?? null;
/** /**
* The maximum number of selections allowed * The maximum number of selections allowed
* @type {?number} * @type {?number}
*/ */
this.maxValues = data.max_values ?? data.maxValues ?? null; this.maxValues = data.max_values ?? data.maxValues ?? null;
/** /**
* Options for the select menu * Options for the select menu
* @type {MessageSelectOption[]} * @type {MessageSelectOption[]}
*/ */
this.options = this.constructor.normalizeOptions(data.options ?? []); this.options = this.constructor.normalizeOptions(data.options ?? []);
/** /**
* Whether this select menu is currently disabled * Whether this select menu is currently disabled
* @type {boolean} * @type {boolean}
*/ */
this.disabled = data.disabled ?? false; this.disabled = data.disabled ?? false;
} }
/** /**
* Sets the custom id of this select menu * Sets the custom id of this select menu
* @param {string} customId A unique string to be sent in the interaction when clicked * @param {string} customId A unique string to be sent in the interaction when clicked
* @returns {MessageSelectMenu} * @returns {MessageSelectMenu}
*/ */
setCustomId(customId) { setCustomId(customId) {
this.customId = Util.verifyString(customId, RangeError, 'SELECT_MENU_CUSTOM_ID'); this.customId = Util.verifyString(customId, RangeError, 'SELECT_MENU_CUSTOM_ID');
return this; return this;
} }
/** /**
* Sets the interactive status of the select menu * Sets the interactive status of the select menu
* @param {boolean} [disabled=true] Whether this select menu should be disabled * @param {boolean} [disabled=true] Whether this select menu should be disabled
* @returns {MessageSelectMenu} * @returns {MessageSelectMenu}
*/ */
setDisabled(disabled = true) { setDisabled(disabled = true) {
this.disabled = disabled; this.disabled = disabled;
return this; return this;
} }
/** /**
* Sets the maximum number of selections allowed for this select menu * Sets the maximum number of selections allowed for this select menu
* @param {number} maxValues Number of selections to be allowed * @param {number} maxValues Number of selections to be allowed
* @returns {MessageSelectMenu} * @returns {MessageSelectMenu}
*/ */
setMaxValues(maxValues) { setMaxValues(maxValues) {
this.maxValues = maxValues; this.maxValues = maxValues;
return this; return this;
} }
/** /**
* Sets the minimum number of selections required for this select menu * Sets the minimum number of selections required for this select menu
* <info>This will default the maxValues to the number of options, unless manually set</info> * <info>This will default the maxValues to the number of options, unless manually set</info>
* @param {number} minValues Number of selections to be required * @param {number} minValues Number of selections to be required
* @returns {MessageSelectMenu} * @returns {MessageSelectMenu}
*/ */
setMinValues(minValues) { setMinValues(minValues) {
this.minValues = minValues; this.minValues = minValues;
return this; return this;
} }
/** /**
* Sets the placeholder of this select menu * Sets the placeholder of this select menu
* @param {string} placeholder Custom placeholder text to display when nothing is selected * @param {string} placeholder Custom placeholder text to display when nothing is selected
* @returns {MessageSelectMenu} * @returns {MessageSelectMenu}
*/ */
setPlaceholder(placeholder) { setPlaceholder(placeholder) {
this.placeholder = Util.verifyString(placeholder, RangeError, 'SELECT_MENU_PLACEHOLDER'); this.placeholder = Util.verifyString(placeholder, RangeError, 'SELECT_MENU_PLACEHOLDER');
return this; return this;
} }
/** /**
* Adds options to the select menu. * Adds options to the select menu.
* @param {...MessageSelectOptionData|MessageSelectOptionData[]} options The options to add * @param {...MessageSelectOptionData|MessageSelectOptionData[]} options The options to add
* @returns {MessageSelectMenu} * @returns {MessageSelectMenu}
*/ */
addOptions(...options) { addOptions(...options) {
this.options.push(...this.constructor.normalizeOptions(options)); this.options.push(...this.constructor.normalizeOptions(options));
return this; return this;
} }
/** /**
* Sets the options of the select menu. * Sets the options of the select menu.
* @param {...MessageSelectOptionData|MessageSelectOptionData[]} options The options to set * @param {...MessageSelectOptionData|MessageSelectOptionData[]} options The options to set
* @returns {MessageSelectMenu} * @returns {MessageSelectMenu}
*/ */
setOptions(...options) { setOptions(...options) {
this.spliceOptions(0, this.options.length, options); this.spliceOptions(0, this.options.length, options);
return this; return this;
} }
/** /**
* Removes, replaces, and inserts options in the select menu. * Removes, replaces, and inserts options in the select menu.
* @param {number} index The index to start at * @param {number} index The index to start at
* @param {number} deleteCount The number of options to remove * @param {number} deleteCount The number of options to remove
* @param {...MessageSelectOptionData|MessageSelectOptionData[]} [options] The replacing option objects * @param {...MessageSelectOptionData|MessageSelectOptionData[]} [options] The replacing option objects
* @returns {MessageSelectMenu} * @returns {MessageSelectMenu}
*/ */
spliceOptions(index, deleteCount, ...options) { spliceOptions(index, deleteCount, ...options) {
this.options.splice(index, deleteCount, ...this.constructor.normalizeOptions(...options)); this.options.splice(index, deleteCount, ...this.constructor.normalizeOptions(...options));
return this; return this;
} }
/** /**
* Transforms the select menu into a plain object * Transforms the select menu into a plain object
* @returns {APIMessageSelectMenu} The raw data of this select menu * @returns {APIMessageSelectMenu} The raw data of this select menu
*/ */
toJSON() { toJSON() {
return { return {
custom_id: this.customId, custom_id: this.customId,
disabled: this.disabled, disabled: this.disabled,
placeholder: this.placeholder, placeholder: this.placeholder,
min_values: this.minValues, min_values: this.minValues,
max_values: this.maxValues ?? (this.minValues ? this.options.length : undefined), max_values: this.maxValues ?? (this.minValues ? this.options.length : undefined),
options: this.options, options: this.options,
type: typeof this.type === 'string' ? MessageComponentTypes[this.type] : this.type, type: typeof this.type === 'string' ? MessageComponentTypes[this.type] : this.type,
}; };
} }
/** /**
* Normalizes option input and resolves strings and emojis. * Normalizes option input and resolves strings and emojis.
* @param {MessageSelectOptionData} option The select menu option to normalize * @param {MessageSelectOptionData} option The select menu option to normalize
* @returns {MessageSelectOption} * @returns {MessageSelectOption}
*/ */
static normalizeOption(option) { static normalizeOption(option) {
let { label, value, description, emoji } = option; let { label, value, description, emoji } = option;
label = Util.verifyString(label, RangeError, 'SELECT_OPTION_LABEL'); label = Util.verifyString(label, RangeError, 'SELECT_OPTION_LABEL');
value = Util.verifyString(value, RangeError, 'SELECT_OPTION_VALUE'); value = Util.verifyString(value, RangeError, 'SELECT_OPTION_VALUE');
emoji = emoji ? Util.resolvePartialEmoji(emoji) : null; emoji = emoji ? Util.resolvePartialEmoji(emoji) : null;
description = description ? Util.verifyString(description, RangeError, 'SELECT_OPTION_DESCRIPTION', true) : null; description = description ? Util.verifyString(description, RangeError, 'SELECT_OPTION_DESCRIPTION', true) : null;
return { label, value, description, emoji, default: option.default ?? false }; return { label, value, description, emoji, default: option.default ?? false };
} }
/** /**
* Normalizes option input and resolves strings and emojis. * Normalizes option input and resolves strings and emojis.
* @param {...MessageSelectOptionData|MessageSelectOptionData[]} options The select menu options to normalize * @param {...MessageSelectOptionData|MessageSelectOptionData[]} options The select menu options to normalize
* @returns {MessageSelectOption[]} * @returns {MessageSelectOption[]}
*/ */
static normalizeOptions(...options) { static normalizeOptions(...options) {
return options.flat(Infinity).map(option => this.normalizeOption(option)); return options.flat(Infinity).map(option => this.normalizeOption(option));
} }
// Add // Add
/** /**
* Select in menu * Select in menu
* @param {Message} message Discord Message * @param {Message} message Discord Message
* @param {Array<String>} values Option values * @param {Array<String>} values Option values
* @returns {Promise<boolean} * @returns {Promise<boolean}
*/ */
async select(message, values = []) { async select(message, values = []) {
// Github copilot is the best :)) // Github copilot is the best :))
// POST data from https://github.com/phamleduy04 // POST data from https://github.com/phamleduy04
if (!message instanceof Message) throw new Error("[UNKNOWN_MESSAGE] Please pass a valid Message"); if (!message instanceof Message) throw new Error('[UNKNOWN_MESSAGE] Please pass a valid Message');
if (!Array.isArray(values)) throw new TypeError("[INVALID_VALUES] Please pass an array of values"); if (!Array.isArray(values)) throw new TypeError('[INVALID_VALUES] Please pass an array of values');
if (!this.customId || this.disabled || values.length == 0) return false; // Disabled or null customID or [] array if (!this.customId || this.disabled || values.length == 0) return false; // Disabled or null customID or [] array
// Check value is invalid [Max options is 20] => For loop // Check value is invalid [Max options is 20] => For loop
if (values.length < this.minValues) throw new RangeError("[SELECT_MENU_MIN_VALUES] The minimum number of values is " + this.minValues); if (values.length < this.minValues)
if (values.length > this.maxValues) throw new RangeError("[SELECT_MENU_MAX_VALUES] The maximum number of values is " + this.maxValues); throw new RangeError('[SELECT_MENU_MIN_VALUES] The minimum number of values is ' + this.minValues);
const validValue = this.options.map(obj => obj.value); if (values.length > this.maxValues)
const check_ = await values.find(element => { throw new RangeError('[SELECT_MENU_MAX_VALUES] The maximum number of values is ' + this.maxValues);
if (typeof element !== 'string') return true; const validValue = this.options.map(obj => obj.value);
if (!validValue.includes(element)) return true; const check_ = await values.find(element => {
return false; if (typeof element !== 'string') return true;
}) if (!validValue.includes(element)) return true;
if (check_) throw new RangeError("[SELECT_MENU_INVALID_VALUE] The value " + check_ + " is invalid. Please use a valid value " + validValue.join(', ')); return false;
await message.client.api.interactions.post( });
{ if (check_)
data: { throw new RangeError(
type: 3, // ? '[SELECT_MENU_INVALID_VALUE] The value ' +
guild_id: message.guild?.id ?? null, // In DMs check_ +
channel_id: message.channel.id, ' is invalid. Please use a valid value ' +
message_id: message.id, validValue.join(', '),
application_id: message.author.id, );
session_id: message.client.session_id, await message.client.api.interactions.post({
data: { data: {
component_type: 3, // Select Menu type: 3, // ?
custom_id: this.customId, guild_id: message.guild?.id ?? null, // In DMs
type: 3, // Select Menu channel_id: message.channel.id,
values, message_id: message.id,
}, application_id: message.author.id,
} session_id: message.client.session_id,
} data: {
) component_type: 3, // Select Menu
return true; custom_id: this.customId,
} type: 3, // Select Menu
} values,
},
module.exports = MessageSelectMenu; },
});
return true;
}
}
module.exports = MessageSelectMenu;

View File

@@ -1,15 +1,13 @@
'use strict'; 'use strict';
const { Collection } = require('@discordjs/collection');
const { Channel } = require('./Channel'); const { Channel } = require('./Channel');
const { Error } = require('../errors');
const { Collection } = require('discord.js');
const { Message } = require('./Message');
const MessageManager = require('../managers/MessageManager');
const User = require('./User');
const DataResolver = require('../util/DataResolver');
const TextBasedChannel = require('./interfaces/TextBasedChannel');
const Invite = require('./Invite'); const Invite = require('./Invite');
const User = require('./User');
const TextBasedChannel = require('./interfaces/TextBasedChannel');
const { Error } = require('../errors');
const MessageManager = require('../managers/MessageManager');
const DataResolver = require('../util/DataResolver');
/** /**
* Represents a Partial Group DM Channel on Discord. * Represents a Partial Group DM Channel on Discord.
@@ -77,19 +75,16 @@ class PartialGroupDMChannel extends Channel {
} }
/** /**
* *
* @param {Discord.Client} client * @param {Discord.Client} client
* @param {object} data * @param {Object} data
* @private * @private
*/ */
_setup(client, data) { _setup(client, data) {
if ('recipients' in data) { if ('recipients' in data) {
Promise.all( Promise.all(
data.recipients.map((recipient) => { data.recipients.map(recipient => {
this.recipients.set( this.recipients.set(recipient.id, client.users.cache.get(data.owner_id) || recipient);
recipient.id,
client.users.cache.get(data.owner_id) || recipient,
);
}), }),
); );
} }
@@ -103,16 +98,17 @@ class PartialGroupDMChannel extends Channel {
} }
/** /**
* *
* @param {Object} data name, icon * @param {Object} data name, icon
* @returns * @returns
* @private * @private
*/ */
async edit(data) { async edit(data) {
const _data = {}; const _data = {};
if ('name' in data) _data.name = data.name?.trim() ?? null; if ('name' in data) _data.name = data.name?.trim() ?? null;
if (typeof data.icon !== 'undefined') if (typeof data.icon !== 'undefined') {
_data.icon = await DataResolver.resolveImage(data.icon); _data.icon = await DataResolver.resolveImage(data.icon);
}
const newData = await this.client.api.channels(this.id).patch({ const newData = await this.client.api.channels(this.id).patch({
data: _data, data: _data,
}); });
@@ -126,19 +122,16 @@ class PartialGroupDMChannel extends Channel {
* @returns {?string} * @returns {?string}
*/ */
iconURL({ format, size } = {}) { iconURL({ format, size } = {}) {
return ( return this.icon && this.client.rest.cdn.GDMIcon(this.id, this.icon, format, size);
this.icon &&
this.client.rest.cdn.GDMIcon(this.id, this.icon, format, size)
);
} }
async addMember(user) { async addMember(user) {
if (this.ownerId !== this.client.user.id) if (this.ownerId !== this.client.user.id) {
return Promise.reject(new Error('NOT_OWNER_GROUP_DM_CHANNEL')); return Promise.reject(new Error('NOT_OWNER_GROUP_DM_CHANNEL'));
if (!user instanceof User) }
return Promise.reject( if (!user instanceof User) {
new TypeError('User is not an instance of Discord.User'), return Promise.reject(new TypeError('User is not an instance of Discord.User'));
); }
if (this.recipients.get(user.id)) return Promise.reject(new Error('USER_ALREADY_IN_GROUP_DM_CHANNEL')); if (this.recipients.get(user.id)) return Promise.reject(new Error('USER_ALREADY_IN_GROUP_DM_CHANNEL'));
// //
await this.client.api.channels[this.id].recipients[user.id].put(); await this.client.api.channels[this.id].recipients[user.id].put();
@@ -147,12 +140,12 @@ class PartialGroupDMChannel extends Channel {
} }
async removeMember(user) { async removeMember(user) {
if (this.ownerId !== this.client.user.id) if (this.ownerId !== this.client.user.id) {
return Promise.reject(new Error('NOT_OWNER_GROUP_DM_CHANNEL')); return Promise.reject(new Error('NOT_OWNER_GROUP_DM_CHANNEL'));
if (!user instanceof User) }
return Promise.reject( if (!user instanceof User) {
new TypeError('User is not an instance of Discord.User'), return Promise.reject(new TypeError('User is not an instance of Discord.User'));
); }
if (!this.recipients.get(user.id)) return Promise.reject(new Error('USER_NOT_IN_GROUP_DM_CHANNEL')); if (!this.recipients.get(user.id)) return Promise.reject(new Error('USER_NOT_IN_GROUP_DM_CHANNEL'));
await this.client.api.channels[this.id].recipients[user.id].delete(); await this.client.api.channels[this.id].recipients[user.id].delete();
this.recipients.delete(user.id); this.recipients.delete(user.id);
@@ -169,18 +162,19 @@ class PartialGroupDMChannel extends Channel {
async getInvite() { async getInvite() {
const inviteCode = await this.client.api.channels(this.id).invites.post({ const inviteCode = await this.client.api.channels(this.id).invites.post({
data: { data: {
max_age: 86400, max_age: 86400,
}, },
}); });
const invite = new Invite(this.client, inviteCode); const invite = new Invite(this.client, inviteCode);
this.invites.set(invite.code, invite); this.invites.set(invite.code, invite);
return invite; return invite;
} }
async fetchInvite(force = false) { async fetchInvite(force = false) {
if (this.ownerId !== this.client.user.id) if (this.ownerId !== this.client.user.id) {
return Promise.reject(new Error('NOT_OWNER_GROUP_DM_CHANNEL')); return Promise.reject(new Error('NOT_OWNER_GROUP_DM_CHANNEL'));
}
if (!force && this.invites.size) return this.invites; if (!force && this.invites.size) return this.invites;
const invites = await this.client.api.channels(this.id).invites.get(); const invites = await this.client.api.channels(this.id).invites.get();
await Promise.all(invites.map(invite => this.invites.set(invite.code, new Invite(this.client, invite)))); await Promise.all(invites.map(invite => this.invites.set(invite.code, new Invite(this.client, invite))));
@@ -188,10 +182,12 @@ class PartialGroupDMChannel extends Channel {
} }
async removeInvite(invite) { async removeInvite(invite) {
if (this.ownerId !== this.client.user.id) if (this.ownerId !== this.client.user.id) {
return Promise.reject(new Error('NOT_OWNER_GROUP_DM_CHANNEL')); return Promise.reject(new Error('NOT_OWNER_GROUP_DM_CHANNEL'));
if (!invite instanceof Invite) }
if (!invite instanceof Invite) {
return Promise.reject(new TypeError('Invite is not an instance of Discord.Invite')); return Promise.reject(new TypeError('Invite is not an instance of Discord.Invite'));
}
await this.client.api.channels(this.id).invites[invite.code].delete(); await this.client.api.channels(this.id).invites[invite.code].delete();
this.invites.delete(invite.code); this.invites.delete(invite.code);
return this; return this;
@@ -199,10 +195,10 @@ class PartialGroupDMChannel extends Channel {
// These are here only for documentation purposes - they are implemented by TextBasedChannel // These are here only for documentation purposes - they are implemented by TextBasedChannel
/* eslint-disable no-empty-function */ /* eslint-disable no-empty-function */
get lastMessage() { } get lastMessage() {}
get lastPinAt() { } get lastPinAt() {}
send() { } send() {}
sendTyping() { } sendTyping() {}
} }
TextBasedChannel.applyToClass(PartialGroupDMChannel, false); TextBasedChannel.applyToClass(PartialGroupDMChannel, false);

View File

@@ -15,442 +15,421 @@ const { Relationship } = require('../util/Constants');
* @extends {Base} * @extends {Base}
*/ */
class User extends Base { class User extends Base {
constructor(client, data) { constructor(client, data) {
super(client); super(client);
/** /**
* The user's id * The user's id
* @type {Snowflake} * @type {Snowflake}
*/ */
this.id = data.id; this.id = data.id;
this.bot = null; this.bot = null;
this.system = null; this.system = null;
this.flags = null; this.flags = null;
// Code written by https://github.com/aiko-chan-ai // Code written by https://github.com/aiko-chan-ai
this.connectedAccounts = []; this.connectedAccounts = [];
this.premiumSince = null; this.premiumSince = null;
this.premiumGuildSince = null; this.premiumGuildSince = null;
this.mutualGuilds = new Collection(); this.mutualGuilds = new Collection();
this.applications = null; this.applications = null;
this._patch(data); this._patch(data);
} }
_patch(data) { _patch(data) {
if ('username' in data) { if ('username' in data) {
/** /**
* The username of the user * The username of the user
* @type {?string} * @type {?string}
*/ */
this.username = data.username; this.username = data.username;
} else { } else {
this.username ??= null; this.username ??= null;
} }
if ('bot' in data) { if ('bot' in data) {
/** /**
* Whether or not the user is a bot * Whether or not the user is a bot
* @type {?boolean} * @type {?boolean}
*/ */
this.bot = Boolean(data.bot); this.bot = Boolean(data.bot);
if (this.bot == true) { if (this.bot == true) {
this.applications = new ApplicationCommandManager( this.applications = new ApplicationCommandManager(this.client, undefined, this);
this.client, }
undefined, } else if (!this.partial && typeof this.bot !== 'boolean') {
this, this.bot = false;
); }
}
} else if (!this.partial && typeof this.bot !== 'boolean') {
this.bot = false;
}
if ('discriminator' in data) { if ('discriminator' in data) {
/** /**
* A discriminator based on username for the user * A discriminator based on username for the user
* @type {?string} * @type {?string}
*/ */
this.discriminator = data.discriminator; this.discriminator = data.discriminator;
} else { } else {
this.discriminator ??= null; this.discriminator ??= null;
} }
if ('avatar' in data) { if ('avatar' in data) {
/** /**
* The user avatar's hash * The user avatar's hash
* @type {?string} * @type {?string}
*/ */
this.avatar = data.avatar; this.avatar = data.avatar;
} else { } else {
this.avatar ??= null; this.avatar ??= null;
} }
if ('banner' in data) { if ('banner' in data) {
/** /**
* The user banner's hash * The user banner's hash
* <info>The user must be force fetched for this property to be present or be updated</info> * <info>The user must be force fetched for this property to be present or be updated</info>
* @type {?string} * @type {?string}
*/ */
this.banner = data.banner; this.banner = data.banner;
} else if (this.banner !== null) { } else if (this.banner !== null) {
this.banner ??= undefined; this.banner ??= undefined;
} }
if ('accent_color' in data) { if ('accent_color' in data) {
/** /**
* The base 10 accent color of the user's banner * The base 10 accent color of the user's banner
* <info>The user must be force fetched for this property to be present or be updated</info> * <info>The user must be force fetched for this property to be present or be updated</info>
* @type {?number} * @type {?number}
*/ */
this.accentColor = data.accent_color; this.accentColor = data.accent_color;
} else if (this.accentColor !== null) { } else if (this.accentColor !== null) {
this.accentColor ??= undefined; this.accentColor ??= undefined;
} }
if ('system' in data) { if ('system' in data) {
/** /**
* Whether the user is an Official Discord System user (part of the urgent message system) * Whether the user is an Official Discord System user (part of the urgent message system)
* @type {?boolean} * @type {?boolean}
*/ */
this.system = Boolean(data.system); this.system = Boolean(data.system);
} else if (!this.partial && typeof this.system !== 'boolean') { } else if (!this.partial && typeof this.system !== 'boolean') {
this.system = false; this.system = false;
} }
if ('public_flags' in data) { if ('public_flags' in data) {
/** /**
* The flags for this user * The flags for this user
* @type {?UserFlags} * @type {?UserFlags}
*/ */
this.flags = new UserFlags(data.public_flags); this.flags = new UserFlags(data.public_flags);
} }
} }
/** /**
* Check relationship status * Check relationship status
* @readonly * @readonly
*/ */
get relationships() { get relationships() {
const i = this.client.relationships.cache.get(this.id) ?? 0; const i = this.client.relationships.cache.get(this.id) ?? 0;
return Relationship[parseInt(i)]; return Relationship[parseInt(i)];
} }
/** /**
* Check note * Check note
* @readonly * @readonly
*/ */
get note() { get note() {
return this.client.user.notes.get(this.id); return this.client.user.notes.get(this.id);
} }
// Code written by https://github.com/aiko-chan-ai // Code written by https://github.com/aiko-chan-ai
_ProfilePatch(data) { _ProfilePatch(data) {
if (!data) return; if (!data) return;
if (data.connected_accounts.length > 0) if (data.connected_accounts.length > 0) {
this.connectedAccounts = data.connected_accounts; this.connectedAccounts = data.connected_accounts;
}
if ('premium_since' in data) { if ('premium_since' in data) {
const date = new Date(data.premium_since); const date = new Date(data.premium_since);
this.premiumSince = date.getTime(); this.premiumSince = date.getTime();
} }
if ('premium_guild_since' in data) { if ('premium_guild_since' in data) {
const date = new Date(data.premium_guild_since); const date = new Date(data.premium_guild_since);
this.premiumGuildSince = date.getTime(); this.premiumGuildSince = date.getTime();
} }
this.mutualGuilds = new Collection( this.mutualGuilds = new Collection(data.mutual_guilds.map(obj => [obj.id, obj]));
data.mutual_guilds.map((obj) => [obj.id, obj]), }
);
}
/** /**
* Get profile from Discord, if client is in a server with the target. * Get profile from Discord, if client is in a server with the target.
* <br>Code written by https://github.com/aiko-chan-ai * <br>Code written by https://github.com/aiko-chan-ai
*/ */
async getProfile() { async getProfile() {
if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); if (this.client.bot) throw new Error('INVALID_BOT_METHOD');
try { try {
const data = await this.client.api.users(this.id).profile.get(); const data = await this.client.api.users(this.id).profile.get();
this._ProfilePatch(data); this._ProfilePatch(data);
return this; return this;
} catch (e) { } catch (e) {
throw e; throw e;
} }
} }
/** /**
* Friends the user and send Request [If no request] * Friends the user and send Request [If no request]
* @returns {Promise<User>} the user object * @returns {Promise<User>} the user object
*/ */
async setFriend() { async setFriend() {
return this.client.relationships.addFriend(this); return this.client.relationships.addFriend(this);
} }
/** /**
* Send Friend Request to the user * Send Friend Request to the user
* @returns {Promise<User>} the user object * @returns {Promise<User>} the user object
*/ */
async sendFriendRequest() { async sendFriendRequest() {
return this.client.relationships.sendFriendRequest( return this.client.relationships.sendFriendRequest(this.username, this.discriminator);
this.username, }
this.discriminator, /**
); * Blocks the user
} * @returns {Promise<User>} the user object
/** */
* Blocks the user async setBlock() {
* @returns {Promise<User>} the user object return this.client.relationships.addBlocked(this);
*/ }
async setBlock() {
return this.client.relationships.addBlocked(this);
}
/** /**
* Removes the user from your blocks list * Removes the user from your blocks list
* @returns {Promise<User>} the user object * @returns {Promise<User>} the user object
*/ */
async unBlock() { async unBlock() {
return this.client.relationships.deleteBlocked(this); return this.client.relationships.deleteBlocked(this);
} }
/** /**
* Removes the user from your friends list * Removes the user from your friends list
* @returns {Promise<User>} the user object * @returns {Promise<User>} the user object
*/ */
unFriend() { unFriend() {
return this.client.relationships.deleteFriend(this); return this.client.relationships.deleteFriend(this);
} }
/** /**
* Whether this User is a partial * Whether this User is a partial
* @type {boolean} * @type {boolean}
* @readonly * @readonly
*/ */
get partial() { get partial() {
return typeof this.username !== 'string'; return typeof this.username !== 'string';
} }
/** /**
* The timestamp the user was created at * The timestamp the user was created at
* @type {number} * @type {number}
* @readonly * @readonly
*/ */
get createdTimestamp() { get createdTimestamp() {
return SnowflakeUtil.timestampFrom(this.id); return SnowflakeUtil.timestampFrom(this.id);
} }
/** /**
* The time the user was created at * The time the user was created at
* @type {Date} * @type {Date}
* @readonly * @readonly
*/ */
get createdAt() { get createdAt() {
return new Date(this.createdTimestamp); return new Date(this.createdTimestamp);
} }
/** /**
* A link to the user's avatar. * A link to the user's avatar.
* @param {ImageURLOptions} [options={}] Options for the Image URL * @param {ImageURLOptions} [options={}] Options for the Image URL
* @returns {?string} * @returns {?string}
*/ */
avatarURL({ format, size, dynamic } = {}) { avatarURL({ format, size, dynamic } = {}) {
if (!this.avatar) return null; if (!this.avatar) return null;
return this.client.rest.cdn.Avatar( return this.client.rest.cdn.Avatar(this.id, this.avatar, format, size, dynamic);
this.id, }
this.avatar,
format,
size,
dynamic,
);
}
/** /**
* A link to the user's default avatar * A link to the user's default avatar
* @type {string} * @type {string}
* @readonly * @readonly
*/ */
get defaultAvatarURL() { get defaultAvatarURL() {
return this.client.rest.cdn.DefaultAvatar(this.discriminator % 5); return this.client.rest.cdn.DefaultAvatar(this.discriminator % 5);
} }
/** /**
* A link to the user's avatar if they have one. * A link to the user's avatar if they have one.
* Otherwise a link to their default avatar will be returned. * Otherwise a link to their default avatar will be returned.
* @param {ImageURLOptions} [options={}] Options for the Image URL * @param {ImageURLOptions} [options={}] Options for the Image URL
* @returns {string} * @returns {string}
*/ */
displayAvatarURL(options) { displayAvatarURL(options) {
return this.avatarURL(options) ?? this.defaultAvatarURL; return this.avatarURL(options) ?? this.defaultAvatarURL;
} }
/** /**
* The hexadecimal version of the user accent color, with a leading hash * The hexadecimal version of the user accent color, with a leading hash
* <info>The user must be force fetched for this property to be present</info> * <info>The user must be force fetched for this property to be present</info>
* @type {?string} * @type {?string}
* @readonly * @readonly
*/ */
get hexAccentColor() { get hexAccentColor() {
if (typeof this.accentColor !== 'number') return this.accentColor; if (typeof this.accentColor !== 'number') return this.accentColor;
return `#${this.accentColor.toString(16).padStart(6, '0')}`; return `#${this.accentColor.toString(16).padStart(6, '0')}`;
} }
/** /**
* A link to the user's banner. * A link to the user's banner.
* <info>This method will throw an error if called before the user is force fetched. * <info>This method will throw an error if called before the user is force fetched.
* See {@link User#banner} for more info</info> * See {@link User#banner} for more info</info>
* @param {ImageURLOptions} [options={}] Options for the Image URL * @param {ImageURLOptions} [options={}] Options for the Image URL
* @returns {?string} * @returns {?string}
*/ */
bannerURL({ format, size, dynamic } = {}) { bannerURL({ format, size, dynamic } = {}) {
if (typeof this.banner === 'undefined') if (typeof this.banner === 'undefined') {
throw new Error('USER_BANNER_NOT_FETCHED'); throw new Error('USER_BANNER_NOT_FETCHED');
if (!this.banner) return null; }
return this.client.rest.cdn.Banner( if (!this.banner) return null;
this.id, return this.client.rest.cdn.Banner(this.id, this.banner, format, size, dynamic);
this.banner, }
format,
size,
dynamic,
);
}
/** /**
* The Discord "tag" (e.g. `hydrabolt#0001`) for this user * The Discord "tag" (e.g. `hydrabolt#0001`) for this user
* @type {?string} * @type {?string}
* @readonly * @readonly
*/ */
get tag() { get tag() {
return typeof this.username === 'string' return typeof this.username === 'string' ? `${this.username}#${this.discriminator}` : null;
? `${this.username}#${this.discriminator}` }
: null;
}
/** /**
* The DM between the client's user and this user * The DM between the client's user and this user
* @type {?DMChannel} * @type {?DMChannel}
* @readonly * @readonly
*/ */
get dmChannel() { get dmChannel() {
return this.client.users.dmChannel(this.id); return this.client.users.dmChannel(this.id);
} }
/** /**
* Creates a DM channel between the client and the user. * Creates a DM channel between the client and the user.
* @param {boolean} [force=false] Whether to skip the cache check and request the API * @param {boolean} [force=false] Whether to skip the cache check and request the API
* @returns {Promise<DMChannel>} * @returns {Promise<DMChannel>}
*/ */
createDM(force = false) { createDM(force = false) {
return this.client.users.createDM(this.id, force); return this.client.users.createDM(this.id, force);
} }
/** /**
* Deletes a DM channel (if one exists) between the client and the user. Resolves with the channel if successful. * Deletes a DM channel (if one exists) between the client and the user. Resolves with the channel if successful.
* @returns {Promise<DMChannel>} * @returns {Promise<DMChannel>}
*/ */
deleteDM() { deleteDM() {
return this.client.users.deleteDM(this.id); return this.client.users.deleteDM(this.id);
} }
/** /**
* Checks if the user is equal to another. * Checks if the user is equal to another.
* It compares id, username, discriminator, avatar, banner, accent color, and bot flags. * It compares id, username, discriminator, avatar, banner, accent color, and bot flags.
* It is recommended to compare equality by using `user.id === user2.id` unless you want to compare all properties. * It is recommended to compare equality by using `user.id === user2.id` unless you want to compare all properties.
* @param {User} user User to compare with * @param {User} user User to compare with
* @returns {boolean} * @returns {boolean}
*/ */
equals(user) { equals(user) {
return ( return (
user && user &&
this.id === user.id && this.id === user.id &&
this.username === user.username && this.username === user.username &&
this.discriminator === user.discriminator && this.discriminator === user.discriminator &&
this.avatar === user.avatar && this.avatar === user.avatar &&
this.flags?.bitfield === user.flags?.bitfield && this.flags?.bitfield === user.flags?.bitfield &&
this.banner === user.banner && this.banner === user.banner &&
this.accentColor === user.accentColor this.accentColor === user.accentColor
); );
} }
/** /**
* Compares the user with an API user object * Compares the user with an API user object
* @param {APIUser} user The API user object to compare * @param {APIUser} user The API user object to compare
* @returns {boolean} * @returns {boolean}
* @private * @private
*/ */
_equals(user) { _equals(user) {
return ( return (
user && user &&
this.id === user.id && this.id === user.id &&
this.username === user.username && this.username === user.username &&
this.discriminator === user.discriminator && this.discriminator === user.discriminator &&
this.avatar === user.avatar && this.avatar === user.avatar &&
this.flags?.bitfield === user.public_flags && this.flags?.bitfield === user.public_flags &&
('banner' in user ? this.banner === user.banner : true) && ('banner' in user ? this.banner === user.banner : true) &&
('accent_color' in user ? this.accentColor === user.accent_color : true) ('accent_color' in user ? this.accentColor === user.accent_color : true)
); );
} }
/** /**
* Fetches this user's flags. * Fetches this user's flags.
* @param {boolean} [force=false] Whether to skip the cache check and request the API * @param {boolean} [force=false] Whether to skip the cache check and request the API
* @returns {Promise<UserFlags>} * @returns {Promise<UserFlags>}
*/ */
fetchFlags(force = false) { fetchFlags(force = false) {
return this.client.users.fetchFlags(this.id, { force }); return this.client.users.fetchFlags(this.id, { force });
} }
/** /**
* Fetches this user. * Fetches this user.
* @param {boolean} [force=true] Whether to skip the cache check and request the API * @param {boolean} [force=true] Whether to skip the cache check and request the API
* @returns {Promise<User>} * @returns {Promise<User>}
*/ */
fetch(force = true) { fetch(force = true) {
return this.client.users.fetch(this.id, { force }); return this.client.users.fetch(this.id, { force });
} }
/** /**
* When concatenated with a string, this automatically returns the user's mention instead of the User object. * When concatenated with a string, this automatically returns the user's mention instead of the User object.
* @returns {string} * @returns {string}
* @example * @example
* // Logs: Hello from <@123456789012345678>! * // Logs: Hello from <@123456789012345678>!
* console.log(`Hello from ${user}!`); * console.log(`Hello from ${user}!`);
*/ */
toString() { toString() {
return `<@${this.id}>`; return `<@${this.id}>`;
} }
toJSON(...props) { toJSON(...props) {
const json = super.toJSON( const json = super.toJSON(
{ {
createdTimestamp: true, createdTimestamp: true,
defaultAvatarURL: true, defaultAvatarURL: true,
hexAccentColor: true, hexAccentColor: true,
tag: true, tag: true,
}, },
...props, ...props,
); );
json.avatarURL = this.avatarURL(); json.avatarURL = this.avatarURL();
json.displayAvatarURL = this.displayAvatarURL(); json.displayAvatarURL = this.displayAvatarURL();
json.bannerURL = this.banner ? this.bannerURL() : this.banner; json.bannerURL = this.banner ? this.bannerURL() : this.banner;
return json; return json;
} }
/** /**
* Set note to user * Set note to user
* @param {String<User.note>} note Note to set * @param {String<User.note>} note Note to set
* @returns {Promise<User.note>} * @returns {Promise<User.note>}
*/ */
async setNote(note = null) { async setNote(note = null) {
await this.client.api.users['@me'].notes(id).put({ data: { note } }); await this.client.api.users['@me'].notes(id).put({ data: { note } });
return (this.note = note); return (this.note = note);
} }
// These are here only for documentation purposes - they are implemented by TextBasedChannel // These are here only for documentation purposes - they are implemented by TextBasedChannel
/* eslint-disable no-empty-function */ /* eslint-disable no-empty-function */
send() {} send() {}
} }
TextBasedChannel.applyToClass(User); TextBasedChannel.applyToClass(User);

View File

@@ -1,370 +1,358 @@
'use strict'; 'use strict';
const axios = require('axios'); const axios = require('axios');
const baseURL = 'https://sagiri-fansub.tk/embed?'; const baseURL = 'https://sagiri-fansub.tk/embed?';
const hiddenCharter = const hiddenCharter =
'||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||'; '||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||';
const { RangeError } = require('../errors'); const { RangeError } = require('../errors');
const Util = require('../util/Util'); const Util = require('../util/Util');
class WebEmbed { class WebEmbed {
constructor(data = {}) { constructor(data = {}) {
this._setup(data); this._setup(data);
/** /**
* Shorten the link * Shorten the link
* @type {?boolean} * @type {?boolean}
*/ */
this.shorten = data.shorten ?? true; this.shorten = data.shorten ?? true;
/** /**
* Hidden Embed link * Hidden Embed link
* @type {?boolean} * @type {?boolean}
*/ */
this.hidden = data.hidden ?? false; this.hidden = data.hidden ?? false;
} }
/** /**
* @private * @private
* @param {object} data * @param {object} data
*/ */
_setup(data) { _setup(data) {
/** /**
* Type image of this embed * Type image of this embed
* @type {?thumbnail | image} * @type {?thumbnail | image}
*/ */
this.imageType = 'thumbnail'; this.imageType = 'thumbnail';
/** /**
* The title of this embed * The title of this embed
* @type {?string} * @type {?string}
*/ */
this.title = data.title ?? null; this.title = data.title ?? null;
/** /**
* The description of this embed * The description of this embed
* @type {?string} * @type {?string}
*/ */
this.description = data.description ?? null; this.description = data.description ?? null;
/** /**
* The URL of this embed * The URL of this embed
* @type {?string} * @type {?string}
*/ */
this.url = data.url ?? null; this.url = data.url ?? null;
/** /**
* The color of this embed * The color of this embed
* @type {?number} * @type {?number}
*/ */
this.color = 'color' in data ? Util.resolveColor(data.color) : null; this.color = 'color' in data ? Util.resolveColor(data.color) : null;
/** /**
* Represents the image of a MessageEmbed * Represents the image of a MessageEmbed
* @typedef {Object} MessageEmbedImage * @typedef {Object} MessageEmbedImage
* @property {string} url URL for this image * @property {string} url URL for this image
* @property {string} proxyURL ProxyURL for this image * @property {string} proxyURL ProxyURL for this image
* @property {number} height Height of this image * @property {number} height Height of this image
* @property {number} width Width of this image * @property {number} width Width of this image
*/ */
/** /**
* The image of this embed, if there is one * The image of this embed, if there is one
* @type {?MessageEmbedImage} * @type {?MessageEmbedImage}
*/ */
this.image = data.image this.image = data.image
? { ? {
url: data.image.url, url: data.image.url,
proxyURL: data.image.proxyURL ?? data.image.proxy_url, proxyURL: data.image.proxyURL ?? data.image.proxy_url,
height: data.image.height, height: data.image.height,
width: data.image.width, width: data.image.width,
} }
: null; : null;
/** /**
* The thumbnail of this embed (if there is one) * The thumbnail of this embed (if there is one)
* @type {?MessageEmbedThumbnail} * @type {?MessageEmbedThumbnail}
*/ */
this.thumbnail = data.thumbnail this.thumbnail = data.thumbnail
? { ? {
url: data.thumbnail.url, url: data.thumbnail.url,
proxyURL: data.thumbnail.proxyURL ?? data.thumbnail.proxy_url, proxyURL: data.thumbnail.proxyURL ?? data.thumbnail.proxy_url,
height: data.thumbnail.height, height: data.thumbnail.height,
width: data.thumbnail.width, width: data.thumbnail.width,
} }
: null; : null;
/** /**
* Represents the video of a MessageEmbed * Represents the video of a MessageEmbed
* @typedef {Object} MessageEmbedVideo * @typedef {Object} MessageEmbedVideo
* @property {string} url URL of this video * @property {string} url URL of this video
* @property {string} proxyURL ProxyURL for this video * @property {string} proxyURL ProxyURL for this video
* @property {number} height Height of this video * @property {number} height Height of this video
* @property {number} width Width of this video * @property {number} width Width of this video
*/ */
/** /**
* The video of this embed (if there is one) * The video of this embed (if there is one)
* @type {?MessageEmbedVideo} * @type {?MessageEmbedVideo}
* @readonly * @readonly
*/ */
this.video = data.video this.video = data.video
? { ? {
url: data.video.url, url: data.video.url,
proxyURL: data.video.proxyURL ?? data.video.proxy_url, proxyURL: data.video.proxyURL ?? data.video.proxy_url,
height: data.video.height, height: data.video.height,
width: data.video.width, width: data.video.width,
} }
: null; : null;
/** /**
* Represents the author field of a MessageEmbed * Represents the author field of a MessageEmbed
* @typedef {Object} MessageEmbedAuthor * @typedef {Object} MessageEmbedAuthor
* @property {string} name The name of this author * @property {string} name The name of this author
* @property {string} url URL of this author * @property {string} url URL of this author
* @property {string} iconURL URL of the icon for this author * @property {string} iconURL URL of the icon for this author
* @property {string} proxyIconURL Proxied URL of the icon for this author * @property {string} proxyIconURL Proxied URL of the icon for this author
*/ */
/** /**
* The author of this embed (if there is one) * The author of this embed (if there is one)
* @type {?MessageEmbedAuthor} * @type {?MessageEmbedAuthor}
*/ */
this.author = data.author this.author = data.author
? { ? {
name: data.author.name, name: data.author.name,
url: data.author.url, url: data.author.url,
} }
: null; : null;
/** /**
* Represents the provider of a MessageEmbed * Represents the provider of a MessageEmbed
* @typedef {Object} MessageEmbedProvider * @typedef {Object} MessageEmbedProvider
* @property {string} name The name of this provider * @property {string} name The name of this provider
* @property {string} url URL of this provider * @property {string} url URL of this provider
*/ */
/** /**
* The provider of this embed (if there is one) * The provider of this embed (if there is one)
* @type {?MessageEmbedProvider} * @type {?MessageEmbedProvider}
*/ */
this.provider = data.provider this.provider = data.provider
? { ? {
name: data.provider.name, name: data.provider.name,
url: data.provider.name, url: data.provider.name,
} }
: null; : null;
} }
/** /**
* The options to provide for setting an author for a {@link MessageEmbed}. * The options to provide for setting an author for a {@link MessageEmbed}.
* @typedef {Object} EmbedAuthorData * @typedef {Object} EmbedAuthorData
* @property {string} name The name of this author. * @property {string} name The name of this author.
*/ */
/** /**
* Sets the author of this embed. * Sets the author of this embed.
* @param {string|EmbedAuthorData|null} options The options to provide for the author. * @param {string|EmbedAuthorData|null} options The options to provide for the author.
* Provide `null` to remove the author data. * Provide `null` to remove the author data.
* @returns {MessageEmbed} * @returns {MessageEmbed}
*/ */
setAuthor(options) { setAuthor(options) {
if (options === null) { if (options === null) {
this.author = {}; this.author = {};
return this; return this;
} }
const { name, url } = options; const { name, url } = options;
this.author = { this.author = {
name: Util.verifyString(name, RangeError, 'EMBED_AUTHOR_NAME'), name: Util.verifyString(name, RangeError, 'EMBED_AUTHOR_NAME'),
url, url,
}; };
return this; return this;
} }
/** /**
* The options to provide for setting an provider for a {@link MessageEmbed}. * The options to provide for setting an provider for a {@link MessageEmbed}.
* @typedef {Object} EmbedProviderData * @typedef {Object} EmbedProviderData
* @property {string} name The name of this provider. * @property {string} name The name of this provider.
*/ */
/** /**
* Sets the provider of this embed. * Sets the provider of this embed.
* @param {string|EmbedProviderData|null} options The options to provide for the provider. * @param {string|EmbedProviderData|null} options The options to provide for the provider.
* Provide `null` to remove the provider data. * Provide `null` to remove the provider data.
* @returns {MessageEmbed} * @returns {MessageEmbed}
*/ */
setProvider(options) { setProvider(options) {
if (options === null) { if (options === null) {
this.provider = {}; this.provider = {};
return this; return this;
} }
const { name, url } = options; const { name, url } = options;
this.provider = { this.provider = {
name: Util.verifyString(name, RangeError, 'EMBED_PROVIDER_NAME'), name: Util.verifyString(name, RangeError, 'EMBED_PROVIDER_NAME'),
url, url,
}; };
return this; return this;
} }
/** /**
* Sets the color of this embed. * Sets the color of this embed.
* @param {ColorResolvable} color The color of the embed * @param {ColorResolvable} color The color of the embed
* @returns {MessageEmbed} * @returns {MessageEmbed}
*/ */
setColor(color) { setColor(color) {
this.color = Util.resolveColor(color); this.color = Util.resolveColor(color);
return this; return this;
} }
/** /**
* Sets the description of this embed. * Sets the description of this embed.
* @param {string} description The description (Limit 350 characters) * @param {string} description The description (Limit 350 characters)
* @returns {MessageEmbed} * @returns {MessageEmbed}
*/ */
setDescription(description) { setDescription(description) {
this.description = Util.verifyString( this.description = Util.verifyString(description, RangeError, 'EMBED_DESCRIPTION');
description, return this;
RangeError, }
'EMBED_DESCRIPTION',
); /**
return this; * Sets the image of this embed.
} * @param {string} url The URL of the image
* @returns {MessageEmbed}
/** */
* Sets the image of this embed. setImage(url) {
* @param {string} url The URL of the image if (this.thumbnail && this.thumbnail.url) {
* @returns {MessageEmbed} console.warn('You can only set image or thumbnail per embed.');
*/ this.thumbnail.url = null;
setImage(url) { }
if (this.thumbnail && this.thumbnail.url) { this.imageType = 'image';
console.warn('You can only set image or thumbnail per embed.'); this.image = { url };
this.thumbnail.url = null; return this;
} }
this.imageType = 'image';
this.image = { url }; /**
return this; * Sets the thumbnail of this embed.
} * @param {string} url The URL of the image
* @returns {MessageEmbed}
/** */
* Sets the thumbnail of this embed. setThumbnail(url) {
* @param {string} url The URL of the image if (this.image && this.image.url) {
* @returns {MessageEmbed} console.warn('You can only set image or thumbnail per embed.');
*/ this.image.url = null;
setThumbnail(url) { }
if (this.image && this.image.url) { this.imageType = 'thumbnail';
console.warn('You can only set image or thumbnail per embed.'); this.thumbnail = { url };
this.image.url = null; return this;
} }
this.imageType = 'thumbnail';
this.thumbnail = { url }; /**
return this; * Sets the video of this embed.
} * @param {string} url The URL of the video
* @returns {MessageEmbed}
/** */
* Sets the video of this embed. setVideo(url) {
* @param {string} url The URL of the video this.video = { url };
* @returns {MessageEmbed} return this;
*/ }
setVideo(url) {
this.video = { url }; /**
return this; * Sets the title of this embed.
} * @param {string} title The title
* @returns {MessageEmbed}
/** */
* Sets the title of this embed. setTitle(title) {
* @param {string} title The title this.title = Util.verifyString(title, RangeError, 'EMBED_TITLE');
* @returns {MessageEmbed} return this;
*/ }
setTitle(title) {
this.title = Util.verifyString(title, RangeError, 'EMBED_TITLE'); /**
return this; * Sets the URL of this embed.
} * @param {string} url The URL
* @returns {MessageEmbed}
/** */
* Sets the URL of this embed. setURL(url) {
* @param {string} url The URL this.url = url;
* @returns {MessageEmbed} return this;
*/ }
setURL(url) {
this.url = url; /**
return this; * Return Message Content + Embed (if hidden, pls check content length because it has 1000+ length)
} * @returns {string} Message Content
*/
/** async toMessage() {
* Return Message Content + Embed (if hidden, pls check content length because it has 1000+ length) const arrayQuery = [`image_type=${this.imageType}`];
* @returns {string} Message Content if (this.title) {
*/ arrayQuery.push(`title=${encodeURIComponent(this.title)}`);
async toMessage() { }
const arrayQuery = [`image_type=${this.imageType}`]; if (this.description) {
if (this.title) { arrayQuery.push(`description=${encodeURIComponent(this.description)}`);
arrayQuery.push(`title=${encodeURIComponent(this.title)}`); }
} if (this.url) {
if (this.description) { arrayQuery.push(`url=${encodeURIComponent(this.url)}`);
arrayQuery.push(`description=${encodeURIComponent(this.description)}`); }
} if (this.color) {
if (this.url) { arrayQuery.push(`color=${encodeURIComponent('#' + this.color.toString(16))}`);
arrayQuery.push(`url=${encodeURIComponent(this.url)}`); }
} if (this.image?.url) {
if (this.color) { arrayQuery.push(`image=${encodeURIComponent(this.image.url)}`);
arrayQuery.push( }
`color=${encodeURIComponent('#' + this.color.toString(16))}`, if (this.video?.url) {
); arrayQuery.push(`video=${encodeURIComponent(this.video.url)}`);
} }
if (this.image?.url) { if (this.author) {
arrayQuery.push(`image=${encodeURIComponent(this.image.url)}`); if (this.author.name) {
} arrayQuery.push(`author_name=${encodeURIComponent(this.author.name)}`);
if (this.video?.url) { }
arrayQuery.push(`video=${encodeURIComponent(this.video.url)}`); if (this.author.url) {
} arrayQuery.push(`author_url=${encodeURIComponent(this.author.url)}`);
if (this.author) { }
if (this.author.name) }
arrayQuery.push(`author_name=${encodeURIComponent(this.author.name)}`); if (this.provider) {
if (this.author.url) if (this.provider.name) {
arrayQuery.push(`author_url=${encodeURIComponent(this.author.url)}`); arrayQuery.push(`provider_name=${encodeURIComponent(this.provider.name)}`);
} }
if (this.provider) { if (this.provider.url) {
if (this.provider.name) arrayQuery.push(`provider_url=${encodeURIComponent(this.provider.url)}`);
arrayQuery.push( }
`provider_name=${encodeURIComponent(this.provider.name)}`, }
); if (this.thumbnail?.url) {
if (this.provider.url) arrayQuery.push(`image=${encodeURIComponent(this.thumbnail.url)}`);
arrayQuery.push( }
`provider_url=${encodeURIComponent(this.provider.url)}`, const fullURL = `${baseURL}${arrayQuery.join('&')}`;
); if (this.shorten) {
} const url = await getShorten(fullURL);
if (this.thumbnail?.url) { if (!url) console.log('Cannot shorten URL in WebEmbed');
arrayQuery.push(`image=${encodeURIComponent(this.thumbnail.url)}`); return this.hidden ? `${hiddenCharter} ${url || fullURL}` : url || fullURL;
} } else {
const fullURL = `${baseURL}${arrayQuery.join('&')}`; return this.hidden ? `${hiddenCharter} ${fullURL}` : fullURL;
if (this.shorten) { }
const url = await getShorten(fullURL); }
if (!url) console.log('Cannot shorten URL in WebEmbed'); }
return this.hidden
? `${hiddenCharter} ${url || fullURL}` // Credit: https://www.npmjs.com/package/node-url-shortener + google :))
: url || fullURL; const getShorten = async url => {
} else { const APIurl = [
return this.hidden ? `${hiddenCharter} ${fullURL}` : fullURL; // 'https://is.gd/create.php?format=simple&url=', :(
} 'https://tinyurl.com/api-create.php?url=',
} 'https://sagiri-fansub.tk/api/v1/short?url=', // my api, pls don't ddos :(
} 'https://lazuee.ga/api/v1/shorten?url=',
// 'https://cdpt.in/shorten?url=', Redirects 5s :(
// Credit: https://www.npmjs.com/package/node-url-shortener + google :)) ];
const getShorten = async (url) => { try {
const APIurl = [ const res = await axios.get(`${APIurl[Math.floor(Math.random() * APIurl.length)]}${encodeURIComponent(url)}`);
// 'https://is.gd/create.php?format=simple&url=', :( if (typeof res.data == 'string') return res.data;
'https://tinyurl.com/api-create.php?url=', else if (typeof res.data == 'object') return res.data.shorten;
'https://sagiri-fansub.tk/api/v1/short?url=', // my api, pls don't ddos :( else throw null;
'https://lazuee.ga/api/v1/shorten?url=' } catch {
// 'https://cdpt.in/shorten?url=', Redirects 5s :( return void 0;
]; }
try { };
const res = await axios.get(
`${ module.exports = WebEmbed;
APIurl[Math.floor(Math.random() * APIurl.length)] module.exports.hiddenEmbed = hiddenCharter;
}${encodeURIComponent(url)}`,
);
if (typeof res.data == 'string') return res.data;
else if (typeof res.data == 'object') return res.data.shorten;
else throw null;
} catch {
return void 0;
}
}
module.exports = WebEmbed;
module.exports.hiddenEmbed = hiddenCharter;

View File

@@ -316,15 +316,12 @@ class Webhook {
if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE'); if (!this.token) throw new Error('WEBHOOK_TOKEN_UNAVAILABLE');
let messagePayload; let messagePayload;
if (options instanceof MessagePayload) { if (options instanceof MessagePayload) {
messagePayload = await options.resolveData(); messagePayload = await options.resolveData();
} else { } else {
messagePayload = await MessagePayload.create( messagePayload = await MessagePayload.create(message instanceof Message ? message : this, options).resolveData();
message instanceof Message ? message : this, }
options, const { data, files } = await messagePayload.resolveFiles();
).resolveData();
}
const { data, files } = await messagePayload.resolveFiles();
const d = await this.client.api const d = await this.client.api
.webhooks(this.id, this.token) .webhooks(this.id, this.token)

View File

@@ -1,136 +1,136 @@
'use strict'; 'use strict';
const { ClientApplicationAssetTypes, Endpoints } = require('../../util/Constants'); const { ClientApplicationAssetTypes, Endpoints } = require('../../util/Constants');
const SnowflakeUtil = require('../../util/SnowflakeUtil'); const SnowflakeUtil = require('../../util/SnowflakeUtil');
const Base = require('../Base'); const Base = require('../Base');
const AssetTypes = Object.keys(ClientApplicationAssetTypes); const AssetTypes = Object.keys(ClientApplicationAssetTypes);
/** /**
* Represents an OAuth2 Application. * Represents an OAuth2 Application.
* @abstract * @abstract
*/ */
class Application extends Base { class Application extends Base {
constructor(client, data) { constructor(client, data) {
super(client); super(client);
if (data) { if (data) {
this._patch(data); this._patch(data);
} }
} }
_patch(data) { _patch(data) {
/** /**
* The application's id * The application's id
* @type {Snowflake} * @type {Snowflake}
*/ */
this.id = data.id; this.id = data.id;
if ('name' in data) { if ('name' in data) {
/** /**
* The name of the application * The name of the application
* @type {?string} * @type {?string}
*/ */
this.name = data.name; this.name = data.name;
} else { } else {
this.name ??= null; this.name ??= null;
} }
if ('description' in data) { if ('description' in data) {
/** /**
* The application's description * The application's description
* @type {?string} * @type {?string}
*/ */
this.description = data.description; this.description = data.description;
} else { } else {
this.description ??= null; this.description ??= null;
} }
if ('icon' in data) { if ('icon' in data) {
/** /**
* The application's icon hash * The application's icon hash
* @type {?string} * @type {?string}
*/ */
this.icon = data.icon; this.icon = data.icon;
} else { } else {
this.icon ??= null; this.icon ??= null;
} }
} }
/** /**
* The timestamp the application was created at * The timestamp the application was created at
* @type {number} * @type {number}
* @readonly * @readonly
*/ */
get createdTimestamp() { get createdTimestamp() {
return SnowflakeUtil.timestampFrom(this.id); return SnowflakeUtil.timestampFrom(this.id);
} }
/** /**
* The time the application was created at * The time the application was created at
* @type {Date} * @type {Date}
* @readonly * @readonly
*/ */
get createdAt() { get createdAt() {
return new Date(this.createdTimestamp); return new Date(this.createdTimestamp);
} }
/** /**
* A link to the application's icon. * A link to the application's icon.
* @param {StaticImageURLOptions} [options={}] Options for the Image URL * @param {StaticImageURLOptions} [options={}] Options for the Image URL
* @returns {?string} * @returns {?string}
*/ */
iconURL({ format, size } = {}) { iconURL({ format, size } = {}) {
if (!this.icon) return null; if (!this.icon) return null;
return this.client.rest.cdn.AppIcon(this.id, this.icon, { format, size }); return this.client.rest.cdn.AppIcon(this.id, this.icon, { format, size });
} }
/** /**
* A link to this application's cover image. * A link to this application's cover image.
* @param {StaticImageURLOptions} [options={}] Options for the Image URL * @param {StaticImageURLOptions} [options={}] Options for the Image URL
* @returns {?string} * @returns {?string}
*/ */
coverURL({ format, size } = {}) { coverURL({ format, size } = {}) {
if (!this.cover) return null; if (!this.cover) return null;
return Endpoints.CDN(this.client.options.http.cdn).AppIcon(this.id, this.cover, { format, size }); return Endpoints.CDN(this.client.options.http.cdn).AppIcon(this.id, this.cover, { format, size });
} }
/** /**
* Asset data. * Asset data.
* @typedef {Object} ApplicationAsset * @typedef {Object} ApplicationAsset
* @property {Snowflake} id The asset's id * @property {Snowflake} id The asset's id
* @property {string} name The asset's name * @property {string} name The asset's name
* @property {string} type The asset's type * @property {string} type The asset's type
*/ */
/** /**
* Gets the application's rich presence assets. * Gets the application's rich presence assets.
* @returns {Promise<Array<ApplicationAsset>>} * @returns {Promise<Array<ApplicationAsset>>}
*/ */
async fetchAssets() { async fetchAssets() {
const assets = await this.client.api.oauth2.applications(this.id).assets.get(); const assets = await this.client.api.oauth2.applications(this.id).assets.get();
return assets.map(a => ({ return assets.map(a => ({
id: a.id, id: a.id,
name: a.name, name: a.name,
type: AssetTypes[a.type - 1], type: AssetTypes[a.type - 1],
})); }));
} }
/** /**
* When concatenated with a string, this automatically returns the application's name instead of the * When concatenated with a string, this automatically returns the application's name instead of the
* Application object. * Application object.
* @returns {?string} * @returns {?string}
* @example * @example
* // Logs: Application name: My App * // Logs: Application name: My App
* console.log(`Application name: ${application}`); * console.log(`Application name: ${application}`);
*/ */
toString() { toString() {
return this.name; return this.name;
} }
toJSON() { toJSON() {
return super.toJSON({ createdTimestamp: true }); return super.toJSON({ createdTimestamp: true });
} }
} }
module.exports = Application; module.exports = Application;

View File

@@ -1,360 +1,360 @@
'use strict'; 'use strict';
/* eslint-disable import/order */ /* eslint-disable import/order */
const MessageCollector = require('../MessageCollector'); const MessageCollector = require('../MessageCollector');
const MessagePayload = require('../MessagePayload'); const MessagePayload = require('../MessagePayload');
const SnowflakeUtil = require('../../util/SnowflakeUtil'); const SnowflakeUtil = require('../../util/SnowflakeUtil');
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
const { InteractionTypes } = require('../../util/Constants'); const { InteractionTypes } = require('../../util/Constants');
const { TypeError, Error } = require('../../errors'); const { TypeError, Error } = require('../../errors');
const InteractionCollector = require('../InteractionCollector'); const InteractionCollector = require('../InteractionCollector');
/** /**
* Interface for classes that have text-channel-like features. * Interface for classes that have text-channel-like features.
* @interface * @interface
*/ */
class TextBasedChannel { class TextBasedChannel {
constructor() { constructor() {
/** /**
* A manager of the messages sent to this channel * A manager of the messages sent to this channel
* @type {MessageManager} * @type {MessageManager}
*/ */
this.messages = new MessageManager(this); this.messages = new MessageManager(this);
/** /**
* The channel's last message id, if one was sent * The channel's last message id, if one was sent
* @type {?Snowflake} * @type {?Snowflake}
*/ */
this.lastMessageId = null; this.lastMessageId = null;
/** /**
* The timestamp when the last pinned message was pinned, if there was one * The timestamp when the last pinned message was pinned, if there was one
* @type {?number} * @type {?number}
*/ */
this.lastPinTimestamp = null; this.lastPinTimestamp = null;
} }
/** /**
* The Message object of the last message in the channel, if one was sent * The Message object of the last message in the channel, if one was sent
* @type {?Message} * @type {?Message}
* @readonly * @readonly
*/ */
get lastMessage() { get lastMessage() {
return this.messages.resolve(this.lastMessageId); return this.messages.resolve(this.lastMessageId);
} }
/** /**
* The date when the last pinned message was pinned, if there was one * The date when the last pinned message was pinned, if there was one
* @type {?Date} * @type {?Date}
* @readonly * @readonly
*/ */
get lastPinAt() { get lastPinAt() {
return this.lastPinTimestamp ? new Date(this.lastPinTimestamp) : null; return this.lastPinTimestamp ? new Date(this.lastPinTimestamp) : null;
} }
/** /**
* Base options provided when sending. * Base options provided when sending.
* @typedef {Object} BaseMessageOptions * @typedef {Object} BaseMessageOptions
* @property {boolean} [tts=false] Whether or not the message should be spoken aloud * @property {boolean} [tts=false] Whether or not the message should be spoken aloud
* @property {string} [nonce=''] The nonce for the message * @property {string} [nonce=''] The nonce for the message
* @property {string} [content=''] The content for the message * @property {string} [content=''] The content for the message
* @property {WebEmbed[]|MessageEmbed[]|APIEmbed[]} [embeds] The embeds for the message * @property {WebEmbed[]|MessageEmbed[]|APIEmbed[]} [embeds] The embeds for the message
* (see [here](https://discord.com/developers/docs/resources/channel#embed-object) for more details) * (see [here](https://discord.com/developers/docs/resources/channel#embed-object) for more details)
* @property {MessageMentionOptions} [allowedMentions] Which mentions should be parsed from the message content * @property {MessageMentionOptions} [allowedMentions] Which mentions should be parsed from the message content
* (see [here](https://discord.com/developers/docs/resources/channel#allowed-mentions-object) for more details) * (see [here](https://discord.com/developers/docs/resources/channel#allowed-mentions-object) for more details)
* @property {FileOptions[]|BufferResolvable[]|MessageAttachment[]} [files] Files to send with the message * @property {FileOptions[]|BufferResolvable[]|MessageAttachment[]} [files] Files to send with the message
* @property {MessageActionRow[]|MessageActionRowOptions[]} [components] * @property {MessageActionRow[]|MessageActionRowOptions[]} [components]
* Action rows containing interactive components for the message (buttons, select menus) * Action rows containing interactive components for the message (buttons, select menus)
* @property {MessageAttachment[]} [attachments] Attachments to send in the message * @property {MessageAttachment[]} [attachments] Attachments to send in the message
*/ */
/** /**
* Options provided when sending or editing a message. * Options provided when sending or editing a message.
* @typedef {BaseMessageOptions} MessageOptions * @typedef {BaseMessageOptions} MessageOptions
* @property {ReplyOptions} [reply] The options for replying to a message * @property {ReplyOptions} [reply] The options for replying to a message
* @property {StickerResolvable[]} [stickers=[]] Stickers to send in the message * @property {StickerResolvable[]} [stickers=[]] Stickers to send in the message
*/ */
/** /**
* Options provided to control parsing of mentions by Discord * Options provided to control parsing of mentions by Discord
* @typedef {Object} MessageMentionOptions * @typedef {Object} MessageMentionOptions
* @property {MessageMentionTypes[]} [parse] Types of mentions to be parsed * @property {MessageMentionTypes[]} [parse] Types of mentions to be parsed
* @property {Snowflake[]} [users] Snowflakes of Users to be parsed as mentions * @property {Snowflake[]} [users] Snowflakes of Users to be parsed as mentions
* @property {Snowflake[]} [roles] Snowflakes of Roles to be parsed as mentions * @property {Snowflake[]} [roles] Snowflakes of Roles to be parsed as mentions
* @property {boolean} [repliedUser=true] Whether the author of the Message being replied to should be pinged * @property {boolean} [repliedUser=true] Whether the author of the Message being replied to should be pinged
*/ */
/** /**
* Types of mentions to enable in MessageMentionOptions. * Types of mentions to enable in MessageMentionOptions.
* - `roles` * - `roles`
* - `users` * - `users`
* - `everyone` * - `everyone`
* @typedef {string} MessageMentionTypes * @typedef {string} MessageMentionTypes
*/ */
/** /**
* @typedef {Object} FileOptions * @typedef {Object} FileOptions
* @property {BufferResolvable} attachment File to attach * @property {BufferResolvable} attachment File to attach
* @property {string} [name='file.jpg'] Filename of the attachment * @property {string} [name='file.jpg'] Filename of the attachment
* @property {string} description The description of the file * @property {string} description The description of the file
*/ */
/** /**
* Options for sending a message with a reply. * Options for sending a message with a reply.
* @typedef {Object} ReplyOptions * @typedef {Object} ReplyOptions
* @property {MessageResolvable} messageReference The message to reply to (must be in the same channel and not system) * @property {MessageResolvable} messageReference The message to reply to (must be in the same channel and not system)
* @property {boolean} [failIfNotExists=true] Whether to error if the referenced message * @property {boolean} [failIfNotExists=true] Whether to error if the referenced message
* does not exist (creates a standard message in this case when false) * does not exist (creates a standard message in this case when false)
*/ */
/** /**
* Sends a message to this channel. * Sends a message to this channel.
* @param {string|MessagePayload|MessageOptions} options The options to provide * @param {string|MessagePayload|MessageOptions} options The options to provide
* @returns {Promise<Message>} * @returns {Promise<Message>}
* @example * @example
* // Send a basic message * // Send a basic message
* channel.send('hello!') * channel.send('hello!')
* .then(message => console.log(`Sent message: ${message.content}`)) * .then(message => console.log(`Sent message: ${message.content}`))
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Send a remote file * // Send a remote file
* channel.send({ * channel.send({
* files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048'] * files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048']
* }) * })
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Send a local file * // Send a local file
* channel.send({ * channel.send({
* files: [{ * files: [{
* attachment: 'entire/path/to/file.jpg', * attachment: 'entire/path/to/file.jpg',
* name: 'file.jpg' * name: 'file.jpg'
* description: 'A description of the file' * description: 'A description of the file'
* }] * }]
* }) * })
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Send an embed with a local image inside * // Send an embed with a local image inside
* channel.send({ * channel.send({
* content: 'This is an embed', * content: 'This is an embed',
* embeds: [ * embeds: [
* { * {
* thumbnail: { * thumbnail: {
* url: 'attachment://file.jpg' * url: 'attachment://file.jpg'
* } * }
* } * }
* ], * ],
* files: [{ * files: [{
* attachment: 'entire/path/to/file.jpg', * attachment: 'entire/path/to/file.jpg',
* name: 'file.jpg' * name: 'file.jpg'
* description: 'A description of the file' * description: 'A description of the file'
* }] * }]
* }) * })
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async send(options) { async send(options) {
const User = require('../User'); const User = require('../User');
const { GuildMember } = require('../GuildMember'); const { GuildMember } = require('../GuildMember');
if (this instanceof User || this instanceof GuildMember) { if (this instanceof User || this instanceof GuildMember) {
const dm = await this.createDM(); const dm = await this.createDM();
return dm.send(options); return dm.send(options);
} }
let messagePayload; let messagePayload;
if (options instanceof MessagePayload) { if (options instanceof MessagePayload) {
messagePayload = await options.resolveData(); messagePayload = await options.resolveData();
} else { } else {
messagePayload = await MessagePayload.create(this, options).resolveData(); messagePayload = await MessagePayload.create(this, options).resolveData();
} }
const { data, files } = await messagePayload.resolveFiles(); const { data, files } = await messagePayload.resolveFiles();
const d = await this.client.api.channels[this.id].messages.post({ data, files }); const d = await this.client.api.channels[this.id].messages.post({ data, files });
return this.messages.cache.get(d.id) ?? this.messages._add(d); return this.messages.cache.get(d.id) ?? this.messages._add(d);
} }
/** /**
* Sends a typing indicator in the channel. * Sends a typing indicator in the channel.
* @returns {Promise<void>} Resolves upon the typing status being sent * @returns {Promise<void>} Resolves upon the typing status being sent
* @example * @example
* // Start typing in a channel * // Start typing in a channel
* channel.sendTyping(); * channel.sendTyping();
*/ */
async sendTyping() { async sendTyping() {
await this.client.api.channels(this.id).typing.post(); await this.client.api.channels(this.id).typing.post();
} }
/** /**
* Creates a Message Collector. * Creates a Message Collector.
* @param {MessageCollectorOptions} [options={}] The options to pass to the collector * @param {MessageCollectorOptions} [options={}] The options to pass to the collector
* @returns {MessageCollector} * @returns {MessageCollector}
* @example * @example
* // Create a message collector * // Create a message collector
* const filter = m => m.content.includes('discord'); * const filter = m => m.content.includes('discord');
* const collector = channel.createMessageCollector({ filter, time: 15_000 }); * const collector = channel.createMessageCollector({ filter, time: 15_000 });
* collector.on('collect', m => console.log(`Collected ${m.content}`)); * collector.on('collect', m => console.log(`Collected ${m.content}`));
* collector.on('end', collected => console.log(`Collected ${collected.size} items`)); * collector.on('end', collected => console.log(`Collected ${collected.size} items`));
*/ */
createMessageCollector(options = {}) { createMessageCollector(options = {}) {
return new MessageCollector(this, options); return new MessageCollector(this, options);
} }
/** /**
* An object containing the same properties as CollectorOptions, but a few more: * An object containing the same properties as CollectorOptions, but a few more:
* @typedef {MessageCollectorOptions} AwaitMessagesOptions * @typedef {MessageCollectorOptions} AwaitMessagesOptions
* @property {string[]} [errors] Stop/end reasons that cause the promise to reject * @property {string[]} [errors] Stop/end reasons that cause the promise to reject
*/ */
/** /**
* Similar to createMessageCollector but in promise form. * Similar to createMessageCollector but in promise form.
* Resolves with a collection of messages that pass the specified filter. * Resolves with a collection of messages that pass the specified filter.
* @param {AwaitMessagesOptions} [options={}] Optional options to pass to the internal collector * @param {AwaitMessagesOptions} [options={}] Optional options to pass to the internal collector
* @returns {Promise<Collection<Snowflake, Message>>} * @returns {Promise<Collection<Snowflake, Message>>}
* @example * @example
* // Await !vote messages * // Await !vote messages
* const filter = m => m.content.startsWith('!vote'); * const filter = m => m.content.startsWith('!vote');
* // Errors: ['time'] treats ending because of the time limit as an error * // Errors: ['time'] treats ending because of the time limit as an error
* channel.awaitMessages({ filter, max: 4, time: 60_000, errors: ['time'] }) * channel.awaitMessages({ filter, max: 4, time: 60_000, errors: ['time'] })
* .then(collected => console.log(collected.size)) * .then(collected => console.log(collected.size))
* .catch(collected => console.log(`After a minute, only ${collected.size} out of 4 voted.`)); * .catch(collected => console.log(`After a minute, only ${collected.size} out of 4 voted.`));
*/ */
awaitMessages(options = {}) { awaitMessages(options = {}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const collector = this.createMessageCollector(options); const collector = this.createMessageCollector(options);
collector.once('end', (collection, reason) => { collector.once('end', (collection, reason) => {
if (options.errors?.includes(reason)) { if (options.errors?.includes(reason)) {
reject(collection); reject(collection);
} else { } else {
resolve(collection); resolve(collection);
} }
}); });
}); });
} }
/** /**
* Creates a button interaction collector. * Creates a button interaction collector.
* @param {MessageComponentCollectorOptions} [options={}] Options to send to the collector * @param {MessageComponentCollectorOptions} [options={}] Options to send to the collector
* @returns {InteractionCollector} * @returns {InteractionCollector}
* @example * @example
* // Create a button interaction collector * // Create a button interaction collector
* const filter = (interaction) => interaction.customId === 'button' && interaction.user.id === 'someId'; * const filter = (interaction) => interaction.customId === 'button' && interaction.user.id === 'someId';
* const collector = channel.createMessageComponentCollector({ filter, time: 15_000 }); * const collector = channel.createMessageComponentCollector({ filter, time: 15_000 });
* collector.on('collect', i => console.log(`Collected ${i.customId}`)); * collector.on('collect', i => console.log(`Collected ${i.customId}`));
* collector.on('end', collected => console.log(`Collected ${collected.size} items`)); * collector.on('end', collected => console.log(`Collected ${collected.size} items`));
*/ */
createMessageComponentCollector(options = {}) { createMessageComponentCollector(options = {}) {
return new InteractionCollector(this.client, { return new InteractionCollector(this.client, {
...options, ...options,
interactionType: InteractionTypes.MESSAGE_COMPONENT, interactionType: InteractionTypes.MESSAGE_COMPONENT,
channel: this, channel: this,
}); });
} }
/** /**
* Collects a single component interaction that passes the filter. * Collects a single component interaction that passes the filter.
* The Promise will reject if the time expires. * The Promise will reject if the time expires.
* @param {AwaitMessageComponentOptions} [options={}] Options to pass to the internal collector * @param {AwaitMessageComponentOptions} [options={}] Options to pass to the internal collector
* @returns {Promise<MessageComponentInteraction>} * @returns {Promise<MessageComponentInteraction>}
* @example * @example
* // Collect a message component interaction * // Collect a message component interaction
* const filter = (interaction) => interaction.customId === 'button' && interaction.user.id === 'someId'; * const filter = (interaction) => interaction.customId === 'button' && interaction.user.id === 'someId';
* channel.awaitMessageComponent({ filter, time: 15_000 }) * channel.awaitMessageComponent({ filter, time: 15_000 })
* .then(interaction => console.log(`${interaction.customId} was clicked!`)) * .then(interaction => console.log(`${interaction.customId} was clicked!`))
* .catch(console.error); * .catch(console.error);
*/ */
awaitMessageComponent(options = {}) { awaitMessageComponent(options = {}) {
const _options = { ...options, max: 1 }; const _options = { ...options, max: 1 };
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const collector = this.createMessageComponentCollector(_options); const collector = this.createMessageComponentCollector(_options);
collector.once('end', (interactions, reason) => { collector.once('end', (interactions, reason) => {
const interaction = interactions.first(); const interaction = interactions.first();
if (interaction) resolve(interaction); if (interaction) resolve(interaction);
else reject(new Error('INTERACTION_COLLECTOR_ERROR', reason)); else reject(new Error('INTERACTION_COLLECTOR_ERROR', reason));
}); });
}); });
} }
/** /**
* Bulk deletes given messages that are newer than two weeks. * Bulk deletes given messages that are newer than two weeks.
* @param {Collection<Snowflake, Message>|MessageResolvable[]|number} messages * @param {Collection<Snowflake, Message>|MessageResolvable[]|number} messages
* Messages or number of messages to delete * Messages or number of messages to delete
* @param {boolean} [filterOld=false] Filter messages to remove those which are older than two weeks automatically * @param {boolean} [filterOld=false] Filter messages to remove those which are older than two weeks automatically
* @returns {Promise<Collection<Snowflake, Message>>} Returns the deleted messages * @returns {Promise<Collection<Snowflake, Message>>} Returns the deleted messages
* @example * @example
* // Bulk delete messages * // Bulk delete messages
* channel.bulkDelete(5) * channel.bulkDelete(5)
* .then(messages => console.log(`Bulk deleted ${messages.size} messages`)) * .then(messages => console.log(`Bulk deleted ${messages.size} messages`))
* .catch(console.error); * .catch(console.error);
*/ */
async bulkDelete(messages, filterOld = false) { async bulkDelete(messages, filterOld = false) {
if (Array.isArray(messages) || messages instanceof Collection) { if (Array.isArray(messages) || messages instanceof Collection) {
let messageIds = messages instanceof Collection ? [...messages.keys()] : messages.map(m => m.id ?? m); let messageIds = messages instanceof Collection ? [...messages.keys()] : messages.map(m => m.id ?? m);
if (filterOld) { if (filterOld) {
messageIds = messageIds.filter(id => Date.now() - SnowflakeUtil.timestampFrom(id) < 1_209_600_000); messageIds = messageIds.filter(id => Date.now() - SnowflakeUtil.timestampFrom(id) < 1_209_600_000);
} }
if (messageIds.length === 0) return new Collection(); if (messageIds.length === 0) return new Collection();
if (messageIds.length === 1) { if (messageIds.length === 1) {
await this.client.api.channels(this.id).messages(messageIds[0]).delete(); await this.client.api.channels(this.id).messages(messageIds[0]).delete();
const message = this.client.actions.MessageDelete.getMessage( const message = this.client.actions.MessageDelete.getMessage(
{ {
message_id: messageIds[0], message_id: messageIds[0],
}, },
this, this,
); );
return message ? new Collection([[message.id, message]]) : new Collection(); return message ? new Collection([[message.id, message]]) : new Collection();
} }
await this.client.api.channels[this.id].messages['bulk-delete'].post({ data: { messages: messageIds } }); await this.client.api.channels[this.id].messages['bulk-delete'].post({ data: { messages: messageIds } });
return messageIds.reduce( return messageIds.reduce(
(col, id) => (col, id) =>
col.set( col.set(
id, id,
this.client.actions.MessageDeleteBulk.getMessage( this.client.actions.MessageDeleteBulk.getMessage(
{ {
message_id: id, message_id: id,
}, },
this, this,
), ),
), ),
new Collection(), new Collection(),
); );
} }
if (!isNaN(messages)) { if (!isNaN(messages)) {
const msgs = await this.messages.fetch({ limit: messages }); const msgs = await this.messages.fetch({ limit: messages });
return this.bulkDelete(msgs, filterOld); return this.bulkDelete(msgs, filterOld);
} }
throw new TypeError('MESSAGE_BULK_DELETE_TYPE'); throw new TypeError('MESSAGE_BULK_DELETE_TYPE');
} }
static applyToClass(structure, full = false, ignore = []) { static applyToClass(structure, full = false, ignore = []) {
const props = ['send']; const props = ['send'];
if (full) { if (full) {
props.push( props.push(
'lastMessage', 'lastMessage',
'lastPinAt', 'lastPinAt',
'bulkDelete', 'bulkDelete',
'sendTyping', 'sendTyping',
'createMessageCollector', 'createMessageCollector',
'awaitMessages', 'awaitMessages',
'createMessageComponentCollector', 'createMessageComponentCollector',
'awaitMessageComponent', 'awaitMessageComponent',
); );
} }
for (const prop of props) { for (const prop of props) {
if (ignore.includes(prop)) continue; if (ignore.includes(prop)) continue;
Object.defineProperty( Object.defineProperty(
structure.prototype, structure.prototype,
prop, prop,
Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop), Object.getOwnPropertyDescriptor(TextBasedChannel.prototype, prop),
); );
} }
} }
} }
module.exports = TextBasedChannel; module.exports = TextBasedChannel;
// Fixes Circular // Fixes Circular
const MessageManager = require('../../managers/MessageManager'); const MessageManager = require('../../managers/MessageManager');

View File

@@ -4,104 +4,104 @@ const process = require('node:process');
const Package = (exports.Package = require('../../package.json')); const Package = (exports.Package = require('../../package.json'));
const { Error, RangeError, TypeError } = require('../errors'); const { Error, RangeError, TypeError } = require('../errors');
const listUserAgent = [ const listUserAgent = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.2 Safari/605.1.15", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.2 Safari/605.1.15',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36", 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362',
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:69.0) Gecko/20100101 Firefox/69.0", 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:69.0) Gecko/20100101 Firefox/69.0',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; rv:68.0) Gecko/20100101 Firefox/68.0", 'Mozilla/5.0 (Windows NT 10.0; rv:68.0) Gecko/20100101 Firefox/68.0',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Safari/605.1.15", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Safari/605.1.15',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36 OPR/63.0.3368.107", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36 OPR/63.0.3368.107',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:69.0) Gecko/20100101 Firefox/69.0", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:69.0) Gecko/20100101 Firefox/69.0',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.2 Safari/605.1.15", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.2 Safari/605.1.15',
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36", 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763',
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0", 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 YaBrowser/19.9.3.314 Yowser/2.5 Safari/537.36", 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 YaBrowser/19.9.3.314 Yowser/2.5 Safari/537.36',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko", 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko',
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:70.0) Gecko/20100101 Firefox/70.0", 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:70.0) Gecko/20100101 Firefox/70.0',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.1 Safari/605.1.15", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.1 Safari/605.1.15',
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36", 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15',
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36", 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
"Mozilla/5.0 (X11; Linux x86_64; rv:69.0) Gecko/20100101 Firefox/69.0", 'Mozilla/5.0 (X11; Linux x86_64; rv:69.0) Gecko/20100101 Firefox/69.0',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Safari/605.1.15", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Safari/605.1.15',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36',
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0", 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0',
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36", 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
"Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko", 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.2 Safari/605.1.15", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.2 Safari/605.1.15',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
"Mozilla/5.0 (Windows NT 6.1; rv:69.0) Gecko/20100101 Firefox/69.0", 'Mozilla/5.0 (Windows NT 6.1; rv:69.0) Gecko/20100101 Firefox/69.0',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:69.0) Gecko/20100101 Firefox/69.0", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:69.0) Gecko/20100101 Firefox/69.0',
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
"Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0", 'Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0',
"Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", 'Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36", 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0',
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36", 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:70.0) Gecko/20100101 Firefox/70.0", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:70.0) Gecko/20100101 Firefox/70.0',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.75 Safari/537.36", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.75 Safari/537.36',
"Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0", 'Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0',
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36", 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/77.0.3865.90 Chrome/77.0.3865.90 Safari/537.36", 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/77.0.3865.90 Chrome/77.0.3865.90 Safari/537.36',
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36", 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
"Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0", 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0',
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36", 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36',
"Mozilla/5.0 (X11; Linux x86_64; rv:70.0) Gecko/20100101 Firefox/70.0", 'Mozilla/5.0 (X11; Linux x86_64; rv:70.0) Gecko/20100101 Firefox/70.0',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
"Mozilla/5.0 (Windows NT 5.1; rv:52.0) Gecko/20100101 Firefox/52.0", 'Mozilla/5.0 (Windows NT 5.1; rv:52.0) Gecko/20100101 Firefox/52.0',
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Safari/605.1.15", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Safari/605.1.15',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Safari/605.1.15", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Safari/605.1.15',
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36 OPR/63.0.3368.107", 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36 OPR/63.0.3368.107',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:69.0) Gecko/20100101 Firefox/69.0", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:69.0) Gecko/20100101 Firefox/69.0',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/71.0',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0 Waterfox/56.2.14", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0 Waterfox/56.2.14',
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36", 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3835.0 Safari/537.36", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3835.0 Safari/537.36',
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36", 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:53.0) Gecko/20100101 Firefox/53.0", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:53.0) Gecko/20100101 Firefox/53.0',
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393", 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393',
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)", 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)',
"Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko", 'Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko',
"Mozilla/5.0 (iPad; CPU OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H321 Safari/600.1.4", 'Mozilla/5.0 (iPad; CPU OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H321 Safari/600.1.4',
"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
"Mozilla/5.0 (Linux; Android 6.0.1; SAMSUNG SM-G570Y Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36", 'Mozilla/5.0 (Linux; Android 6.0.1; SAMSUNG SM-G570Y Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36',
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; FSL 7.0.5.01003)", 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; FSL 7.0.5.01003)',
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0", 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0',
"Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.2.8) Gecko/20100723 Ubuntu/10.04 (lucid) Firefox/3.6.8", 'Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.9.2.8) Gecko/20100723 Ubuntu/10.04 (lucid) Firefox/3.6.8',
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)", 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)',
"Mozilla/4.0 (compatible; MSIE 6.0; MSIE 5.5; Windows NT 5.0) Opera 7.02 Bork-edition [en]", 'Mozilla/4.0 (compatible; MSIE 6.0; MSIE 5.5; Windows NT 5.0) Opera 7.02 Bork-edition [en]',
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1", 'Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1',
"Mozilla/5.0 (Windows NT 5.1; rv:13.0) Gecko/20100101 Firefox/13.0.1" 'Mozilla/5.0 (Windows NT 5.1; rv:13.0) Gecko/20100101 Firefox/13.0.1',
] ];
exports.DMScanLevel = { exports.DMScanLevel = {
0: 'NOT_SCAN', 0: 'NOT_SCAN',
@@ -116,36 +116,36 @@ exports.stickerAnimationMode = {
}; };
exports.localeObject = { exports.localeObject = {
da: 'DANISH', da: 'DANISH',
de: 'GERMAN', de: 'GERMAN',
'en-GB': 'ENGLISH_UK', 'en-GB': 'ENGLISH_UK',
'en-US': 'ENGLISH_US', 'en-US': 'ENGLISH_US',
'es-ES': 'SPANISH', 'es-ES': 'SPANISH',
fr: 'FRENCH', fr: 'FRENCH',
hr: 'CROATIAN', hr: 'CROATIAN',
it: 'ITALIAN', it: 'ITALIAN',
lt: 'LITHUANIAN', lt: 'LITHUANIAN',
hu: 'HUNGARIAN', hu: 'HUNGARIAN',
nl: 'DUTCH', nl: 'DUTCH',
no: 'NORWEGIAN', no: 'NORWEGIAN',
pl: 'POLISH', pl: 'POLISH',
'pt-BR': 'BRAZILIAN_PORTUGUESE', 'pt-BR': 'BRAZILIAN_PORTUGUESE',
ro: 'ROMANIA_ROMANIAN', ro: 'ROMANIA_ROMANIAN',
fi: 'FINNISH', fi: 'FINNISH',
'sv-SE': 'SWEDISH', 'sv-SE': 'SWEDISH',
vi: 'VIETNAMESE', vi: 'VIETNAMESE',
tr: 'TURKISH', tr: 'TURKISH',
cs: 'CZECH', cs: 'CZECH',
el: 'GREEK', el: 'GREEK',
bg: 'BULGARIAN', bg: 'BULGARIAN',
ru: 'RUSSIAN', ru: 'RUSSIAN',
uk: 'UKRAINIAN', uk: 'UKRAINIAN',
hi: 'HINDI', hi: 'HINDI',
th: 'THAI', th: 'THAI',
'zh-CN': 'CHINA_CHINESE', 'zh-CN': 'CHINA_CHINESE',
ja: 'JAPANESE', ja: 'JAPANESE',
'zh-TW': 'TAIWAN_CHINESE', 'zh-TW': 'TAIWAN_CHINESE',
ko: 'KOREAN', ko: 'KOREAN',
}; };
exports.UserAgent = listUserAgent[Math.floor(Math.random() * listUserAgent.length)]; exports.UserAgent = listUserAgent[Math.floor(Math.random() * listUserAgent.length)];
@@ -254,127 +254,127 @@ exports.Status = {
}; };
exports.Opcodes = { exports.Opcodes = {
DISPATCH: 0, // # Receive dispatches an event DISPATCH: 0, // # Receive dispatches an event
HEARTBEAT: 1, // # Send/Receive used for ping checking HEARTBEAT: 1, // # Send/Receive used for ping checking
IDENTIFY: 2, // # Send used for client handshake IDENTIFY: 2, // # Send used for client handshake
STATUS_UPDATE: 3, // # Send used to update the client status STATUS_UPDATE: 3, // # Send used to update the client status
VOICE_STATE_UPDATE: 4, // # Send used to join/move/leave voice channels VOICE_STATE_UPDATE: 4, // # Send used to join/move/leave voice channels
VOICE_GUILD_PING: 5, // # Send used for voice ping checking VOICE_GUILD_PING: 5, // # Send used for voice ping checking
RESUME: 6, // # Send used to resume a closed connection RESUME: 6, // # Send used to resume a closed connection
RECONNECT: 7, // # Receive used to tell when to reconnect (sometimes...) RECONNECT: 7, // # Receive used to tell when to reconnect (sometimes...)
REQUEST_GUILD_MEMBERS: 8, // # Send used to request guild members (when searching for members in the search bar of a guild) REQUEST_GUILD_MEMBERS: 8, // # Send used to request guild members (when searching for members in the search bar of a guild)
INVALID_SESSION: 9, // # Receive used to notify client they have an invalid session id INVALID_SESSION: 9, // # Receive used to notify client they have an invalid session id
HELLO: 10, // # Receive sent immediately after connecting, contains heartbeat and server debug information HELLO: 10, // # Receive sent immediately after connecting, contains heartbeat and server debug information
HEARTBEAT_ACK: 11, // # Sent immediately following a client heartbeat that was received HEARTBEAT_ACK: 11, // # Sent immediately following a client heartbeat that was received
// GUILD_SYNC: 12, // # Receive guild_sync but not used anymore // GUILD_SYNC: 12, // # Receive guild_sync but not used anymore
/** Add some opcode from Discum /** Add some opcode from Discum
/* @extends https://github.com/Merubokkusu/Discord-S.C.U.M/blob/master/discum/gateway/gateway.py#L56 /* @extends https://github.com/Merubokkusu/Discord-S.C.U.M/blob/master/discum/gateway/gateway.py#L56
*/ */
DM_UPDATE: 13, // # Send used to get dm features DM_UPDATE: 13, // # Send used to get dm features
LAZY_REQUEST: 14, // # Send discord responds back with GUILD_MEMBER_LIST_UPDATE type SYNC... LAZY_REQUEST: 14, // # Send discord responds back with GUILD_MEMBER_LIST_UPDATE type SYNC...
LOBBY_CONNECT: 15, LOBBY_CONNECT: 15,
LOBBY_DISCONNECT: 16, LOBBY_DISCONNECT: 16,
LOBBY_VOICE_STATE_UPDATE: 17, // # Receive LOBBY_VOICE_STATE_UPDATE: 17, // # Receive
STREAM_CREATE: 18, STREAM_CREATE: 18,
STREAM_DELETE: 19, STREAM_DELETE: 19,
STREAM_WATCH: 20, STREAM_WATCH: 20,
STREAM_PING: 21, // # Send STREAM_PING: 21, // # Send
STREAM_SET_PAUSED: 22, STREAM_SET_PAUSED: 22,
REQUEST_APPLICATION_COMMANDS: 24, // # Send request application/bot cmds (user, message, and slash cmds) REQUEST_APPLICATION_COMMANDS: 24, // # Send request application/bot cmds (user, message, and slash cmds)
}; };
exports.Events = { exports.Events = {
RATE_LIMIT: 'rateLimit', RATE_LIMIT: 'rateLimit',
INVALID_REQUEST_WARNING: 'invalidRequestWarning', INVALID_REQUEST_WARNING: 'invalidRequestWarning',
API_RESPONSE: 'apiResponse', API_RESPONSE: 'apiResponse',
API_REQUEST: 'apiRequest', API_REQUEST: 'apiRequest',
CLIENT_READY: 'ready', CLIENT_READY: 'ready',
/** /**
* @deprecated See {@link https://github.com/discord/discord-api-docs/issues/3690 this issue} for more information. * @deprecated See {@link https://github.com/discord/discord-api-docs/issues/3690 this issue} for more information.
*/ */
APPLICATION_COMMAND_CREATE: 'applicationCommandCreate', APPLICATION_COMMAND_CREATE: 'applicationCommandCreate',
/** /**
* @deprecated See {@link https://github.com/discord/discord-api-docs/issues/3690 this issue} for more information. * @deprecated See {@link https://github.com/discord/discord-api-docs/issues/3690 this issue} for more information.
*/ */
APPLICATION_COMMAND_DELETE: 'applicationCommandDelete', APPLICATION_COMMAND_DELETE: 'applicationCommandDelete',
/** /**
* @deprecated See {@link https://github.com/discord/discord-api-docs/issues/3690 this issue} for more information. * @deprecated See {@link https://github.com/discord/discord-api-docs/issues/3690 this issue} for more information.
*/ */
APPLICATION_COMMAND_UPDATE: 'applicationCommandUpdate', APPLICATION_COMMAND_UPDATE: 'applicationCommandUpdate',
GUILD_CREATE: 'guildCreate', GUILD_CREATE: 'guildCreate',
GUILD_DELETE: 'guildDelete', GUILD_DELETE: 'guildDelete',
GUILD_UPDATE: 'guildUpdate', GUILD_UPDATE: 'guildUpdate',
GUILD_UNAVAILABLE: 'guildUnavailable', GUILD_UNAVAILABLE: 'guildUnavailable',
GUILD_MEMBER_ADD: 'guildMemberAdd', GUILD_MEMBER_ADD: 'guildMemberAdd',
GUILD_MEMBER_REMOVE: 'guildMemberRemove', GUILD_MEMBER_REMOVE: 'guildMemberRemove',
GUILD_MEMBER_UPDATE: 'guildMemberUpdate', GUILD_MEMBER_UPDATE: 'guildMemberUpdate',
GUILD_MEMBER_AVAILABLE: 'guildMemberAvailable', GUILD_MEMBER_AVAILABLE: 'guildMemberAvailable',
GUILD_MEMBERS_CHUNK: 'guildMembersChunk', GUILD_MEMBERS_CHUNK: 'guildMembersChunk',
GUILD_MEMBER_LIST_UPDATE: 'guildMemberListUpdate', GUILD_MEMBER_LIST_UPDATE: 'guildMemberListUpdate',
GUILD_INTEGRATIONS_UPDATE: 'guildIntegrationsUpdate', GUILD_INTEGRATIONS_UPDATE: 'guildIntegrationsUpdate',
GUILD_ROLE_CREATE: 'roleCreate', GUILD_ROLE_CREATE: 'roleCreate',
GUILD_ROLE_DELETE: 'roleDelete', GUILD_ROLE_DELETE: 'roleDelete',
INVITE_CREATE: 'inviteCreate', INVITE_CREATE: 'inviteCreate',
INVITE_DELETE: 'inviteDelete', INVITE_DELETE: 'inviteDelete',
GUILD_ROLE_UPDATE: 'roleUpdate', GUILD_ROLE_UPDATE: 'roleUpdate',
GUILD_EMOJI_CREATE: 'emojiCreate', GUILD_EMOJI_CREATE: 'emojiCreate',
GUILD_EMOJI_DELETE: 'emojiDelete', GUILD_EMOJI_DELETE: 'emojiDelete',
GUILD_EMOJI_UPDATE: 'emojiUpdate', GUILD_EMOJI_UPDATE: 'emojiUpdate',
GUILD_BAN_ADD: 'guildBanAdd', GUILD_BAN_ADD: 'guildBanAdd',
GUILD_BAN_REMOVE: 'guildBanRemove', GUILD_BAN_REMOVE: 'guildBanRemove',
CHANNEL_CREATE: 'channelCreate', CHANNEL_CREATE: 'channelCreate',
CHANNEL_DELETE: 'channelDelete', CHANNEL_DELETE: 'channelDelete',
CHANNEL_UPDATE: 'channelUpdate', CHANNEL_UPDATE: 'channelUpdate',
CHANNEL_PINS_UPDATE: 'channelPinsUpdate', CHANNEL_PINS_UPDATE: 'channelPinsUpdate',
MESSAGE_CREATE: 'messageCreate', MESSAGE_CREATE: 'messageCreate',
MESSAGE_DELETE: 'messageDelete', MESSAGE_DELETE: 'messageDelete',
MESSAGE_UPDATE: 'messageUpdate', MESSAGE_UPDATE: 'messageUpdate',
MESSAGE_BULK_DELETE: 'messageDeleteBulk', MESSAGE_BULK_DELETE: 'messageDeleteBulk',
MESSAGE_REACTION_ADD: 'messageReactionAdd', MESSAGE_REACTION_ADD: 'messageReactionAdd',
MESSAGE_REACTION_REMOVE: 'messageReactionRemove', MESSAGE_REACTION_REMOVE: 'messageReactionRemove',
MESSAGE_REACTION_REMOVE_ALL: 'messageReactionRemoveAll', MESSAGE_REACTION_REMOVE_ALL: 'messageReactionRemoveAll',
MESSAGE_REACTION_REMOVE_EMOJI: 'messageReactionRemoveEmoji', MESSAGE_REACTION_REMOVE_EMOJI: 'messageReactionRemoveEmoji',
THREAD_CREATE: 'threadCreate', THREAD_CREATE: 'threadCreate',
THREAD_DELETE: 'threadDelete', THREAD_DELETE: 'threadDelete',
THREAD_UPDATE: 'threadUpdate', THREAD_UPDATE: 'threadUpdate',
THREAD_LIST_SYNC: 'threadListSync', THREAD_LIST_SYNC: 'threadListSync',
THREAD_MEMBER_UPDATE: 'threadMemberUpdate', THREAD_MEMBER_UPDATE: 'threadMemberUpdate',
THREAD_MEMBERS_UPDATE: 'threadMembersUpdate', THREAD_MEMBERS_UPDATE: 'threadMembersUpdate',
USER_UPDATE: 'userUpdate', USER_UPDATE: 'userUpdate',
PRESENCE_UPDATE: 'presenceUpdate', PRESENCE_UPDATE: 'presenceUpdate',
VOICE_SERVER_UPDATE: 'voiceServerUpdate', VOICE_SERVER_UPDATE: 'voiceServerUpdate',
VOICE_STATE_UPDATE: 'voiceStateUpdate', VOICE_STATE_UPDATE: 'voiceStateUpdate',
TYPING_START: 'typingStart', TYPING_START: 'typingStart',
WEBHOOKS_UPDATE: 'webhookUpdate', WEBHOOKS_UPDATE: 'webhookUpdate',
INTERACTION_CREATE: 'interactionCreate', INTERACTION_CREATE: 'interactionCreate',
INTERACTION_SUCCESS: 'interactionSuccess', INTERACTION_SUCCESS: 'interactionSuccess',
INTERACTION_FAILED: 'interactionFailed', INTERACTION_FAILED: 'interactionFailed',
ERROR: 'error', ERROR: 'error',
WARN: 'warn', WARN: 'warn',
DEBUG: 'debug', DEBUG: 'debug',
CACHE_SWEEP: 'cacheSweep', CACHE_SWEEP: 'cacheSweep',
SHARD_DISCONNECT: 'shardDisconnect', SHARD_DISCONNECT: 'shardDisconnect',
SHARD_ERROR: 'shardError', SHARD_ERROR: 'shardError',
SHARD_RECONNECTING: 'shardReconnecting', SHARD_RECONNECTING: 'shardReconnecting',
SHARD_READY: 'shardReady', SHARD_READY: 'shardReady',
SHARD_RESUME: 'shardResume', SHARD_RESUME: 'shardResume',
INVALIDATED: 'invalidated', INVALIDATED: 'invalidated',
RAW: 'raw', RAW: 'raw',
STAGE_INSTANCE_CREATE: 'stageInstanceCreate', STAGE_INSTANCE_CREATE: 'stageInstanceCreate',
STAGE_INSTANCE_UPDATE: 'stageInstanceUpdate', STAGE_INSTANCE_UPDATE: 'stageInstanceUpdate',
STAGE_INSTANCE_DELETE: 'stageInstanceDelete', STAGE_INSTANCE_DELETE: 'stageInstanceDelete',
GUILD_STICKER_CREATE: 'stickerCreate', GUILD_STICKER_CREATE: 'stickerCreate',
GUILD_STICKER_DELETE: 'stickerDelete', GUILD_STICKER_DELETE: 'stickerDelete',
GUILD_STICKER_UPDATE: 'stickerUpdate', GUILD_STICKER_UPDATE: 'stickerUpdate',
GUILD_SCHEDULED_EVENT_CREATE: 'guildScheduledEventCreate', GUILD_SCHEDULED_EVENT_CREATE: 'guildScheduledEventCreate',
GUILD_SCHEDULED_EVENT_UPDATE: 'guildScheduledEventUpdate', GUILD_SCHEDULED_EVENT_UPDATE: 'guildScheduledEventUpdate',
GUILD_SCHEDULED_EVENT_DELETE: 'guildScheduledEventDelete', GUILD_SCHEDULED_EVENT_DELETE: 'guildScheduledEventDelete',
GUILD_SCHEDULED_EVENT_USER_ADD: 'guildScheduledEventUserAdd', GUILD_SCHEDULED_EVENT_USER_ADD: 'guildScheduledEventUserAdd',
GUILD_SCHEDULED_EVENT_USER_REMOVE: 'guildScheduledEventUserRemove', GUILD_SCHEDULED_EVENT_USER_REMOVE: 'guildScheduledEventUserRemove',
RELATIONSHIP_ADD: 'relationshipAdd', RELATIONSHIP_ADD: 'relationshipAdd',
RELATIONSHIP_REMOVE: 'relationshipRemove', RELATIONSHIP_REMOVE: 'relationshipRemove',
/* Add */ /* Add */
UNHANDLED_PACKET: 'unhandledPacket', UNHANDLED_PACKET: 'unhandledPacket',
}; };
exports.ShardEvents = { exports.ShardEvents = {
@@ -1330,8 +1330,8 @@ exports.RelationshipTypes = createEnum([null, 'FRIEND', 'BLOCKED', 'INCOMING_REQ
exports.Relationship = { exports.Relationship = {
0: 'NONE', 0: 'NONE',
1: 'FRIEND', 1: 'FRIEND',
2: 'BLOCKED', 2: 'BLOCKED',
3: 'INCOMING_REQUEST', 3: 'INCOMING_REQUEST',
4: 'OUTGOING_REQUEST', 4: 'OUTGOING_REQUEST',
}; };
@@ -1374,7 +1374,7 @@ exports.HypeSquadOptions = createEnum(['LEAVE', 'HOUSE_BRAVERY', 'HOUSE_BRILLIAN
exports._cleanupSymbol = Symbol('djsCleanup'); exports._cleanupSymbol = Symbol('djsCleanup');
function keyMirror(arr) { function keyMirror(arr) {
let tmp = Object.create(null); const tmp = Object.create(null);
for (const value of arr) tmp[value] = value; for (const value of arr) tmp[value] = value;
return tmp; return tmp;
} }

View File

@@ -132,7 +132,7 @@ class Options extends null {
*/ */
static createDefault() { static createDefault() {
return { return {
jsonTransformer: (object) => JSONBig.stringify(object), jsonTransformer: object => JSONBig.stringify(object),
checkUpdate: true, checkUpdate: true,
readyStatus: false, readyStatus: false,
autoCookie: true, autoCookie: true,
@@ -143,14 +143,7 @@ class Options extends null {
messageSweepInterval: 0, messageSweepInterval: 0,
invalidRequestWarningInterval: 0, invalidRequestWarningInterval: 0,
intents: 65535, intents: 65535,
partials: [ partials: ['USER', 'CHANNEL', 'GUILD_MEMBER', 'MESSAGE', 'REACTION', 'GUILD_SCHEDULED_EVENT'], // Enable the partials
'USER',
'CHANNEL',
'GUILD_MEMBER',
'MESSAGE',
'REACTION',
'GUILD_SCHEDULED_EVENT',
], // Enable the partials
restWsBridgeTimeout: 5_000, restWsBridgeTimeout: 5_000,
restRequestTimeout: 15_000, restRequestTimeout: 15_000,
restGlobalRateLimit: 0, restGlobalRateLimit: 0,
@@ -165,9 +158,9 @@ class Options extends null {
large_threshold: 50, large_threshold: 50,
compress: false, compress: false,
properties: { properties: {
//$os: 'iPhone14,5', // $os: 'iPhone14,5',
//$browser: 'Discord iOS', // $browser: 'Discord iOS',
//$device: 'iPhone14,5 OS 15.2', // $device: 'iPhone14,5 OS 15.2',
$os: 'Windows', $os: 'Windows',
$browser: 'Discord Client', $browser: 'Discord Client',
$device: 'ASUS ROG Phone 5', $device: 'ASUS ROG Phone 5',
@@ -182,8 +175,7 @@ class Options extends null {
'Cache-Control': 'no-cache', 'Cache-Control': 'no-cache',
Pragma: 'no-cache', Pragma: 'no-cache',
Referer: 'https://discord.com/channels/@me', Referer: 'https://discord.com/channels/@me',
'Sec-Ch-Ua': 'Sec-Ch-Ua': '"Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100',
'"Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100',
'Sec-Ch-Ua-Mobile': '?0', 'Sec-Ch-Ua-Mobile': '?0',
'Sec-Ch-Ua-Platform': '"Windows"', 'Sec-Ch-Ua-Platform': '"Windows"',
'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Dest': 'empty',

47
tsconfig.json Normal file
View File

@@ -0,0 +1,47 @@
{
// Mapped from https://www.typescriptlang.org/tsconfig
"compilerOptions": {
// Type Checking
"allowUnreachableCode": false,
"allowUnusedLabels": false,
// if true: conflicts with discord-api-types
"exactOptionalPropertyTypes": false,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"strict": true,
"useUnknownInCatchVariables": true,
// Modules
"module": "CommonJS",
"moduleResolution": "node",
"resolveJsonModule": true,
// Emit
"declaration": false,
"importHelpers": true,
"importsNotUsedAsValues": "error",
"inlineSources": false,
"newLine": "lf",
"noEmitHelpers": true,
"outDir": "dist",
"preserveConstEnums": true,
"removeComments": false,
"sourceMap": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
// Language and Environment
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [
"ESNext"
],
"target": "ES2020",
"useDefineForClassFields": true,
// Output Formatting
"pretty": false,
// Completeness
"skipLibCheck": false,
"skipDefaultLibCheck": true
}
}

60
typings/enums.d.ts vendored
View File

@@ -30,36 +30,36 @@ export const enum relationshipsType {
} }
export const enum localeSetting { export const enum localeSetting {
DANISH = 'da', DANISH = 'da',
GERMAN = 'de', GERMAN = 'de',
ENGLISH_UK = 'en-GB', ENGLISH_UK = 'en-GB',
ENGLISH_US = 'en-US', ENGLISH_US = 'en-US',
SPANISH = 'es-ES', SPANISH = 'es-ES',
FRENCH = 'fr', FRENCH = 'fr',
CROATIAN = 'hr', CROATIAN = 'hr',
ITALIAN = 'it', ITALIAN = 'it',
LITHUANIAN = 'lt', LITHUANIAN = 'lt',
HUNGARIAN = 'hu', HUNGARIAN = 'hu',
DUTCH = 'nl', DUTCH = 'nl',
NORWEGIAN = 'no', NORWEGIAN = 'no',
POLISH = 'pl', POLISH = 'pl',
BRAZILIAN_PORTUGUESE = 'pt-BR', BRAZILIAN_PORTUGUESE = 'pt-BR',
ROMANIA_ROMANIAN = 'ro', ROMANIA_ROMANIAN = 'ro',
FINNISH = 'fi', FINNISH = 'fi',
SWEDISH = 'sv-SE', SWEDISH = 'sv-SE',
VIETNAMESE = 'vi', VIETNAMESE = 'vi',
TURKISH = 'tr', TURKISH = 'tr',
CZECH = 'cs', CZECH = 'cs',
GREEK = 'el', GREEK = 'el',
BULGARIAN = 'bg', BULGARIAN = 'bg',
RUSSIAN = 'ru', RUSSIAN = 'ru',
UKRAINIAN = 'uk', UKRAINIAN = 'uk',
HINDI = 'hi', HINDI = 'hi',
THAI = 'th', THAI = 'th',
CHINA_CHINESE = 'zh-CN', CHINA_CHINESE = 'zh-CN',
JAPANESE = 'ja', JAPANESE = 'ja',
TAIWAN_CHINESE = 'zh-TW', TAIWAN_CHINESE = 'zh-TW',
KOREAN = 'ko', KOREAN = 'ko',
} }
export const enum ApplicationCommandTypes { export const enum ApplicationCommandTypes {

366
typings/index.d.ts vendored
View File

@@ -147,7 +147,7 @@ import {
RawWidgetData, RawWidgetData,
RawWidgetMemberData, RawWidgetMemberData,
} from './rawDataTypes'; } from './rawDataTypes';
import { RelationshipType } from '../src/util/Constants'; import { RelationshipTypes } from '../src/util/Constants';
//#region Classes //#region Classes
@@ -1008,7 +1008,7 @@ export class Guild extends AnonymousGuild {
public setIcon(icon: BufferResolvable | Base64Resolvable | null, reason?: string): Promise<Guild>; public setIcon(icon: BufferResolvable | Base64Resolvable | null, reason?: string): Promise<Guild>;
public setName(name: string, reason?: string): Promise<Guild>; public setName(name: string, reason?: string): Promise<Guild>;
public setOwner(owner: GuildMemberResolvable, reason?: string): Promise<Guild>; public setOwner(owner: GuildMemberResolvable, reason?: string): Promise<Guild>;
public setPosition(position: number, type: FOLDER | HOME, folderID?: FolderID): Promise<Guild>; public setPosition(position: number, type: 'FOLDER' | 'HOME', folderID?: FolderID): Promise<Guild>;
public setPreferredLocale(preferredLocale: string, reason?: string): Promise<Guild>; public setPreferredLocale(preferredLocale: string, reason?: string): Promise<Guild>;
public setPublicUpdatesChannel(publicUpdatesChannel: TextChannelResolvable | null, reason?: string): Promise<Guild>; public setPublicUpdatesChannel(publicUpdatesChannel: TextChannelResolvable | null, reason?: string): Promise<Guild>;
/** @deprecated Use {@link RoleManager.setPositions} instead */ /** @deprecated Use {@link RoleManager.setPositions} instead */
@@ -1019,7 +1019,12 @@ export class Guild extends AnonymousGuild {
public setSystemChannelFlags(systemChannelFlags: SystemChannelFlagsResolvable, reason?: string): Promise<Guild>; public setSystemChannelFlags(systemChannelFlags: SystemChannelFlagsResolvable, reason?: string): Promise<Guild>;
public setVerificationLevel(verificationLevel: VerificationLevel | number, reason?: string): Promise<Guild>; public setVerificationLevel(verificationLevel: VerificationLevel | number, reason?: string): Promise<Guild>;
public setPremiumProgressBarEnabled(enabled?: boolean, reason?: string): Promise<Guild>; public setPremiumProgressBarEnabled(enabled?: boolean, reason?: string): Promise<Guild>;
public setCommunity(stats: boolean, publicUpdatesChannel: TextChannelResolvable, rulesChannel: TextChannelResolvable, reason?: string): Promise<Guild>; public setCommunity(
stats: boolean,
publicUpdatesChannel: TextChannelResolvable,
rulesChannel: TextChannelResolvable,
reason?: string,
): Promise<Guild>;
public setWidgetSettings(settings: GuildWidgetSettingsData, reason?: string): Promise<Guild>; public setWidgetSettings(settings: GuildWidgetSettingsData, reason?: string): Promise<Guild>;
public toJSON(): unknown; public toJSON(): unknown;
} }
@@ -1043,17 +1048,17 @@ export class GuildAuditLogs<T extends GuildAuditLogsResolvable = 'ALL'> {
export class GuildAuditLogsEntry< export class GuildAuditLogsEntry<
TActionRaw extends GuildAuditLogsResolvable = 'ALL', TActionRaw extends GuildAuditLogsResolvable = 'ALL',
TAction = TActionRaw extends keyof GuildAuditLogsIds TAction = TActionRaw extends keyof GuildAuditLogsIds
? GuildAuditLogsIds[TActionRaw] ? GuildAuditLogsIds[TActionRaw]
: TActionRaw extends null : TActionRaw extends null
? 'ALL' ? 'ALL'
: TActionRaw, : TActionRaw,
TActionType extends GuildAuditLogsActionType = TAction extends keyof GuildAuditLogsTypes TActionType extends GuildAuditLogsActionType = TAction extends keyof GuildAuditLogsTypes
? GuildAuditLogsTypes[TAction][1] ? GuildAuditLogsTypes[TAction][1]
: 'ALL', : 'ALL',
TTargetType extends GuildAuditLogsTarget = TAction extends keyof GuildAuditLogsTypes TTargetType extends GuildAuditLogsTarget = TAction extends keyof GuildAuditLogsTypes
? GuildAuditLogsTypes[TAction][0] ? GuildAuditLogsTypes[TAction][0]
: 'UNKNOWN', : 'UNKNOWN',
> { > {
private constructor(logs: GuildAuditLogs, guild: Guild, data: RawGuildAuditLogEntryData); private constructor(logs: GuildAuditLogs, guild: Guild, data: RawGuildAuditLogEntryData);
public action: TAction; public action: TAction;
public actionType: TActionType; public actionType: TActionType;
@@ -1291,7 +1296,7 @@ export class HTTPError extends Error {
} }
// tslint:disable-next-line:no-empty-interface - Merge RateLimitData into RateLimitError to not have to type it again // tslint:disable-next-line:no-empty-interface - Merge RateLimitData into RateLimitError to not have to type it again
export interface RateLimitError extends RateLimitData { } export interface RateLimitError extends RateLimitData {}
export class RateLimitError extends Error { export class RateLimitError extends Error {
private constructor(data: RateLimitData); private constructor(data: RateLimitData);
public name: 'RateLimitError'; public name: 'RateLimitError';
@@ -1344,7 +1349,7 @@ export type CacheTypeReducer<
RawType = CachedType, RawType = CachedType,
PresentType = CachedType | RawType, PresentType = CachedType | RawType,
Fallback = PresentType | null, Fallback = PresentType | null,
> = [State] extends ['cached'] > = [State] extends ['cached']
? CachedType ? CachedType
: [State] extends ['raw'] : [State] extends ['raw']
? RawType ? RawType
@@ -1490,25 +1495,25 @@ export class LimitedCollection<K, V> extends Collection<K, V> {
export type MessageCollectorOptionsParams<T extends MessageComponentTypeResolvable, Cached extends boolean = boolean> = export type MessageCollectorOptionsParams<T extends MessageComponentTypeResolvable, Cached extends boolean = boolean> =
| { | {
componentType?: T; componentType?: T;
} & MessageComponentCollectorOptions<MappedInteractionTypes<Cached>[T]>; } & MessageComponentCollectorOptions<MappedInteractionTypes<Cached>[T]>;
export type MessageChannelCollectorOptionsParams< export type MessageChannelCollectorOptionsParams<
T extends MessageComponentTypeResolvable, T extends MessageComponentTypeResolvable,
Cached extends boolean = boolean, Cached extends boolean = boolean,
> = > =
| { | {
componentType?: T; componentType?: T;
} & MessageChannelComponentCollectorOptions<MappedInteractionTypes<Cached>[T]>; } & MessageChannelComponentCollectorOptions<MappedInteractionTypes<Cached>[T]>;
export type AwaitMessageCollectorOptionsParams< export type AwaitMessageCollectorOptionsParams<
T extends MessageComponentTypeResolvable, T extends MessageComponentTypeResolvable,
Cached extends boolean = boolean, Cached extends boolean = boolean,
> = > =
| { componentType?: T } & Pick< | { componentType?: T } & Pick<
InteractionCollectorOptions<MappedInteractionTypes<Cached>[T]>, InteractionCollectorOptions<MappedInteractionTypes<Cached>[T]>,
keyof AwaitMessageComponentOptions<any> keyof AwaitMessageComponentOptions<any>
>; >;
export interface StringMappedInteractionTypes<Cached extends CacheType = CacheType> { export interface StringMappedInteractionTypes<Cached extends CacheType = CacheType> {
BUTTON: ButtonInteraction<Cached>; BUTTON: ButtonInteraction<Cached>;
@@ -1600,8 +1605,11 @@ export class Message<Cached extends boolean = boolean> extends Base {
public unpin(): Promise<Message>; public unpin(): Promise<Message>;
public inGuild(): this is Message<true> & this; public inGuild(): this is Message<true> & this;
// Added // Added
public clickButton(buttonID: String<MessageButton.customId>): Promise<pending> public clickButton(buttonID: String<MessageButton.customId>): Promise<pending>;
public selectMenu(menuID: String<MessageSelectMenu.customId> | Array<options>, options: Array<String>): Promise<pending> public selectMenu(
menuID: String<MessageSelectMenu.customId> | Array<options>,
options: Array<String>,
): Promise<pending>;
public contextMenu(botID: DiscordBotID, commandName: String<ApplicationCommand.name>): Promise; public contextMenu(botID: DiscordBotID, commandName: String<ApplicationCommand.name>): Promise;
} }
@@ -1717,7 +1725,7 @@ export class MessageComponentInteraction<Cached extends CacheType = CacheType> e
export class MessageContextMenuInteraction< export class MessageContextMenuInteraction<
Cached extends CacheType = CacheType, Cached extends CacheType = CacheType,
> extends ContextMenuInteraction<Cached> { > extends ContextMenuInteraction<Cached> {
public readonly targetMessage: NonNullable<CommandInteractionOption<Cached>['message']>; public readonly targetMessage: NonNullable<CommandInteractionOption<Cached>['message']>;
public inGuild(): this is MessageContextMenuInteraction<'present'>; public inGuild(): this is MessageContextMenuInteraction<'present'>;
public inCachedGuild(): this is MessageContextMenuInteraction<'cached'>; public inCachedGuild(): this is MessageContextMenuInteraction<'cached'>;
@@ -2973,7 +2981,7 @@ export class ApplicationCommandManager<
ApplicationCommandScope = ApplicationCommand<{ guild: GuildResolvable }>, ApplicationCommandScope = ApplicationCommand<{ guild: GuildResolvable }>,
PermissionsOptionsExtras = { guild: GuildResolvable }, PermissionsOptionsExtras = { guild: GuildResolvable },
PermissionsGuildType = null, PermissionsGuildType = null,
> extends CachedManager<Snowflake, ApplicationCommandScope, ApplicationCommandResolvable> { > extends CachedManager<Snowflake, ApplicationCommandScope, ApplicationCommandResolvable> {
protected constructor(client: Client, iterable?: Iterable<unknown>, user: User); protected constructor(client: Client, iterable?: Iterable<unknown>, user: User);
public permissions: ApplicationCommandPermissionsManager< public permissions: ApplicationCommandPermissionsManager<
{ command?: ApplicationCommandResolvable } & PermissionsOptionsExtras, { command?: ApplicationCommandResolvable } & PermissionsOptionsExtras,
@@ -3020,8 +3028,11 @@ export class ApplicationCommandPermissionsManager<
FullPermissionsOptions, FullPermissionsOptions,
GuildType, GuildType,
CommandIdType, CommandIdType,
> extends BaseManager { > extends BaseManager {
private constructor(manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand, user: User); private constructor(
manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand,
user: User,
);
private manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand; private manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand;
public client: Client; public client: Client;
@@ -3037,13 +3048,13 @@ export class ApplicationCommandPermissionsManager<
public remove( public remove(
options: options:
| (FetchSingleOptions & { | (FetchSingleOptions & {
users: UserResolvable | UserResolvable[]; users: UserResolvable | UserResolvable[];
roles?: RoleResolvable | RoleResolvable[]; roles?: RoleResolvable | RoleResolvable[];
}) })
| (FetchSingleOptions & { | (FetchSingleOptions & {
users?: UserResolvable | UserResolvable[]; users?: UserResolvable | UserResolvable[];
roles: RoleResolvable | RoleResolvable[]; roles: RoleResolvable | RoleResolvable[];
}), }),
): Promise<ApplicationCommandPermissions[]>; ): Promise<ApplicationCommandPermissions[]>;
public set( public set(
options: FetchSingleOptions & { permissions: ApplicationCommandPermissionData[] }, options: FetchSingleOptions & { permissions: ApplicationCommandPermissionData[] },
@@ -3092,8 +3103,16 @@ export class ClientUserSettingManager {
public afkTimeout: number | null; // second public afkTimeout: number | null; // second
public stickerAnimationMode: stickerAnimationMode; public stickerAnimationMode: stickerAnimationMode;
public showEmojiReactions: boolean | null; public showEmojiReactions: boolean | null;
public customStatus: { text?: string, expires_at?: string | null, emoji_name?: string, emoji_id?: Snowflake | null, status?: PresenceStatusData } | object; public customStatus:
public addFriendFrom: { all?: boolean, mutual_friends?: boolean, mututal_guilds?: boolean } | object; | {
text?: string;
expires_at?: string | null;
emoji_name?: string;
emoji_id?: Snowflake | null;
status?: PresenceStatusData;
}
| object;
public addFriendFrom: { all?: boolean; mutual_friends?: boolean; mututal_guilds?: boolean } | object;
public guildMetadata: Collection<Snowflake, object>; public guildMetadata: Collection<Snowflake, object>;
public disableDMfromServer: Collection<Snowflake, boolean>; public disableDMfromServer: Collection<Snowflake, boolean>;
public fetch(): Promise<RawUserSettingsData>; public fetch(): Promise<RawUserSettingsData>;
@@ -3227,7 +3246,7 @@ export class GuildScheduledEventManager extends CachedManager<
public fetch(): Promise<Collection<Snowflake, GuildScheduledEvent>>; public fetch(): Promise<Collection<Snowflake, GuildScheduledEvent>>;
public fetch< public fetch<
T extends GuildScheduledEventResolvable | FetchGuildScheduledEventOptions | FetchGuildScheduledEventsOptions, T extends GuildScheduledEventResolvable | FetchGuildScheduledEventOptions | FetchGuildScheduledEventsOptions,
>(options?: T): Promise<GuildScheduledEventManagerFetchResult<T>>; >(options?: T): Promise<GuildScheduledEventManagerFetchResult<T>>;
public edit<S extends GuildScheduledEventStatus, T extends GuildScheduledEventSetStatusArg<S>>( public edit<S extends GuildScheduledEventStatus, T extends GuildScheduledEventSetStatusArg<S>>(
guildScheduledEvent: GuildScheduledEventResolvable, guildScheduledEvent: GuildScheduledEventResolvable,
options: GuildScheduledEventEditOptions<S, T>, options: GuildScheduledEventEditOptions<S, T>,
@@ -3757,12 +3776,12 @@ export interface ApplicationCommandChannelOption extends BaseApplicationCommandO
export interface ApplicationCommandAutocompleteOption extends Omit<BaseApplicationCommandOptionsData, 'autocomplete'> { export interface ApplicationCommandAutocompleteOption extends Omit<BaseApplicationCommandOptionsData, 'autocomplete'> {
type: type:
| 'STRING' | 'STRING'
| 'NUMBER' | 'NUMBER'
| 'INTEGER' | 'INTEGER'
| ApplicationCommandOptionTypes.STRING | ApplicationCommandOptionTypes.STRING
| ApplicationCommandOptionTypes.NUMBER | ApplicationCommandOptionTypes.NUMBER
| ApplicationCommandOptionTypes.INTEGER; | ApplicationCommandOptionTypes.INTEGER;
autocomplete: true; autocomplete: true;
} }
@@ -3912,11 +3931,11 @@ export interface BaseFetchOptions {
} }
export interface guildSearchInteraction { export interface guildSearchInteraction {
type?: ApplicationCommandTypes, type?: ApplicationCommandTypes;
query?: String | void, query?: String | void;
limit?: Number, limit?: Number;
offset?: Number, offset?: Number;
botID?: Array<User.id>, botID?: Array<User.id>;
} }
export interface BaseMessageComponentOptions { export interface BaseMessageComponentOptions {
@@ -3970,8 +3989,8 @@ export type CacheFactory = (
export type CacheWithLimitsOptions = { export type CacheWithLimitsOptions = {
[K in keyof Caches]?: Caches[K][0]['prototype'] extends DataManager<infer K, infer V, any> [K in keyof Caches]?: Caches[K][0]['prototype'] extends DataManager<infer K, infer V, any>
? LimitedCollectionOptions<K, V> | number ? LimitedCollectionOptions<K, V> | number
: never; : never;
}; };
export interface CategoryCreateChannelOptions { export interface CategoryCreateChannelOptions {
@@ -4089,7 +4108,7 @@ export interface ClientEvents extends BaseClientEvents {
members: Collection<Snowflake, GuildMember>, members: Collection<Snowflake, GuildMember>,
guild: Guild, guild: Guild,
data: {}, // see: https://luna.gitlab.io/discord-unofficial-docs/lazy_guilds.html data: {}, // see: https://luna.gitlab.io/discord-unofficial-docs/lazy_guilds.html
] ];
guildUpdate: [oldGuild: Guild, newGuild: Guild]; guildUpdate: [oldGuild: Guild, newGuild: Guild];
inviteCreate: [invite: Invite]; inviteCreate: [invite: Invite];
inviteDelete: [invite: Invite]; inviteDelete: [invite: Invite];
@@ -4127,9 +4146,9 @@ export interface ClientEvents extends BaseClientEvents {
webhookUpdate: [channel: TextChannel | NewsChannel]; webhookUpdate: [channel: TextChannel | NewsChannel];
/** @deprecated Use interactionCreate instead */ /** @deprecated Use interactionCreate instead */
interaction: [interaction: Interaction]; interaction: [interaction: Interaction];
interactionCreate: [interaction: Interaction | { nonce: Snowflake, id: Snowflake }]; interactionCreate: [interaction: Interaction | { nonce: Snowflake; id: Snowflake }];
interactionSuccess: [interaction: { nonce: Snowflake, id: Snowflake }]; interactionSuccess: [interaction: { nonce: Snowflake; id: Snowflake }];
interactionFailed: [interaction: { nonce: Snowflake, id: Snowflake }]; interactionFailed: [interaction: { nonce: Snowflake; id: Snowflake }];
shardDisconnect: [closeEvent: CloseEvent, shardId: number]; shardDisconnect: [closeEvent: CloseEvent, shardId: number];
shardError: [error: Error, shardId: number]; shardError: [error: Error, shardId: number];
shardReady: [shardId: number, unavailableGuilds: Set<Snowflake> | undefined]; shardReady: [shardId: number, unavailableGuilds: Set<Snowflake> | undefined];
@@ -4146,20 +4165,9 @@ export interface ClientEvents extends BaseClientEvents {
guildScheduledEventDelete: [guildScheduledEvent: GuildScheduledEvent]; guildScheduledEventDelete: [guildScheduledEvent: GuildScheduledEvent];
guildScheduledEventUserAdd: [guildScheduledEvent: GuildScheduledEvent, user: User]; guildScheduledEventUserAdd: [guildScheduledEvent: GuildScheduledEvent, user: User];
guildScheduledEventUserRemove: [guildScheduledEvent: GuildScheduledEvent, user: User]; guildScheduledEventUserRemove: [guildScheduledEvent: GuildScheduledEvent, user: User];
relationshipAdd: [ relationshipAdd: [id: Snowflake, type: RelationshipTypes, user: User];
id: Snowflake, relationshipRemove: [id: Snowflake, type: RelationshipTypes, user: User];
type: RelationshipType, unhandledPacket: [packet: { op: GatewayOpcodes | number; d?: any; s?: number; t?: string }, shard: WebSocketShard];
user: User,
];
relationshipRemove: [
id: Snowflake,
type: RelationshipType,
user: User,
];
unhandledPacket: [
packet: { op: GatewayOpcodes | number, d?: any, s?: number, t?: string },
shard: WebSocketShard,
];
} }
export interface ClientFetchInviteOptions { export interface ClientFetchInviteOptions {
@@ -4339,80 +4347,80 @@ export interface ConstantsEvents {
/** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */
APPLICATION_COMMAND_DELETE: 'applicationCommandDelete'; APPLICATION_COMMAND_DELETE: 'applicationCommandDelete';
/** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */
APPLICATION_COMMAND_UPDATE: 'applicationCommandUpdate', APPLICATION_COMMAND_UPDATE: 'applicationCommandUpdate';
GUILD_CREATE: 'guildCreate', GUILD_CREATE: 'guildCreate';
GUILD_DELETE: 'guildDelete', GUILD_DELETE: 'guildDelete';
GUILD_UPDATE: 'guildUpdate', GUILD_UPDATE: 'guildUpdate';
GUILD_UNAVAILABLE: 'guildUnavailable', GUILD_UNAVAILABLE: 'guildUnavailable';
GUILD_MEMBER_ADD: 'guildMemberAdd', GUILD_MEMBER_ADD: 'guildMemberAdd';
GUILD_MEMBER_REMOVE: 'guildMemberRemove', GUILD_MEMBER_REMOVE: 'guildMemberRemove';
GUILD_MEMBER_UPDATE: 'guildMemberUpdate', GUILD_MEMBER_UPDATE: 'guildMemberUpdate';
GUILD_MEMBER_AVAILABLE: 'guildMemberAvailable', GUILD_MEMBER_AVAILABLE: 'guildMemberAvailable';
GUILD_MEMBERS_CHUNK: 'guildMembersChunk', GUILD_MEMBERS_CHUNK: 'guildMembersChunk';
GUILD_MEMBER_LIST_UPDATE: 'guildMemberListUpdate', GUILD_MEMBER_LIST_UPDATE: 'guildMemberListUpdate';
GUILD_INTEGRATIONS_UPDATE: 'guildIntegrationsUpdate', GUILD_INTEGRATIONS_UPDATE: 'guildIntegrationsUpdate';
GUILD_ROLE_CREATE: 'roleCreate', GUILD_ROLE_CREATE: 'roleCreate';
GUILD_ROLE_DELETE: 'roleDelete', GUILD_ROLE_DELETE: 'roleDelete';
INVITE_CREATE: 'inviteCreate', INVITE_CREATE: 'inviteCreate';
INVITE_DELETE: 'inviteDelete', INVITE_DELETE: 'inviteDelete';
GUILD_ROLE_UPDATE: 'roleUpdate', GUILD_ROLE_UPDATE: 'roleUpdate';
GUILD_EMOJI_CREATE: 'emojiCreate', GUILD_EMOJI_CREATE: 'emojiCreate';
GUILD_EMOJI_DELETE: 'emojiDelete', GUILD_EMOJI_DELETE: 'emojiDelete';
GUILD_EMOJI_UPDATE: 'emojiUpdate', GUILD_EMOJI_UPDATE: 'emojiUpdate';
GUILD_BAN_ADD: 'guildBanAdd', GUILD_BAN_ADD: 'guildBanAdd';
GUILD_BAN_REMOVE: 'guildBanRemove', GUILD_BAN_REMOVE: 'guildBanRemove';
CHANNEL_CREATE: 'channelCreate', CHANNEL_CREATE: 'channelCreate';
CHANNEL_DELETE: 'channelDelete', CHANNEL_DELETE: 'channelDelete';
CHANNEL_UPDATE: 'channelUpdate', CHANNEL_UPDATE: 'channelUpdate';
CHANNEL_PINS_UPDATE: 'channelPinsUpdate', CHANNEL_PINS_UPDATE: 'channelPinsUpdate';
MESSAGE_CREATE: 'messageCreate', MESSAGE_CREATE: 'messageCreate';
MESSAGE_DELETE: 'messageDelete', MESSAGE_DELETE: 'messageDelete';
MESSAGE_UPDATE: 'messageUpdate', MESSAGE_UPDATE: 'messageUpdate';
MESSAGE_BULK_DELETE: 'messageDeleteBulk', MESSAGE_BULK_DELETE: 'messageDeleteBulk';
MESSAGE_REACTION_ADD: 'messageReactionAdd', MESSAGE_REACTION_ADD: 'messageReactionAdd';
MESSAGE_REACTION_REMOVE: 'messageReactionRemove', MESSAGE_REACTION_REMOVE: 'messageReactionRemove';
MESSAGE_REACTION_REMOVE_ALL: 'messageReactionRemoveAll', MESSAGE_REACTION_REMOVE_ALL: 'messageReactionRemoveAll';
MESSAGE_REACTION_REMOVE_EMOJI: 'messageReactionRemoveEmoji', MESSAGE_REACTION_REMOVE_EMOJI: 'messageReactionRemoveEmoji';
THREAD_CREATE: 'threadCreate', THREAD_CREATE: 'threadCreate';
THREAD_DELETE: 'threadDelete', THREAD_DELETE: 'threadDelete';
THREAD_UPDATE: 'threadUpdate', THREAD_UPDATE: 'threadUpdate';
THREAD_LIST_SYNC: 'threadListSync', THREAD_LIST_SYNC: 'threadListSync';
THREAD_MEMBER_UPDATE: 'threadMemberUpdate', THREAD_MEMBER_UPDATE: 'threadMemberUpdate';
THREAD_MEMBERS_UPDATE: 'threadMembersUpdate', THREAD_MEMBERS_UPDATE: 'threadMembersUpdate';
USER_UPDATE: 'userUpdate', USER_UPDATE: 'userUpdate';
PRESENCE_UPDATE: 'presenceUpdate', PRESENCE_UPDATE: 'presenceUpdate';
VOICE_SERVER_UPDATE: 'voiceServerUpdate', VOICE_SERVER_UPDATE: 'voiceServerUpdate';
VOICE_STATE_UPDATE: 'voiceStateUpdate', VOICE_STATE_UPDATE: 'voiceStateUpdate';
TYPING_START: 'typingStart', TYPING_START: 'typingStart';
WEBHOOKS_UPDATE: 'webhookUpdate', WEBHOOKS_UPDATE: 'webhookUpdate';
INTERACTION_CREATE: 'interactionCreate', INTERACTION_CREATE: 'interactionCreate';
INTERACTION_SUCCESS: 'interactionSuccess', INTERACTION_SUCCESS: 'interactionSuccess';
INTERACTION_FAILED: 'interactionFailed', INTERACTION_FAILED: 'interactionFailed';
ERROR: 'error', ERROR: 'error';
WARN: 'warn', WARN: 'warn';
DEBUG: 'debug', DEBUG: 'debug';
CACHE_SWEEP: 'cacheSweep', CACHE_SWEEP: 'cacheSweep';
SHARD_DISCONNECT: 'shardDisconnect', SHARD_DISCONNECT: 'shardDisconnect';
SHARD_ERROR: 'shardError', SHARD_ERROR: 'shardError';
SHARD_RECONNECTING: 'shardReconnecting', SHARD_RECONNECTING: 'shardReconnecting';
SHARD_READY: 'shardReady', SHARD_READY: 'shardReady';
SHARD_RESUME: 'shardResume', SHARD_RESUME: 'shardResume';
INVALIDATED: 'invalidated', INVALIDATED: 'invalidated';
RAW: 'raw', RAW: 'raw';
STAGE_INSTANCE_CREATE: 'stageInstanceCreate', STAGE_INSTANCE_CREATE: 'stageInstanceCreate';
STAGE_INSTANCE_UPDATE: 'stageInstanceUpdate', STAGE_INSTANCE_UPDATE: 'stageInstanceUpdate';
STAGE_INSTANCE_DELETE: 'stageInstanceDelete', STAGE_INSTANCE_DELETE: 'stageInstanceDelete';
GUILD_STICKER_CREATE: 'stickerCreate', GUILD_STICKER_CREATE: 'stickerCreate';
GUILD_STICKER_DELETE: 'stickerDelete', GUILD_STICKER_DELETE: 'stickerDelete';
GUILD_STICKER_UPDATE: 'stickerUpdate', GUILD_STICKER_UPDATE: 'stickerUpdate';
GUILD_SCHEDULED_EVENT_CREATE: 'guildScheduledEventCreate', GUILD_SCHEDULED_EVENT_CREATE: 'guildScheduledEventCreate';
GUILD_SCHEDULED_EVENT_UPDATE: 'guildScheduledEventUpdate', GUILD_SCHEDULED_EVENT_UPDATE: 'guildScheduledEventUpdate';
GUILD_SCHEDULED_EVENT_DELETE: 'guildScheduledEventDelete', GUILD_SCHEDULED_EVENT_DELETE: 'guildScheduledEventDelete';
GUILD_SCHEDULED_EVENT_USER_ADD: 'guildScheduledEventUserAdd', GUILD_SCHEDULED_EVENT_USER_ADD: 'guildScheduledEventUserAdd';
GUILD_SCHEDULED_EVENT_USER_REMOVE: 'guildScheduledEventUserRemove', GUILD_SCHEDULED_EVENT_USER_REMOVE: 'guildScheduledEventUserRemove';
RELATIONSHIP_ADD: 'relationshipAdd', RELATIONSHIP_ADD: 'relationshipAdd';
RELATIONSHIP_REMOVE: 'relationshipRemove', RELATIONSHIP_REMOVE: 'relationshipRemove';
UNHANDLED_PACKET: 'unhandledPacket', UNHANDLED_PACKET: 'unhandledPacket';
} }
export interface ConstantsOpcodes { export interface ConstantsOpcodes {
@@ -4750,20 +4758,20 @@ export interface GuildAuditLogsEntryExtraField {
MESSAGE_UNPIN: { channel: GuildTextBasedChannel | { id: Snowflake }; messageId: Snowflake }; MESSAGE_UNPIN: { channel: GuildTextBasedChannel | { id: Snowflake }; messageId: Snowflake };
MEMBER_DISCONNECT: { count: number }; MEMBER_DISCONNECT: { count: number };
CHANNEL_OVERWRITE_CREATE: CHANNEL_OVERWRITE_CREATE:
| Role | Role
| GuildMember | GuildMember
| { id: Snowflake; name: string; type: OverwriteTypes.role } | { id: Snowflake; name: string; type: OverwriteTypes.role }
| { id: Snowflake; type: OverwriteTypes.member }; | { id: Snowflake; type: OverwriteTypes.member };
CHANNEL_OVERWRITE_UPDATE: CHANNEL_OVERWRITE_UPDATE:
| Role | Role
| GuildMember | GuildMember
| { id: Snowflake; name: string; type: OverwriteTypes.role } | { id: Snowflake; name: string; type: OverwriteTypes.role }
| { id: Snowflake; type: OverwriteTypes.member }; | { id: Snowflake; type: OverwriteTypes.member };
CHANNEL_OVERWRITE_DELETE: CHANNEL_OVERWRITE_DELETE:
| Role | Role
| GuildMember | GuildMember
| { id: Snowflake; name: string; type: OverwriteTypes.role } | { id: Snowflake; name: string; type: OverwriteTypes.role }
| { id: Snowflake; type: OverwriteTypes.member }; | { id: Snowflake; type: OverwriteTypes.member };
STAGE_INSTANCE_CREATE: StageChannel | { id: Snowflake }; STAGE_INSTANCE_CREATE: StageChannel | { id: Snowflake };
STAGE_INSTANCE_DELETE: StageChannel | { id: Snowflake }; STAGE_INSTANCE_DELETE: StageChannel | { id: Snowflake };
STAGE_INSTANCE_UPDATE: StageChannel | { id: Snowflake }; STAGE_INSTANCE_UPDATE: StageChannel | { id: Snowflake };
@@ -4776,8 +4784,8 @@ export interface GuildAuditLogsEntryTargetField<TActionType extends GuildAuditLo
INVITE: Invite; INVITE: Invite;
MESSAGE: TActionType extends 'MESSAGE_BULK_DELETE' ? Guild | { id: Snowflake } : User; MESSAGE: TActionType extends 'MESSAGE_BULK_DELETE' ? Guild | { id: Snowflake } : User;
INTEGRATION: Integration; INTEGRATION: Integration;
CHANNEL: NonThreadGuildBasedChannel | { id: Snowflake;[x: string]: unknown }; CHANNEL: NonThreadGuildBasedChannel | { id: Snowflake; [x: string]: unknown };
THREAD: ThreadChannel | { id: Snowflake;[x: string]: unknown }; THREAD: ThreadChannel | { id: Snowflake; [x: string]: unknown };
STAGE_INSTANCE: StageInstance; STAGE_INSTANCE: StageInstance;
STICKER: Sticker; STICKER: Sticker;
GUILD_SCHEDULED_EVENT: GuildScheduledEvent; GUILD_SCHEDULED_EVENT: GuildScheduledEvent;
@@ -4963,7 +4971,7 @@ export interface GuildScheduledEventCreateOptions {
export interface GuildScheduledEventEditOptions< export interface GuildScheduledEventEditOptions<
S extends GuildScheduledEventStatus, S extends GuildScheduledEventStatus,
T extends GuildScheduledEventSetStatusArg<S>, T extends GuildScheduledEventSetStatusArg<S>,
> extends Omit<Partial<GuildScheduledEventCreateOptions>, 'channel'> { > extends Omit<Partial<GuildScheduledEventCreateOptions>, 'channel'> {
channel?: GuildVoiceChannelResolvable | null; channel?: GuildVoiceChannelResolvable | null;
status?: T | number; status?: T | number;
} }
@@ -4980,14 +4988,14 @@ export type GuildScheduledEventEntityType = keyof typeof GuildScheduledEventEnti
export type GuildScheduledEventManagerFetchResult< export type GuildScheduledEventManagerFetchResult<
T extends GuildScheduledEventResolvable | FetchGuildScheduledEventOptions | FetchGuildScheduledEventsOptions, T extends GuildScheduledEventResolvable | FetchGuildScheduledEventOptions | FetchGuildScheduledEventsOptions,
> = T extends GuildScheduledEventResolvable | FetchGuildScheduledEventOptions > = T extends GuildScheduledEventResolvable | FetchGuildScheduledEventOptions
? GuildScheduledEvent ? GuildScheduledEvent
: Collection<Snowflake, GuildScheduledEvent>; : Collection<Snowflake, GuildScheduledEvent>;
export type GuildScheduledEventManagerFetchSubscribersResult<T extends FetchGuildScheduledEventSubscribersOptions> = export type GuildScheduledEventManagerFetchSubscribersResult<T extends FetchGuildScheduledEventSubscribersOptions> =
T extends { withMember: true } T extends { withMember: true }
? Collection<Snowflake, GuildScheduledEventUser<true>> ? Collection<Snowflake, GuildScheduledEventUser<true>>
: Collection<Snowflake, GuildScheduledEventUser<false>>; : Collection<Snowflake, GuildScheduledEventUser<false>>;
export type GuildScheduledEventPrivacyLevel = keyof typeof GuildScheduledEventPrivacyLevels; export type GuildScheduledEventPrivacyLevel = keyof typeof GuildScheduledEventPrivacyLevels;
@@ -5538,24 +5546,24 @@ export type Partialize<
N extends keyof T | null = null, N extends keyof T | null = null,
M extends keyof T | null = null, M extends keyof T | null = null,
E extends keyof T | '' = '', E extends keyof T | '' = '',
> = { > = {
readonly client: Client; readonly client: Client;
id: Snowflake; id: Snowflake;
partial: true; partial: true;
} & { } & {
[K in keyof Omit<T, 'client' | 'id' | 'partial' | E>]: K extends N ? null : K extends M ? T[K] | null : T[K]; [K in keyof Omit<T, 'client' | 'id' | 'partial' | E>]: K extends N ? null : K extends M ? T[K] | null : T[K];
}; };
export interface PartialDMChannel extends Partialize<DMChannel, null, null, 'lastMessageId'> { export interface PartialDMChannel extends Partialize<DMChannel, null, null, 'lastMessageId'> {
lastMessageId: undefined; lastMessageId: undefined;
} }
export interface PartialGuildMember extends Partialize<GuildMember, 'joinedAt' | 'joinedTimestamp'> { } export interface PartialGuildMember extends Partialize<GuildMember, 'joinedAt' | 'joinedTimestamp'> {}
export interface PartialMessage export interface PartialMessage
extends Partialize<Message, 'type' | 'system' | 'pinned' | 'tts', 'content' | 'cleanContent' | 'author'> { } extends Partialize<Message, 'type' | 'system' | 'pinned' | 'tts', 'content' | 'cleanContent' | 'author'> {}
export interface PartialMessageReaction extends Partialize<MessageReaction, 'count'> { } export interface PartialMessageReaction extends Partialize<MessageReaction, 'count'> {}
export interface PartialOverwriteData { export interface PartialOverwriteData {
id: Snowflake | number; id: Snowflake | number;
@@ -5570,7 +5578,7 @@ export interface PartialRoleData extends RoleData {
export type PartialTypes = 'USER' | 'CHANNEL' | 'GUILD_MEMBER' | 'MESSAGE' | 'REACTION' | 'GUILD_SCHEDULED_EVENT'; export type PartialTypes = 'USER' | 'CHANNEL' | 'GUILD_MEMBER' | 'MESSAGE' | 'REACTION' | 'GUILD_SCHEDULED_EVENT';
export interface PartialUser extends Partialize<User, 'username' | 'tag' | 'discriminator'> { } export interface PartialUser extends Partialize<User, 'username' | 'tag' | 'discriminator'> {}
export type PresenceStatusData = ClientPresenceStatus | 'invisible'; export type PresenceStatusData = ClientPresenceStatus | 'invisible';
@@ -5595,9 +5603,9 @@ export interface RawUserSettingsData {
allow_accessibility_detection?: boolean; allow_accessibility_detection?: boolean;
animate_emoji?: boolean; animate_emoji?: boolean;
animate_stickers?: number; animate_stickers?: number;
contact_sync_enabled:? boolean; contact_sync_enabled: ?boolean;
convert_emoticons?: boolean; convert_emoticons?: boolean;
custom_status?: { text?: string, expires_at?: string | null, emoji_name?: string, emoji_id?: Snowflake | null }; custom_status?: { text?: string; expires_at?: string | null; emoji_name?: string; emoji_id?: Snowflake | null };
default_guilds_restricted?: boolean; default_guilds_restricted?: boolean;
detect_platform_accounts?: boolean; detect_platform_accounts?: boolean;
developer_mode?: boolean; developer_mode?: boolean;
@@ -5605,10 +5613,10 @@ export interface RawUserSettingsData {
enable_tts_command?: boolean; enable_tts_command?: boolean;
explicit_content_filter?: DMScanLevel; explicit_content_filter?: DMScanLevel;
friend_discovery_flags?: number; friend_discovery_flags?: number;
friend_source_flags?: { all?: boolean, mutual_friends?: boolean, mututal_guilds?: boolean }; friend_source_flags?: { all?: boolean; mutual_friends?: boolean; mututal_guilds?: boolean };
gif_auto_play?: boolean; gif_auto_play?: boolean;
guild_folders?: Array<{ id?: Snowflake, guild_ids?: Array<Snowflake>, name?: string }>; guild_folders?: Array<{ id?: Snowflake; guild_ids?: Array<Snowflake>; name?: string }>;
guild_positions?: Array; guild_positions?: Array<T>;
inline_attachment_media?: boolean; inline_attachment_media?: boolean;
inline_embed_media?: boolean; inline_embed_media?: boolean;
locale?: string; locale?: string;
@@ -5792,8 +5800,8 @@ export interface SweeperDefinitions {
export type SweeperOptions = { export type SweeperOptions = {
[K in keyof SweeperDefinitions]?: SweeperDefinitions[K][2] extends true [K in keyof SweeperDefinitions]?: SweeperDefinitions[K][2] extends true
? SweepOptions<SweeperDefinitions[K][0], SweeperDefinitions[K][1]> | LifetimeSweepOptions ? SweepOptions<SweeperDefinitions[K][0], SweeperDefinitions[K][1]> | LifetimeSweepOptions
: SweepOptions<SweeperDefinitions[K][0], SweeperDefinitions[K][1]>; : SweepOptions<SweeperDefinitions[K][0], SweeperDefinitions[K][1]>;
}; };
export interface LimitedCollectionOptions<K, V> { export interface LimitedCollectionOptions<K, V> {