From 039dd34cf26a8124c5a8d566b932ae52bdd4ca4e Mon Sep 17 00:00:00 2001 From: Elysia <71698422+aiko-chan-ai@users.noreply.github.com> Date: Sun, 14 Jan 2024 12:33:11 +0700 Subject: [PATCH] Update --- Document/FetchGuildMember.md | 187 -- Document/Message.md | 40 +- Document/RichPresence.md | 61 +- Document/SlashCommand.md | 7 - Document/User.md | 170 -- Document/VoiceCall.md | 628 ----- README.md | 8 +- package.json | 8 +- src/client/Client.js | 565 +---- src/client/websocket/handlers/READY.js | 185 +- .../handlers/USER_GUILD_SETTINGS_UPDATE.js | 12 - .../handlers/USER_SETTINGS_UPDATE.js | 9 - src/client/websocket/handlers/index.js | 33 +- src/errors/Messages.js | 92 +- src/index.js | 53 +- src/managers/ClientUserSettingManager.js | 490 ---- src/managers/DeveloperPortalManager.js | 104 - src/managers/GuildFolderManager.js | 24 - src/managers/GuildSettingManager.js | 148 -- src/managers/SessionManager.js | 57 - src/structures/DeveloperPortalApplication.js | 520 ----- src/structures/GuildFolder.js | 75 - src/structures/Session.js | 81 - src/util/RemoteAuth.js | 597 ++--- src/util/Util.js | 259 +-- typings/enums.d.ts | 81 +- typings/index.d.ts | 2053 ++++++----------- 27 files changed, 1250 insertions(+), 5297 deletions(-) delete mode 100644 Document/FetchGuildMember.md delete mode 100644 Document/User.md delete mode 100644 Document/VoiceCall.md delete mode 100644 src/client/websocket/handlers/USER_GUILD_SETTINGS_UPDATE.js delete mode 100644 src/client/websocket/handlers/USER_SETTINGS_UPDATE.js delete mode 100644 src/managers/ClientUserSettingManager.js delete mode 100644 src/managers/DeveloperPortalManager.js delete mode 100644 src/managers/GuildFolderManager.js delete mode 100644 src/managers/GuildSettingManager.js delete mode 100644 src/managers/SessionManager.js delete mode 100644 src/structures/DeveloperPortalApplication.js delete mode 100644 src/structures/GuildFolder.js delete mode 100644 src/structures/Session.js diff --git a/Document/FetchGuildMember.md b/Document/FetchGuildMember.md deleted file mode 100644 index fa8b924..00000000 --- a/Document/FetchGuildMember.md +++ /dev/null @@ -1,187 +0,0 @@ -### Credit: Discord-S.C.U.M -- Link: [Here](https://github.com/Merubokkusu/Discord-S.C.U.M/blob/master/docs/using/fetchingGuildMembers.md#Algorithm) - -# Fetching Guild Members - -### Assume you don't have one of the following permissions -> `ADMINISTRATOR`, `KICK_MEMBERS`, `BAN_MEMBERS`, `MANAGE_ROLES` - -Alright so this really needs a page of its own because it's special. There's no actual api endpoint to get the guild members, so instead you have 2 options: -1) fetch the member list sidebar (guild.members.fetchMemberList, which uses opcode 14) - - when to use: if you can see any categories/channels in the guild - - pro: fast - - con: for large servers (```guild.memberCount > 1000```), only not-offline members are fetched - -2) search for members by query (guild.members.fetchBruteforce, which uses opcode 8) - - when to use: if you cannot see any categories/channels in the guild - - pro: can potentially get the entire member list, can scrape members from multiple guilds at the same time - - con: slow af (speed is dependent on brute forcer optimizations) - -____________________________________ -# Links/Table of Contents -- [fetch the member list sidebar (faster, but less members)](#fetch-the-member-list-sidebar) - - [Algorithm](#Algorithm) - - [How many members can I fetch?](#how-many-members-can-i-fetch) - - [Examples](#Examples) - - [Efficiency & Effectiveness](#efficiency--effectiveness) - - [POC: fetching the memberlist backwards](#fetching-the-member-list-backwards) -- [search for members by query (slower, but more members)](#search-for-members-by-query) - - [Usage](#Usage) - - [Algorithm](#Algorithm-1) - - [How many members can I fetch?](#how-many-members-can-i-fetch-1) -___________________________________ -## Fetch the Member List Sidebar -#### Algorithm -1) load guild data (send an op14 with range [0,99]). If the guild is unavailable, discord will send over a GUILD_CREATE event. -2) subscribe to a list of ranges in member list sidebar. -3) after a GUILD_MEMBER_LIST_UPDATE is received, update the saved member list data and subscribe to a new list of ranges - -note: -- you don't have to wait for a GUILD_MEMBER_LIST_UPDATE event to send the next list of member ranges -- there're 2 methods to fetch the member list: - - overlap. Ranges subscribed to (in order) are: - ``` - [[0,99], [100,199]] - [[0,99], [100,199], [200,299]] - [[0,99], [200,299], [300,399]] - ... - ``` - - nonoverlap. Ranges subscribed to (in order) are: - ``` - [[0,99], [100,199]] - [[0,99], [200,299], [300,399]] - [[0,99], [400,499], [500,599]] - ... - ``` -- more info: https://arandomnewaccount.gitlab.io/discord-unofficial-docs/lazy_guilds.html - -#### How many members can I fetch? -Even though it's not yet known how discord calculates this, you can still come up with a "ground truth" number. The steps are as follows: -1) open your browser's dev tools (chrome dev tools is a favorite) -2) click on the network tab and make sure you can see websocket connections -3) go to a guild and scroll all the way down on the member list -4) see what are the ranges of the last gateway request your client sends (the # of fetchable members is somewhere in these ranges) - -#### Examples -all examples shown use the "overlap" method - -```js -const guild = client.guilds.cache.get('id'); -const channel = guild.channels.cache.get('id'); -// Overlap (slow) -for (let index = 0; index <= guild.memberCount; index += 100) { - await guild.members.fetchMemberList(channel, index, index !== 100).catch(() => {}); - await client.sleep(500); -} -// Non-overlap (fast) -for (let index = 0; index <= guild.memberCount; index += 200) { - await guild.members.fetchMemberList(channel, index == 0 ? 100 : index, index !== 100).catch(() => {}); - await client.sleep(500); -} -console.log(guild.members.cache.size); // will print the number of members in the guild -``` - -It's possible that fetchMembers doesn't fetch all not-offline members due to rate limiting. Don't worry if this happens, you can start fetching members from any index. -```js -const guild = client.guilds.cache.get('id'); -const channel = guild.channels.cache.get('id'); -// Fetch member range 5000-5099 -await guild.members.fetchMemberList(channel, 5000); -``` - -#### Efficiency & Effectiveness - -| | overlap                      | no overlap | -|------|---------|------------| -| 2.1k |![a](https://github.com/Merubokkusu/Discord-S.C.U.M/raw/master/docs/using/memberFetchingStats/2100a.jpg) |![c](https://github.com/Merubokkusu/Discord-S.C.U.M/raw/master/docs/using/memberFetchingStats/2100b.jpg) | -| 128k |![b](https://github.com/Merubokkusu/Discord-S.C.U.M/raw/master/docs/using/memberFetchingStats/128ka.jpg) |![d](https://github.com/Merubokkusu/Discord-S.C.U.M/raw/master/docs/using/memberFetchingStats/128kb.jpg) | - -As you can see, the "no overlap" method fetches 200 members/second while the "overlap" method fetches 100 members/second. However, "no overlap" is also a lot less effective. After doing a few more tests with both methods ("overlap" and "no overlap"), "no overlap" shows a lot less consistency/reliability than "overlap". - - -#### Fetching the member list backwards -(and in pretty much any "style" you want) -So, this is more proof-of-concept, but here's a short explanation. -Suppose you're in a guild with 1000 members and want to fetch the member list backwards (I dunno...more undetectable since noone fetches it backwards? lol). - Since discum requests members in 200-member chunks, you'll either have to request for the following range groups (safer): - ``` - [[0,99],[800,899],[900,999]] #target start: 800 - [[0,99],[700,799],[800,899]] #target start: 700 - [[0,99],[600,699],[700,799]] #target start: 600 - [[0,99],[500,599],[600,699]] #target start: 500 - [[0,99],[400,499],[500,599]] #target start: 400 - [[0,99],[300,399],[400,499]] #target start: 300 - [[0,99],[200,299],[300,399]] #target start: 200 - [[0,99],[100,199],[200,299]] #target start: 100 - [[0,99],[100,199]] #target start: 0 - ``` - or the following range groups (faster): - ``` - [[0,99],[800,899],[900,999]] #target start: 800 - [[0,99],[600,699],[700,799]] #target start: 600 - [[0,99],[400,499],[500,599]] #target start: 400 - [[0,99],[200,299],[300,399]] #target start: 200 - [[0,99],[100,199]] #target start: 0 - ``` - The first one looks like an overlap method while the second looks like a no-overlap method. However, since we're fetching the memberlist backwards, we cannot - use 100 and 200 for the methods. Instead, we need a list of multipliers (method) and a startIndex. -____________________________________ -## Search for Members by Query -#### Usage -1) run the function: - ```js - guild.members.fetchBruteforce({ - delay: 500, - depth: 1, // ['a', 'b', 'c', 'd', ...] or ['aa', 'ab', 'ac', 'ad', ...] if depth is 2, ... - }) - ``` - A wait time of at least 0.5 is needed to prevent the brute forcer from rate limiting too often. In the event that the brute forcer does get rate limited, some time will be lost reconnecting. -#### Algorithm -for simplicity, assume that the list of characters to search for is ['a', 'b', 'c', 'd'] -1) query for up to 100 members in guild who have a nickname/username starting with 'a' -2) on a GUILD_MEMBERS_CHUNK event: - - if there are 100 results: - - add on the 2nd character of the last result. For example, if the results are - ``` - aaaaaaaaaaaa - aaadfd3fgdftjh - ... - Acaddd - ``` - , - the next query will be 'ac'. Note: searches are case-insensitive and consecutive spaces are treated like single spaces. - - if there are less than 100 results: - - replace the last index of the query with the next option in the list - -This algorithm can definitely be made a lot better so have at it. The brute forcer example is just there to help you get started. - -#### How many members can I fetch? -- a limit is posed if many users have the same nickname & username (but different discriminators). Only the 1st 100 members will be able to be fetched. There's no known way to include the discriminator # in the search. -- also, in order to query users with fancy characters in their username/nickname, the op8 brute forcer needs to be slowed down (cause, more characters to search) - -#### Testing !!! - -- Example -```js -const Discord = require('discord.js-selfbot-v13'); -const client = new Discord.Client(); -client.on('debug', console.log) -client.on('ready', () => { - console.log('Logged in as', client.user.tag); - const guild = client.guilds.cache.get('662267976984297473'); // Midjourney - 13M members - guild.members.fetchBruteforce({ - depth: 2, // 2 levels of recursion - delay: 500, // 500ms delay between each request - }); - setInterval(() => { - console.log('Fetching members...', guild.members.cache.size); - }, 1000); -}); -client.login('token'); -``` - -- 2000 years later... - - - -`138k/13.8M (1%)` members fetched in `~30 mins` (with a delay of 500ms) :skull: \ No newline at end of file diff --git a/Document/Message.md b/Document/Message.md index 718a945..c2f909c 100644 --- a/Document/Message.md +++ b/Document/Message.md @@ -17,40 +17,10 @@ ## Interaction
-Button Click - -```js -await Button.click(Message); -// -await message.clickButton(buttonID); -// -await message.clickButton(); // first button -// -await message.clickButton({ row: 0, col: 0}) -``` -
-
-Message Select Menu - -```js -await MessageSelectMenu.select(Message, options); // (v1) -// value: ['value1', 'value2' , ...] -await message.selectMenu(menuID, options) // If message has >= 2 menu -await message.selectMenu(options) // If message has 1 menu -``` -
-
Slash Command ### [Click here](https://github.com/aiko-chan-ai/discord.js-selfbot-v13/blob/main/Document/SlashCommand.md) -
-
-Message Context Command - -```js -await message.contextMenu(botID, commandName); -```
@@ -64,13 +34,7 @@ await message.contextMenu(botID, commandName); Code: ```js const Discord = require('discord.js-selfbot-v13'); -// Selfhost WebEmbed: https://github.com/aiko-chan-ai/WebEmbed -const w = new Discord.WebEmbed({ - shorten: true, - hidden: false, // if you send this embed with MessagePayload.options.embeds, it must set to false - baseURL: '', // if you want self-host API, else skip :v - shortenAPI: '', // if you want Custom shortenAPI (Method: Get, response: Text => URL), else skip :v -}) +const w = new Discord.WebEmbed() .setAuthor({ name: 'hello', url: 'https://google.com' }) .setColor('RED') .setDescription('description uh') @@ -83,7 +47,7 @@ const w = new Discord.WebEmbed({ .setVideo( 'https://cdn.discordapp.com/attachments/877060758092021801/957691816143097936/The_Quintessential_Quintuplets_And_Rick_Astley_Autotune_Remix.mp4', ); -message.channel.send({ content: `Hello world`, embeds: [w] }) // Patched :) +message.channel.send({ content: `Hello world ${Discord.WebEmbed.hiddenEmbed} ${w}` }); ``` ### Features & Issues diff --git a/Document/RichPresence.md b/Document/RichPresence.md index 36a81bc..9c29b64 100644 --- a/Document/RichPresence.md +++ b/Document/RichPresence.md @@ -1,10 +1,3 @@ -## Setup -```js -const client = new Client({ - syncStatus: false, -}); -``` - ## Custom Status and RPC Custom Status @@ -68,57 +61,7 @@ client.user.setActivity(r); > Tutorial: -## Method 1: - -+ Step 1: Send image to Discord - - - -+ Step 2: Get Image URL - - - -```sh -Demo URL: https://cdn.discordapp.com/attachments/820557032016969751/991172011483218010/unknown.png -``` - -+ Step 3: Replace `https://cdn.discordapp.com/` or `https://media.discordapp.net/` with `mp:` - -```diff -- https://cdn.discordapp.com/attachments/820557032016969751/991172011483218010/unknown.png - -- https://media.discordapp.net/attachments/820557032016969751/991172011483218010/unknown.png - -+ mp:attachments/820557032016969751/991172011483218010/unknown.png - -``` - -+ Step 4: - -```js -const Discord = require('discord.js-selfbot-v13'); -const r = new Discord.RichPresence() - .setApplicationId('817229550684471297') - .setType('PLAYING') - .setURL('https://youtube.com/watch?v=dQw4w9WgXcQ') - .setState('State') - .setName('Name') - .setDetails('Details') - .setParty({ - max: 9, - current: 1, - id: Discord.getUUID(), - }) - .setStartTimestamp(Date.now()) - .setAssetsLargeImage('mp:attachments/820557032016969751/991172011483218010/unknown.png') - .setAssetsLargeText('Youtube') - .setAssetsSmallImage('895316294222635008') - .setAssetsSmallText('Bot') - .addButton('name', 'https://link.com/') -client.user.setActivity(r); -``` - -## Method 2: (Discord URL, 2.3.78+) +## Method 1: (Discord URL, v2.3.78+) ```js const Discord = require('discord.js-selfbot-v13'); @@ -145,7 +88,7 @@ client.user.setActivity(r); -## Method 3 (Custom URL, 2.3.78+) +## Method 2 (Custom URL, 2.3.78+) ```js const Discord = require('discord.js-selfbot-v13'); diff --git a/Document/SlashCommand.md b/Document/SlashCommand.md index 5bd6d77..b7ca1b7 100644 --- a/Document/SlashCommand.md +++ b/Document/SlashCommand.md @@ -42,10 +42,3 @@ await message.channel.sendSlash('718642000898818048', 'sauce', a) ### Result ![image](https://user-images.githubusercontent.com/71698422/173347075-5c8a1347-3845-489e-956b-63975911b6e0.png) - -# Events - -- [interactionCreate](https://discordjs-self-v13.netlify.app/#/docs/docs/main/class/Client?scrollTo=e-interactionCreate) -- [interactionFailure](https://discordjs-self-v13.netlify.app/#/docs/docs/main/class/Client?scrollTo=e-interactionFailure) -- [interactionSuccess](https://discordjs-self-v13.netlify.app/#/docs/docs/main/class/Client?scrollTo=e-interactionSuccess) -- [interactionModalCreate](https://discordjs-self-v13.netlify.app/#/docs/docs/main/class/Client?scrollTo=e-interactionModalCreate) diff --git a/Document/User.md b/Document/User.md deleted file mode 100644 index 7da9279..00000000 --- a/Document/User.md +++ /dev/null @@ -1,170 +0,0 @@ -# Quick Links: -- [Setting](https://github.com/aiko-chan-ai/discord.js-selfbot-v13/blob/main/Document/User.md#user-settings) -- [User Info](https://github.com/aiko-chan-ai/discord.js-selfbot-v13/blob/main/Document/User.md#discord-user-info) -- [Relationship](https://github.com/aiko-chan-ai/discord.js-selfbot-v13/blob/main/Document/User.md#discord-user-friend--blocked) -- [Other](https://github.com/aiko-chan-ai/discord.js-selfbot-v13/blob/main/Document/User.md#user--clientuser-method) - -## User Settings -
-Click to show - -```js -client.setting // Return Data Setting User; -client.settings.setDisplayCompactMode(true | false); // Message Compact Mode -client.settings.setTheme('dark' | 'light'); // Discord App theme -client.settings.setLocale(value); // Set Language - /** - * * Locale Setting, must be one of: - * * `DANISH` - * * `GERMAN` - * * `ENGLISH_UK` - * * `ENGLISH_US` - * * `SPANISH` - * * `FRENCH` - * * `CROATIAN` - * * `ITALIAN` - * * `LITHUANIAN` - * * `HUNGARIAN` - * * `DUTCH` - * * `NORWEGIAN` - * * `POLISH` - * * `BRAZILIAN_PORTUGUESE` - * * `ROMANIA_ROMANIAN` - * * `FINNISH` - * * `SWEDISH` - * * `VIETNAMESE` - * * `TURKISH` - * * `CZECH` - * * `GREEK` - * * `BULGARIAN` - * * `RUSSIAN` - * * `UKRAINIAN` - * * `HINDI` - * * `THAI` - * * `CHINA_CHINESE` - * * `JAPANESE` - * * `TAIWAN_CHINESE` - * * `KOREAN` - */ -// Setting Status -client.settings.setCustomStatus({ - status: 'online', // 'online' | 'idle' | 'dnd' | 'invisible' | null - text: 'Hello world', // String | null - emoji: '🎮', // UnicodeEmoji | DiscordEmoji | null - expires: null, // Date.now() + 1 * 3600 * 1000 <= 1h to ms -}); -// => Clear -client.settings.setCustomStatus(); -``` - -
- -## Discord User Info -
-Click to show - -Code: -```js -GuildMember.user.getProfile(); -// or -User.getProfile(); -``` -Response -```js -User { - id: '721746046543331449', - bot: false, - system: false, - flags: UserFlagsBitField { bitfield: 256 }, - connectedAccounts: [], - premiumSince: 1623357181151, - premiumGuildSince: 0, - bio: null, - mutualGuilds: Collection(3) [Map] { - '906765260017516605' => { id: '906765260017516605', nick: null }, - '809133733591384155' => { id: '809133733591384155', nick: 'uwu' }, - '926065180788531261' => { id: '926065180788531261', nick: 'shiro' } - }, - username: 'Shiraori', - discriminator: '1782', - avatar: 'f9ba7fb35b223e5f1a12eb910faa40c2', - banner: undefined, - accentColor: undefined -} -``` -
- -## Discord User Friend / Blocked -
-Click to show - -Code: -```js -// You can use client.relationships to manage your friends and blocked users. -GuildMember.user.setFriend(); -User.unFriend(); -Message.member.user.sendFriendRequest(); -// or -GuildMember.user.setBlock(); -User.unBlock(); -``` -Response -```js -User { - id: '721746046543331449', - bot: false, - system: false, - flags: UserFlagsBitField { bitfield: 256 }, - note: null, - connectedAccounts: [], - premiumSince: 1623357181151, - premiumGuildSince: 0, - bio: null, - mutualGuilds: Collection(3) [Map] { - '906765260017516605' => { id: '906765260017516605', nick: null }, - '809133733591384155' => { id: '809133733591384155', nick: 'uwu' }, - '926065180788531261' => { id: '926065180788531261', nick: 'shiro' } - }, - username: 'Shiraori', - discriminator: '1782', - avatar: 'f9ba7fb35b223e5f1a12eb910faa40c2', - banner: undefined, - accentColor: undefined -} -``` -
- -## User & ClientUser Method -
-Click to show - -```js -// HypeSquad -await client.user.setHypeSquad('HOUSE_BRAVERY'); -await client.user.setHypeSquad('HOUSE_BRILLIANCE'); -await client.user.setHypeSquad('HOUSE_BALANCE'); -await client.user.setHypeSquad('LEAVE'); -// Set Note to User -await user.setNote('Hello World'); -// Set Username -await client.user.setUsername('new username', 'password'); -// Set Accent Color -await client.user.setAccentColor('RED'); // set color same as Embed.setColor() -// Set Banner -await client.user.setBanner('image file / image url'); // same as setAvatar & Require Nitro level 2 -// Set Discord Tag -await client.user.setDiscriminator('1234', 'password'); // #1234 & Require Nitro -// Set About me -await client.user.setAboutMe('Hello World'); -// Set Email -await client.user.setEmail('aiko.dev@mail.nezukobot.vn', 'password'); // It is clone email =)) -// Change Password -await client.user.setPassword('old password', 'new password'); -// Disable Account -await client.user.disableAccount('password'); -// Delete Account [WARNING] Cannot be changed once used! -await client.user.deleteAccount('password'); -// Redeem Nitro -await client.redeemNitro('code') -``` -
diff --git a/Document/VoiceCall.md b/Document/VoiceCall.md deleted file mode 100644 index 8f02383..00000000 --- a/Document/VoiceCall.md +++ /dev/null @@ -1,628 +0,0 @@ -# Setup -- Before you use it, properly initialize the module (`@discordjs/voice` patch) - -```js -new Client({ - patchVoice: true, -}) -``` - -# Usage: Call DM / Group DM - -```js -const dmChannel = client.channels.cache.get('id'); -/* or -const dmChannel = User.dmChannel || await User.createDM(); -*/ -const connection = await dmChannel.call(); -/* Return @discordjs/voice VoiceConnection */ -``` - -# Play Music using `play-dl` - -```js -const play = require('play-dl'); -const { - createAudioPlayer, - createAudioResource, - NoSubscriberBehavior, -} = require('@discordjs/voice'); -const channel = (await (message.member.user.dmChannel || message.member.user.createDM())); -const connection = channel.voiceConnection || await channel.call(); -let stream; -if (!args[0]) { - return message.channel.send('Enter something to search for.'); -} else if (args[0].startsWith('https://www.youtube.com/watch?v=')) { - stream = await play.stream(args.join(' ')); -} else { - const yt_info = await play.search(args, { - limit: 1 - }); - stream = await play.stream(yt_info[0].url); -} -const resource = createAudioResource(stream.stream, { - inputType: stream.type, - inlineVolume: true, -}); -resource.volume.setVolume(0.25); -const player = createAudioPlayer({ - behaviors: { - noSubscriber: NoSubscriberBehavior.Play, - }, -}); -let i = setInterval(() => { - const m = channel.voiceUsers.get(message.author.id); - if (m) { - player.play(resource); - connection.subscribe(player); - clearInterval(i); - } - else console.log('waiting for voice connection'); -}, 250); -``` - -# Play Music with module (support Play, Pause, Search, Skip, Previous, Volume, Loop) - -```js -/* Copyright aiko-chan-ai @2022. All rights reserved. */ -const DjsVoice = require('@discordjs/voice'); -const Discord = require('discord.js-selfbot-v13'); -const playDL = require('play-dl'); -const EventEmitter = require('events'); -const Event = { - READY: 'ready', - NO_SEARCH_RESULT: 'searchNoResult', - SEARCH_RESULT: 'searchResult', - PLAY_SONG: 'playSong', - ADD_SONG: 'addSong', - ADD_PLAYLIST: 'addPlaylist', - LEAVE_VC: 'disconnect', - FINISH: 'finish', - EMPTY: 'empty', - ERROR: 'error', -} -class Stack { - constructor() { - this.data = []; - } - - push(item) { - return this.data.push(item); - } - - pop() { - return this.data.pop(); - } - - peek() { - return this.data[this.length - 1]; - } - - get length() { - return this.data.length; - } - - isEmpty() { - return this.length === 0; - } - - reset () { - this.data = []; - return this; - } -} - -class Queue { - constructor() { - this.data = []; - } - - enqueue(item) { - return this.data.unshift(item); - } - - dequeue() { - return this.data.pop(); - } - - peek() { - return this.data[this.length - 1]; - } - - get length() { - return this.data.length; - } - - isEmpty() { - return this.data.length === 0; - } - - reset() { - this.data = []; - return this; - } -} -class Player extends EventEmitter { - constructor(client, options = {}) { - super(); - if (!client || !client instanceof Discord.Client) throw new Error('Invalid Discord Client (Selfbot)'); - Object.defineProperty(this, 'client', { value: client }); - this._playDl = playDL; - this._queue = new Queue(); - this._previousSongs = new Stack(); - this.song = null; - this.guild = undefined; - this.channel = undefined; - this._currentResourceAudio = undefined; - this._currentTime = 0; - this._playingTime = null; - this.isPlaying = false; - this.volume = 100; - this.loopMode = 0; - this.message = undefined; - this._timeoutEmpty = undefined; - this._player = DjsVoice.createAudioPlayer({ - behaviors: { - noSubscriber: DjsVoice.NoSubscriberBehavior.Play, - }, - }); - this._playerEvent(); - this._validateOptions(options); - this._discordEvent(); - this._privateEvent(); - } - get currentTime() { - return this._currentTime || Date.now() - this._playingTime; - } - get currentConnection() { - return DjsVoice.getVoiceConnection(this.guild?.id || null); - } - get queue() { - return this._queue.data; - } - get previousSongs() { - return this._previousSongs.data; - } - authorization() { - this._playDl.authorization(); - } - _validateOptions(options) { - if (typeof options !== 'object') throw new Error(`Invalid options type (Required: object, got: ${typeof options})`); - this.options = { - directLink: true, - joinNewVoiceChannel: true, - waitingUserToPlayInDMs: true, - nsfw: false, - leaveOnEmpty: true, - leaveOnFinish: true, - leaveOnStop: true, - savePreviousSongs: true, - emptyCooldown: 10_000, - } - if (typeof options.directLink === 'boolean') { - this.options.directLink = options.directLink; - } - if (typeof options.joinNewVoiceChannel === 'boolean') { - this.options.joinNewVoiceChannel = options.joinNewVoiceChannel; - } - if (typeof options.waitingUserToPlayInDMs === 'boolean') { - this.options.waitingUserToPlayInDMs = options.waitingUserToPlayInDMs; - } - if (typeof options.nsfw === 'boolean') { - this.options.nsfw = options.nsfw; - } - if (typeof options.leaveOnEmpty === 'boolean') { - if (typeof options.emptyCooldown === 'number') { - this.options.leaveOnEmpty = options.leaveOnEmpty; - this.options.emptyCooldown = options.emptyCooldown; - } else { - this.options.leaveOnEmpty = false; - } - } - if (typeof options.leaveOnFinish === 'boolean') { - this.options.leaveOnFinish = options.leaveOnFinish; - } - if (typeof options.leaveOnStop === 'boolean') { - this.options.leaveOnStop = options.leaveOnStop; - } - if (typeof options.savePreviousSongs === 'boolean') { - this.options.savePreviousSongs = options.savePreviousSongs; - } - } - async play(options = {}) { - const { - message, - channel, - query, - } = options; - if (!(message instanceof Discord.Message)) throw new Error(`Invalid message type (Required: Message, got: ${typeof message})`); - if (channel && - ( - channel instanceof Discord.DMChannel || - channel instanceof Discord.PartialGroupDMChannel || - channel instanceof Discord.VoiceChannel || - channel instanceof Discord.StageChannel - ) - ) { - let checkChangeVC = false; - if (!this.channel) this.channel = channel; - else { - if (this.options.joinNewVoiceChannel) { - if (this.channel.id !== channel.id) checkChangeVC = true; - this.channel = channel; - } - } - this.guild = channel.guild; - this.message = message; - if (typeof query !== 'string') throw new Error(`Invalid query type (Required: string, got: ${typeof query})`); - const result = await this.search(message, query); - if (result.length < 1) { - throw new Error('No search result with the given query: ' + query); - } else { - for (let i = 0; i < result.length; i++) { - this._queue.enqueue(result[i]); - } - if (!this.isPlaying) { - this._skip(checkChangeVC); - } else if (!result[0].playlist) { - this.emit(Event.ADD_SONG, result[0]); - } - } - } else { - throw new Error(`Invalid channel. Make sure the channel is a DMChannel | PartialGroupDMChannel | VoiceChannel | StageChannel.`); - } - } - async search(message, query, limit = 1) { - if (!(message instanceof Discord.Message)) throw new Error(`Invalid message type (Required: Message, got: ${typeof message})`); - if (typeof query !== 'string') throw new Error(`Invalid query type (Required: string, got: ${typeof query})`); - if (typeof limit !== 'number') throw new Error(`Invalid limit type (Required: number, got: ${typeof limit})`); - if (limit < 1) { - limit = 1; - process.emitWarning(`Invalid limit value (Required: 1 or more, got: ${limit})`); - }; - if (limit > 10) { - limit = 10; - process.emitWarning(`Invalid limit value (Required: 10 or less, got: ${limit})`); - }; - if (/^(https?\:\/\/)?(www\.youtube\.com|youtu\.be)\/.+$/.test(query)) { - const validateData = this._playDl.yt_validate(query); - if (validateData == 'video') { - const result = await this._playDl.video_info(query); - return [result.video_details]; - } else if (validateData == 'playlist') { - const result = await this._playDl.playlist_info(query); - this.emit(Event.ADD_PLAYLIST, result); - const allVideo = await result.all_videos(); - return allVideo.map(video => { - Object.defineProperty(video, 'playlist', { value: result }); - return video; - }); - } else { - return this.emit(Event.ERROR, new Error('Invalid YouTube URL: ' + query)); - } - } else { - const result = await this._playDl.search(query, { - limit, - unblurNSFWThumbnails: this.options.nsfw, - }); - if (result.length < 1) { - this.emit(Event.NO_SEARCH_RESULT, message, query, limit); - return []; - } else { - this.emit(Event.SEARCH_RESULT, message, result, query, limit); - return result; - } - } - } - setLoopMode(mode) { - if ([0, 1, 2].includes(mode)) { - this._loopMode = mode; - } else { - throw new Error(`Invalid mode value (Required: 0 [No loop], 1 [Loop song], 2 [Loop queue], got: ${mode})`); - } - } - async createStream(url) { - const stream = await this._playDl.stream(url); - const resource = DjsVoice.createAudioResource(stream.stream, { - inputType: stream.type, - inlineVolume: true, - }); - this._currentResourceAudio = resource; - this.setVolume(this.volume); - } - _play() { - this._player.play(this._currentResourceAudio); - } - setVolume(volume) { - if (!this._currentResourceAudio) throw new Error('No current resource audio'); - if (typeof volume !== 'number') throw new Error(`Invalid volume type (Required: number, got: ${typeof volume})`); - if (volume < 0) { - volume = 0; - process.emitWarning(`Invalid volume value (Required: 0 or more, got: ${volume})`); - } else if (volume > 100) { - volume = 100; - process.emitWarning(`Invalid volume value (Required: 100 or less, got: ${volume})`); - } - this._currentResourceAudio.volume.setVolume((volume / 100).toFixed(2)); - this.volume = (volume / 100).toFixed(2) * 100; - return (volume / 100).toFixed(2) * 100; - } - pause() { - if (!this._currentResourceAudio) throw new Error('No current resource audio'); - this._player.pause(); - } - resume() { - if (!this._currentResourceAudio) throw new Error('No current resource audio'); - this._player.unpause(); - } - stop() { - if (!this._currentResourceAudio) throw new Error('No current resource audio'); - this._stop(false, this.options.leaveOnStop); - } - _reset(){ - this._currentTime = 0; - this._currentResourceAudio = null; - this._playingTime = 0; - this.isPlaying = false; - this._player.stop(); - } - _stop(finish = false, force = false) { - if (!this._currentResourceAudio) return; - this._queue.reset(); - this._previousSongs.reset(); - this._timeoutEmpty = undefined; - this._reset(); - if (force || finish && this.options.leaveOnFinish) this.currentConnection?.destroy(); - this.channel = null; - this.guild = null; - this.song = null; - this.volume = 100; - this.loopMode = 0; - this.message = null; - } - skip() { - this._skip(); - } - async _skip(checkChangeVC = false) { - if (!this._queue.length) throw new Error('No song in the queue'); - const currentSong = this.song; - if (this.loopMode == 0) { - if (this.options.savePreviousSongs) this._previousSongs.push(currentSong); - const nextSong = this._queue.dequeue(); - this.song = nextSong; - } else if (this.loopMode == 1) { - this.song = currentSong; - } else if (this.loopMode == 2) { - this._queue.enqueue(currentSong); - const nextSong = this._queue.dequeue(); - this.song = nextSong; - } - await this.createStream(this.song.url); - await this.joinVC(checkChangeVC); - this.emit(Event.PLAY_SONG, this.song); - if (!this.guild?.id) await this._awaitDM(); - this._play(); - this._playingTime = Date.now(); - } - async previous() { - if (!this._previousSongs.length) throw new Error('No previous songs'); - const currentSong = this.song; - // add to queue - this._queue.enqueue(currentSong); - const previousSong = this._previousSongs.pop(); - this.song = previousSong; - await this.createStream(this.song.url); - await this.joinVC(); - this._play(); - this.emit(Event.PLAY_SONG, this.song, this.queue); - } - async joinVC(changeVC = false) { - if (this.currentConnection && !changeVC) { - this.currentConnection.subscribe(this._player); - } else if (this.channel instanceof Discord.VoiceChannel) { - const connection = DjsVoice.joinVoiceChannel({ - channelId: this.channel.id, - guildId: this.guild.id, - adapterCreator: this.guild.voiceAdapterCreator, - }); - await DjsVoice.entersState( - connection, - DjsVoice.VoiceConnectionStatus.Ready, - 10_000, - ); - connection.subscribe(this._player); - } else if (this.channel instanceof Discord.StageChannel) { - const connection = DjsVoice.joinVoiceChannel({ - channelId: this.channel.id, - guildId: this.guild.id, - adapterCreator: this.guild.voiceAdapterCreator, - }); - await DjsVoice.entersState( - connection, - DjsVoice.VoiceConnectionStatus.Ready, - 10_000, - ); - connection.subscribe(this._player); - await this.channel.guild.members.me.voice - .setSuppressed(false) - .catch(async () => { - return await this.channel.guild.members.me.voice - .setRequestToSpeak(true); - }); - } else { - const connection = this.channel.voiceConnection || await this.channel.call(); - connection.subscribe(this._player); - } - } - _discordEvent() { - // Event sus .-. - this.client.on('voiceStateUpdate', (oldState, newState) => { - if (!this._currentResourceAudio) return; - if (newState.guild?.id == this.guild?.id) { - if (oldState.channel?.id !== newState.channel?.id && oldState.channel?.id && newState.channel?.id && newState.id == this.client.user.id) { - // change vc - } - if (newState.id == this.client.user.id && oldState.channel?.members?.has(this.client.user.id) && !newState.channel?.members?.has(this.client.user.id)) { - this._stop(); - this.emit(Event.LEAVE_VC, this.message); - } - if (newState.channel?.members?.has(this.client.user.id) && !newState.channel?.members?.filter(m => m.id != this.client.user.id && !m.bot).size) { - // empty - if (this.options.leaveOnEmpty && !this._timeoutEmpty) { - this._timeoutEmpty = setTimeout(() => { - this._stop(false, true); - this.emit(Event.EMPTY, this.message); - }, this.options.emptyCooldown); - } - } - if (newState.channel?.members?.has(this.client.user.id) && newState.channel?.members?.filter(m => m.id != this.client.user.id && !m.bot).size > 0) { - // not empty - if (this._timeoutEmpty) clearTimeout(this._timeoutEmpty); - this._timeoutEmpty = undefined; - } - } else if (!this.guild?.id && !newState.guild?.id) { - // DM channels - if (!newState.channel?.voiceUsers?.filter(m => m.id != this.client.user.id).size) { - // empty - if (this.options.leaveOnEmpty && !this._timeoutEmpty) { - this._timeoutEmpty = setTimeout(() => { - this._stop(false, true); - this.emit(Event.EMPTY, this.message); - }, this.options.emptyCooldown); - } - } - if (newState.channel?.voiceUsers?.filter(m => m.id != this.client.user.id).size > 0) { - // not empty - if (this._timeoutEmpty) clearTimeout(this._timeoutEmpty); - this._timeoutEmpty = undefined; - } - } - }); - } - _awaitDM () { - if (!this.options.waitingUserToPlayInDMs) return true; - return new Promise(resolve => { - let i = setInterval(() => { - const m = this.channel.voiceUsers.get(this.client.user.id); - if (m) { - clearInterval(i); - resolve(true); - } - }, 250); - }) - } - _privateEvent() { - this.on('next_song', async () => { - await this._skip().catch(() => { - if (this.message) this.emit(Event.FINISH, this.message); - this._reset(); - }); - }); - } - _playerEvent() { - const player = this._player; - player.on('stateChange', async (oldState, newState) => { - // idle -> playing - // idle -> buffering - // buffering -> playing - // playing -> idle - if (newState.status.toLowerCase() == 'idle') { - this.isPlaying = false; - } else if (newState.status.toLowerCase() == 'paused' || newState.status.toLowerCase() == 'autopaused') { - this.isPlaying = false; - } else { - this.isPlaying = true; - } - this._currentTime = newState.playbackDuration; - // - if (oldState.status == 'playing' && newState.status == 'idle') { - this.emit('next_song'); - } - }); - player.on('error', (e) => { - this.emit(Event.ERROR, e); - }); - } -} - -module.exports = Player; - -/* Example -const player = new Player(client, options); - -player - .on('playSong', song => { - player.message.channel.send(`Now playing: ${song.title}`); - }) - .on('addSong', song => { - player.message.channel.send(`Added: ${song.title}`); - }) - .on('addPlaylist', playlist => { - player.message.channel.send(`Added Playlist: ${playlist.title}`); - }) - .on('disconnect', (message) => { - message.channel.send('Disconnected from voice channel.'); - }) - .on('finish', (message) => { - message.channel.send('Finished playing.'); - }) - .on('empty', (message) => { - message.channel.send('The queue is empty.'); - }) - .on('error', error => { - console.log('Music error', error); - }) - -client.player = player; - -// Method - -client.player.play({ - message, - channel: message.member.voice.channel, // VoiceChannel | DMChannel | StageChannel | GroupDMChannel - query: string, -}); - -client.player.skip(); - -client.player.previous(); - -client.player.pause(); - -client.player.resume(); - -client.player.setVolume(50); // 50% - -client.player.setLoopMode(1); // 0: none, 1: song, 2: queue; - -client.player.stop(); - -// Options - -options = { - directLink: true, // Whether or not play direct link of the song (not support) - joinNewVoiceChannel: true, // Whether or not joining the new voice channel when using #play method - waitingUserToPlayInDMs: true, // Waiting User join Call to play in DM channels - nsfw: false, // Whether or not play NSFW - leaveOnEmpty: true, // Whether or not leave voice channel when empty (not working) - leaveOnFinish: true, // Whether or not leave voice channel when finish - leaveOnStop: true, // Whether or not leave voice channel when stop - savePreviousSongs: true, // Whether or not save previous songs - emptyCooldown: 10_000, // Cooldown when empty voice channel -} - -// Properties - -song = Song; -guild = Discord.Guild; -channel = Channel; -client = Discord.Client; -isPlaying = false; -volume = 100; -currentTime = Unix timestamp miliseconds; -currentConnection = VoiceConnection; -queue: Song[]; -previousSongs: Song[]; -loopMode = 0; -*/ -``` diff --git a/README.md b/README.md index 3c8ab27..e4dbf45 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ ## About -Welcome to `discord.js-selfbot-v13@v2.15`, based on `discord.js@13.17` +Welcome to `discord.js-selfbot-v13@v3.0`, based on `discord.js@13.17` - discord.js-selfbot-v13 is a [Node.js](https://nodejs.org) module that allows user accounts to interact with the Discord API v9. @@ -57,7 +57,7 @@ **Node.js 16.6.0 or newer is required** -> Recommended Node.js version: 18 (LTS) +> Recommended Node.js version: 18+ (LTS) ```sh-session npm install discord.js-selfbot-v13@latest @@ -106,9 +106,6 @@ console.log('%cWorked!', 'font-size: 50px'); console.log(`%cYou now have your token in the clipboard!`, 'font-size: 16px'); ``` -Credit: . [hxr404](https://github.com/hxr404/Discord-Console-hacks) - - ## Contributing - Before creating an issue, please ensure that it hasn't already been reported/suggested, and double-check the @@ -127,7 +124,6 @@ Github Discussion: [Here](https://github.com/aiko-chan-ai/discord.js-selfbot-v13 A patched version of discord, with bot login support - 📕 [***aiko-chan-ai/Discord-Markdown***](https://github.com/aiko-chan-ai/Discord-Markdown)
Better Markdown to text chat Discord. -- 📗 ... ## Star History diff --git a/package.json b/package.json index cf1bbc7..743a76c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "discord.js-selfbot-v13", - "version": "2.15.1", + "version": "3.0.0", "description": "A unofficial discord.js fork for creating selfbots [Based on discord.js v13]", "main": "./src/index.js", "types": "./typings/index.d.ts", @@ -54,7 +54,6 @@ "@aikochan2k6/qrcode-terminal": "^0.12.1", "@discordjs/builders": "^1.6.3", "@discordjs/collection": "^1.5.3", - "@discordjs/voice": "^0.16.0", "@sapphire/async-queue": "^1.5.0", "@sapphire/shapeshift": "^3.9.3", "@types/node-fetch": "^2.6.7", @@ -62,12 +61,7 @@ "discord-api-types": "^0.37.61", "fetch-cookie": "^2.1.0", "form-data": "^4.0.0", - "json-bigint": "^1.0.0", - "lodash.permutations": "^1.0.0", "node-fetch": "^2.6.9", - "safe-base64": "^2.0.1-0", - "string-similarity": "^4.0.4", - "string_decoder": "^1.3.0", "tough-cookie": "^4.1.3", "ws": "^8.14.2" }, diff --git a/src/client/Client.js b/src/client/Client.js index 8459cb3..f520433 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -1,46 +1,40 @@ 'use strict'; const process = require('node:process'); -const { setInterval, setTimeout } = require('node:timers'); +const { setInterval } = require('node:timers'); +const { setTimeout } = require('node:timers'); const { Collection } = require('@discordjs/collection'); -const { getVoiceConnection } = require('@discordjs/voice'); -const chalk = require('chalk'); -const fetch = require('node-fetch'); const BaseClient = require('./BaseClient'); const ActionsManager = require('./actions/ActionsManager'); const ClientVoiceManager = require('./voice/ClientVoiceManager'); const WebSocketManager = require('./websocket/WebSocketManager'); const { Error, TypeError, RangeError } = require('../errors'); -const Discord = require('../index'); const BaseGuildEmojiManager = require('../managers/BaseGuildEmojiManager'); const BillingManager = require('../managers/BillingManager'); const ChannelManager = require('../managers/ChannelManager'); -const ClientUserSettingManager = require('../managers/ClientUserSettingManager'); -const DeveloperPortalManager = require('../managers/DeveloperPortalManager'); const GuildManager = require('../managers/GuildManager'); +const PresenceManager = require('../managers/PresenceManager'); const RelationshipManager = require('../managers/RelationshipManager'); -const SessionManager = require('../managers/SessionManager'); const UserManager = require('../managers/UserManager'); +const UserNoteManager = require('../managers/UserNoteManager'); const VoiceStateManager = require('../managers/VoiceStateManager'); const ShardClientUtil = require('../sharding/ShardClientUtil'); const ClientPresence = require('../structures/ClientPresence'); const GuildPreview = require('../structures/GuildPreview'); const GuildTemplate = require('../structures/GuildTemplate'); const Invite = require('../structures/Invite'); -const { CustomStatus } = require('../structures/RichPresence'); const { Sticker } = require('../structures/Sticker'); const StickerPack = require('../structures/StickerPack'); const VoiceRegion = require('../structures/VoiceRegion'); const Webhook = require('../structures/Webhook'); const Widget = require('../structures/Widget'); -const { Events, InviteScopes, Status, captchaServices } = require('../util/Constants'); +const { Events, Status } = require('../util/Constants'); const DataResolver = require('../util/DataResolver'); const Intents = require('../util/Intents'); const Options = require('../util/Options'); const Permissions = require('../util/Permissions'); const DiscordAuthWebsocket = require('../util/RemoteAuth'); const Sweepers = require('../util/Sweepers'); -const { getProxyObject } = require('../util/Util'); /** * The main hub for interacting with the Discord API, and the starting point for any bot. @@ -50,7 +44,7 @@ class Client extends BaseClient { /** * @param {ClientOptions} options Options for the client */ - constructor(options = {}) { + constructor(options) { super(options); const data = require('node:worker_threads').workerData ?? process.env; @@ -141,17 +135,6 @@ class Client extends BaseClient { */ this.users = new UserManager(this); - // Patch - /** - * All of the relationships {@link User} - * @type {RelationshipManager} - */ - this.relationships = new RelationshipManager(this); - /** - * All of the settings {@link Object} - * @type {ClientUserSettingManager} - */ - this.settings = new ClientUserSettingManager(this); /** * All of the guilds the client is currently handling, mapped by their ids - * as long as sharding isn't being used, this will be *every* guild the bot is a member of @@ -159,18 +142,6 @@ class Client extends BaseClient { */ this.guilds = new GuildManager(this); - /** - * Manages the API methods - * @type {BillingManager} - */ - this.billing = new BillingManager(this); - - /** - * All of the sessions of the client - * @type {SessionManager} - */ - this.sessions = new SessionManager(this); - /** * All of the {@link Channel}s that the client is currently handling, mapped by their ids - * as long as sharding isn't being used, this will be *every* channel in *every* guild the bot @@ -186,12 +157,6 @@ class Client extends BaseClient { */ this.sweepers = new Sweepers(this, this.options.sweepers); - /** - * The developer portal manager of the client - * @type {DeveloperPortalManager} - */ - this.developerPortal = new DeveloperPortalManager(this); - /** * The presence of the Client * @private @@ -199,6 +164,30 @@ class Client extends BaseClient { */ this.presence = new ClientPresence(this, this.options.presence); + /** + * A manager of the presences belonging to this client + * @type {PresenceManager} + */ + this.presences = new PresenceManager(this); + + /** + * All of the note that have been cached at any point, mapped by their ids + * @type {UserManager} + */ + this.notes = new UserNoteManager(this); + + /** + * All of the relationships {@link User} + * @type {RelationshipManager} + */ + this.relationships = new RelationshipManager(this); + + /** + * Manages the API methods + * @type {BillingManager} + */ + this.billing = new BillingManager(this); + Object.defineProperty(this, 'token', { writable: true }); if (!this.token && 'DISCORD_TOKEN' in process.env) { /** @@ -212,20 +201,12 @@ class Client extends BaseClient { this.token = null; } - this._interactionCache = new Collection(); - /** * User that the client is logged in as * @type {?ClientUser} */ this.user = null; - /** - * The application of this bot - * @type {?ClientApplication} - */ - this.application = null; - /** * Time at which the client was last regarded as being in the `READY` state * (each time the client disconnects and successfully reconnects, this will be overwritten) @@ -233,12 +214,6 @@ class Client extends BaseClient { */ this.readyAt = null; - /** - * Password cache - * @type {?string} - */ - this.password = this.options.password; - if (this.options.messageSweepInterval > 0) { process.emitWarning( 'The message sweeping client options are deprecated, use the global sweepers instead.', @@ -251,15 +226,6 @@ class Client extends BaseClient { } } - /** - * Session ID - * @type {?string} - * @readonly - */ - get sessionId() { - return this.ws.shards.first()?.sessionId; - } - /** * All custom emojis that the client has access to, mapped by their ids * @type {BaseGuildEmojiManager} @@ -291,19 +257,6 @@ class Client extends BaseClient { return this.readyAt ? Date.now() - this.readyAt : null; } - /** - * @external VoiceConnection - * @see {@link https://discord.js.org/#/docs/voice/main/class/VoiceConnection} - */ - /** - * Get connection to current call - * @type {?VoiceConnection} - * @readonly - */ - get callVoice() { - return getVoiceConnection(null); - } - /** * Logs the client in, establishing a WebSocket connection to Discord. * @param {string} [token=this.token] Token of the account to log in with @@ -320,8 +273,7 @@ class Client extends BaseClient { Logging on with a user token is unfortunately against the Discord \`Terms of Service\` and doing so might potentially get your account banned. - Use this at your own risk. -`, + Use this at your own risk.`, ); this.emit( Events.DEBUG, @@ -346,178 +298,10 @@ class Client extends BaseClient { } } - /** - * Login Discord with Username and Password - * @param {string} username Email or Phone Number - * @param {?string} password Password - * @param {?string} mfaCode 2FA Code / Backup Code - * @returns {Promise} - */ - async normalLogin(username, password = this.password, mfaCode) { - if (!username || !password || typeof username !== 'string' || typeof password !== 'string') { - throw new Error('NORMAL_LOGIN'); - } - this.emit( - Events.DEBUG, - `Connecting to Discord with: - username: ${username} - password: ${password.replace(/./g, '*')}`, - ); - const data = await this.api.auth.login.post({ - data: { - login: username, - password: password, - undelete: false, - captcha_key: null, - login_source: null, - gift_code_sku_id: null, - }, - auth: false, - }); - this.password = password; - if (!data.token && data.ticket && data.mfa) { - this.emit(Events.DEBUG, `Using 2FA Code: ${mfaCode}`); - const normal2fa = /(\d{6})/g; - const backupCode = /([a-z0-9]{4})-([a-z0-9]{4})/g; - if (!mfaCode || typeof mfaCode !== 'string') { - throw new Error('LOGIN_FAILED_2FA'); - } - if (normal2fa.test(mfaCode) || backupCode.test(mfaCode)) { - const data2 = await this.api.auth.mfa.totp.post({ - data: { - code: mfaCode, - ticket: data.ticket, - login_source: null, - gift_code_sku_id: null, - }, - auth: false, - }); - return this.login(data2.token); - } else { - throw new Error('LOGIN_FAILED_2FA'); - } - } else if (data.token) { - return this.login(data.token); - } else { - throw new Error('LOGIN_FAILED_UNKNOWN'); - } - } - - /** - * Switch the user - * @param {string} token User Token - * @returns {Promise} - */ - switchUser(token) { - this._clearCache(this.emojis.cache); - this._clearCache(this.guilds.cache); - this._clearCache(this.channels.cache); - this._clearCache(this.users.cache); - this._clearCache(this.relationships.cache); - this._clearCache(this.sessions.cache); - this._clearCache(this.voiceStates.cache); - this.ws.status = Status.IDLE; - return this.login(token); - } - - /** - * Sign in with the QR code on your phone. - * @param {DiscordAuthWebsocketOptions} options Options - * @returns {DiscordAuthWebsocket} - * @example - * client.QRLogin(); - */ - QRLogin(options = {}) { - const QR = new DiscordAuthWebsocket({ ...options, autoLogin: true }); - this.emit(Events.DEBUG, `Preparing to connect to the gateway (QR Login)`, QR); - return QR.connect(this); - } - - /** - * Implement `remoteAuth`, like using your phone to scan a QR code - * @param {string} url URL from QR code - * @returns {Promise} - */ - async remoteAuth(url) { - if (!this.isReady()) throw new Error('CLIENT_NOT_READY', 'Remote Auth'); - // Step 1: Parse URL - url = new URL(url); - if ( - !['discordapp.com', 'discord.com'].includes(url.hostname) || - !url.pathname.startsWith('/ra/') || - url.pathname.length <= 4 - ) { - throw new Error('INVALID_REMOTE_AUTH_URL'); - } - const hash = url.pathname.replace('/ra/', ''); - // Step 2: Post > Get handshake_token - const res = await this.api.users['@me']['remote-auth'].post({ - data: { - fingerprint: hash, - }, - }); - const handshake_token = res.handshake_token; - // Step 3: Post - return this.api.users['@me']['remote-auth'].finish.post({ data: { handshake_token, temporary_token: false } }); - // Cancel - // this.api.users['@me']['remote-auth'].cancel.post({ data: { handshake_token } }); - } - - /** - * Create a new token based on the current token - * @returns {Promise} New Discord Token - */ - createToken() { - return new Promise((resolve, reject) => { - // Step 1: Create DiscordAuthWebsocket - const QR = new DiscordAuthWebsocket({ - hiddenLog: true, - generateQR: false, - autoLogin: false, - debug: false, - failIfError: false, - userAgent: this.options.http.headers['User-Agent'], - wsProperties: this.options.ws.properties, - }); - // Step 2: Add event - QR.once('ready', async (_, url) => { - try { - await this.remoteAuth(url); - } catch (e) { - reject(e); - } - }).once('finish', (user, token) => { - resolve(token); - }); - // Step 3: Connect - QR.connect(); - }); - } - - /** - * Emitted whenever clientOptions.checkUpdate = false - * @event Client#update - * @param {string} oldVersion Current version - * @param {string} newVersion Latest version - */ - - /** - * Check for updates - * @returns {Promise} - */ - async checkUpdate() { - const res_ = await ( - await fetch(`https://registry.npmjs.com/${encodeURIComponent('discord.js-selfbot-v13')}`) - ).json(); - try { - const latest_tag = res_['dist-tags'].latest; - this.emit('update', Discord.version, latest_tag); - this.emit('debug', `${chalk.greenBright('[OK]')} Check Update success`); - } catch { - this.emit('debug', `${chalk.redBright('[Fail]')} Check Update error`); - this.emit('update', Discord.version, false); - } - return this; + QRLogin() { + const ws = new DiscordAuthWebsocket(); + ws.once('ready', () => ws.generateQR()); + return ws.connect(this); } /** @@ -544,7 +328,6 @@ class Client extends BaseClient { this.sweepers.destroy(); this.ws.destroy(); this.token = null; - this.password = null; } /** @@ -558,7 +341,7 @@ class Client extends BaseClient { voip_provider: null, }, }); - await this.destroy(); + return this.destroy(); } /** @@ -586,51 +369,6 @@ class Client extends BaseClient { return new Invite(this, data); } - /** - * Join this Guild using this invite (fast) - * @param {InviteResolvable} invite Invite code or URL - * @returns {Promise} - * @example - * await client.acceptInvite('https://discord.gg/genshinimpact') - */ - async acceptInvite(invite) { - const code = DataResolver.resolveInviteCode(invite); - if (!code) throw new Error('INVITE_RESOLVE_CODE'); - if (invite instanceof Invite) { - await invite.acceptInvite(); - } else { - await this.api.invites(code).post({ - headers: { - 'X-Context-Properties': 'eyJsb2NhdGlvbiI6Ik1hcmtkb3duIExpbmsifQ==', // Markdown Link - }, - data: { - session_id: this.sessionId, - }, - }); - } - } - - /** - * Redeem nitro from code or url. - * @param {string} nitro Nitro url or code - * @param {TextChannelResolvable} channel Channel that the code was sent in - * @param {Snowflake} [paymentSourceId] Payment source id - * @returns {Promise} - */ - redeemNitro(nitro, channel, paymentSourceId) { - if (typeof nitro !== 'string') throw new Error('INVALID_NITRO'); - const nitroCode = - nitro.match(/(discord.gift|discord.com|discordapp.com\/gifts)\/(\w{16,25})/) || - nitro.match(/(discord\.gift\/|discord\.com\/gifts\/|discordapp\.com\/gifts\/)(\w+)/); - if (!nitroCode) return false; - const code = nitroCode[2]; - channel = this.channels.resolveId(channel); - return this.api.entitlements['gift-codes'](code).redeem.post({ - auth: true, - data: { channel_id: channel || null, payment_source_id: paymentSourceId || null }, - }); - } - /** * Obtains a template from Discord. * @param {GuildTemplateResolvable} template Template code or URL @@ -721,16 +459,6 @@ class Client extends BaseClient { } } - /** - * Clear a cache - * @param {Collection} cache The cache to clear - * @returns {number} The number of removed entries - * @private - */ - _clearCache(cache) { - return cache.sweep(() => true); - } - /** * Sweeps all text-based channels' messages and removes the ones older than the max message lifetime. * If the message has been edited, the time of the edit is used rather than the time of the original message. @@ -791,65 +519,13 @@ class Client extends BaseClient { */ /** - * Generates a link that can be used to invite the bot to a guild. - * @param {InviteGenerationOptions} [options={}] Options for the invite - * @returns {string} - * @example - * const link = client.generateInvite({ - * scopes: ['applications.commands'], - * }); - * console.log(`Generated application invite link: ${link}`); - * @example - * const link = client.generateInvite({ - * permissions: [ - * Permissions.FLAGS.SEND_MESSAGES, - * Permissions.FLAGS.MANAGE_GUILD, - * Permissions.FLAGS.MENTION_EVERYONE, - * ], - * scopes: ['bot'], - * }); - * console.log(`Generated bot invite link: ${link}`); + * The sleep function in JavaScript returns a promise that resolves after a specified timeout. + * @param {number} timeout - The timeout parameter is the amount of time, in milliseconds, that the sleep + * function will wait before resolving the promise and continuing execution. + * @returns {void} The `sleep` function is returning a Promise. */ - generateInvite(options = {}) { - if (typeof options !== 'object') throw new TypeError('INVALID_TYPE', 'options', 'object', true); - if (!this.application) throw new Error('CLIENT_NOT_READY', 'generate an invite link'); - - const query = new URLSearchParams({ - client_id: this.application.id, - }); - - const { scopes } = options; - if (typeof scopes === 'undefined') { - throw new TypeError('INVITE_MISSING_SCOPES'); - } - if (!Array.isArray(scopes)) { - throw new TypeError('INVALID_TYPE', 'scopes', 'Array of Invite Scopes', true); - } - if (!scopes.some(scope => ['bot', 'applications.commands'].includes(scope))) { - throw new TypeError('INVITE_MISSING_SCOPES'); - } - const invalidScope = scopes.find(scope => !InviteScopes.includes(scope)); - if (invalidScope) { - throw new TypeError('INVALID_ELEMENT', 'Array', 'scopes', invalidScope); - } - query.set('scope', scopes.join(' ')); - - if (options.permissions) { - const permissions = Permissions.resolve(options.permissions); - if (permissions) query.set('permissions', permissions); - } - - if (options.disableGuildSelect) { - query.set('disable_guild_select', true); - } - - if (options.guild) { - const guildId = this.guilds.resolveId(options.guild); - if (!guildId) throw new TypeError('INVALID_TYPE', 'options.guild', 'GuildResolvable'); - query.set('guild_id', guildId); - } - - return `${this.options.http.api}${this.api.oauth2.authorize}?${query}`; + sleep(timeout) { + return new Promise(r => setTimeout(r, timeout)); } toJSON() { @@ -859,41 +535,46 @@ class Client extends BaseClient { } /** - * Calls {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval} on a script - * with the client as `this`. - * @param {string} script Script to eval - * @returns {*} - * @private + * Join this Guild using this invite (fast) + * @param {InviteResolvable} invite Invite code or URL + * @returns {Promise} + * @example + * await client.acceptInvite('https://discord.gg/genshinimpact') */ - _eval(script) { - return eval(script); + async acceptInvite(invite) { + const code = DataResolver.resolveInviteCode(invite); + if (!code) throw new Error('INVITE_RESOLVE_CODE'); + if (invite instanceof Invite) { + await invite.acceptInvite(); + } else { + await this.api.invites(code).post({ + DiscordContext: { location: 'Markdown Link' }, + data: { + session_id: this.ws.shards.first()?.sessionId, + }, + }); + } } /** - * Sets the client's presence. (Sync Setting). - * @param {Client} client Discord Client - * @private + * Redeem nitro from code or url. + * @param {string} nitro Nitro url or code + * @param {TextChannelResolvable} [channel] Channel that the code was sent in + * @param {Snowflake} [paymentSourceId] Payment source id + * @returns {Promise} */ - customStatusAuto(client) { - client = client ?? this; - if (!client.user) return; - const custom_status = new CustomStatus(); - if (!client.settings.rawSetting.custom_status?.text && !client.settings.rawSetting.custom_status?.emoji_name) { - client.user.setPresence({ - activities: this.presence.activities.filter(a => a.type !== 'CUSTOM'), - status: client.settings.rawSetting.status ?? 'invisible', - }); - } else { - custom_status.setEmoji({ - name: client.settings.rawSetting.custom_status?.emoji_name, - id: client.settings.rawSetting.custom_status?.emoji_id, - }); - custom_status.setState(client.settings.rawSetting.custom_status?.text); - client.user.setPresence({ - activities: [custom_status.toJSON(), ...this.presence.activities.filter(a => a.type !== 'CUSTOM')], - status: client.settings.rawSetting.status ?? 'invisible', - }); - } + redeemNitro(nitro, channel, paymentSourceId) { + if (typeof nitro !== 'string') throw new Error('INVALID_NITRO'); + const nitroCode = + nitro.match(/(discord.gift|discord.com|discordapp.com\/gifts)\/(\w{16,25})/) || + nitro.match(/(discord\.gift\/|discord\.com\/gifts\/|discordapp\.com\/gifts\/)(\w+)/); + if (!nitroCode) return false; + const code = nitroCode[2]; + channel = this.channels.resolveId(channel); + return this.api.entitlements['gift-codes'](code).redeem.post({ + auth: true, + data: { channel_id: channel || null, payment_source_id: paymentSourceId || null }, + }); } /** @@ -909,7 +590,7 @@ class Client extends BaseClient { * Authorize an application. * @param {string} url Discord Auth URL * @param {OAuth2AuthorizeOptions} options Oauth2 options - * @returns {Promise} + * @returns {Promise} * @example * client.authorizeURL(`https://discord.com/api/oauth2/authorize?client_id=botID&permissions=8&scope=applications.commands%20bot`, { guild_id: "guildID", @@ -937,12 +618,14 @@ class Client extends BaseClient { } /** - * Makes waiting time for Client. - * @param {number} miliseconds Sleeping time as milliseconds. - * @returns {Promise | null} + * Calls {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval} on a script + * with the client as `this`. + * @param {string} script Script to eval + * @returns {*} + * @private */ - sleep(miliseconds) { - return typeof miliseconds === 'number' ? new Promise(r => setTimeout(r, miliseconds).unref()) : null; + _eval(script) { + return eval(script); } /** @@ -956,73 +639,8 @@ class Client extends BaseClient { } else { options.intents = Intents.resolve(options.intents); } - if (options && typeof options.checkUpdate !== 'boolean') { - throw new TypeError('CLIENT_INVALID_OPTION', 'checkUpdate', 'a boolean'); - } - if (options && typeof options.syncStatus !== 'boolean') { - throw new TypeError('CLIENT_INVALID_OPTION', 'syncStatus', 'a boolean'); - } - if (options && typeof options.autoRedeemNitro !== 'boolean') { - throw new TypeError('CLIENT_INVALID_OPTION', 'autoRedeemNitro', 'a boolean'); - } - if (options && options.captchaService && !captchaServices.includes(options.captchaService)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'captchaService', captchaServices.join(', ')); - } - // Parse captcha key - if (options && captchaServices.includes(options.captchaService) && options.captchaService !== 'custom') { - if (typeof options.captchaKey !== 'string') { - throw new TypeError('CLIENT_INVALID_OPTION', 'captchaKey', 'a string'); - } - switch (options.captchaService) { - case '2captcha': - if (options.captchaKey.length !== 32) { - throw new TypeError('CLIENT_INVALID_OPTION', 'captchaKey', 'a 32 character string'); - } - break; - case 'capmonster': - if (options.captchaKey.length !== 32) { - throw new TypeError('CLIENT_INVALID_OPTION', 'captchaKey', 'a 32 character string'); - } - break; - case 'nopecha': { - if (options.captchaKey.length !== 16) { - throw new TypeError('CLIENT_INVALID_OPTION', 'captchaKey', 'a 16 character string'); - } - break; - } - } - } - if (typeof options.captchaRetryLimit !== 'number' || isNaN(options.captchaRetryLimit)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'captchaRetryLimit', 'a number'); - } - if (options && typeof options.captchaSolver !== 'function') { - throw new TypeError('CLIENT_INVALID_OPTION', 'captchaSolver', 'a function'); - } - if (options && typeof options.captchaWithProxy !== 'boolean') { - throw new TypeError('CLIENT_INVALID_OPTION', 'captchaWithProxy', 'a boolean'); - } - if (options && typeof options.DMSync !== 'boolean') { - throw new TypeError('CLIENT_INVALID_OPTION', 'DMSync', 'a boolean'); - } - if (options && typeof options.patchVoice !== 'boolean') { - throw new TypeError('CLIENT_INVALID_OPTION', 'patchVoice', 'a boolean'); - } - if (options && options.password && typeof options.password !== 'string') { - throw new TypeError('CLIENT_INVALID_OPTION', 'password', 'a string'); - } - if (options && options.usingNewAttachmentAPI && typeof options.usingNewAttachmentAPI !== 'boolean') { - throw new TypeError('CLIENT_INVALID_OPTION', 'usingNewAttachmentAPI', 'a boolean'); - } - if (options && options.interactionTimeout && typeof options.interactionTimeout !== 'number') { - throw new TypeError('CLIENT_INVALID_OPTION', 'interactionTimeout', 'a number'); - } - if (options && typeof options.proxy !== 'string') { - throw new TypeError('CLIENT_INVALID_OPTION', 'proxy', 'a string'); - } else if (options && options.proxy && typeof options.proxy === 'string') { - getProxyObject(options.proxy); - } - if (typeof options.shardCount !== 'number' || isNaN(options.shardCount) || options.shardCount < 1) { - throw new TypeError('CLIENT_INVALID_OPTION', 'shardCount', 'a number greater than or equal to 1'); + if (typeof options.shardCount !== 'number' || isNaN(options.shardCount) || options.shardCount !== 1) { + throw new TypeError('CLIENT_INVALID_OPTION', 'shardCount', 'a number equal to 1'); } if (options.shards && !(options.shards === 'auto' || Array.isArray(options.shards))) { throw new TypeError('CLIENT_INVALID_OPTION', 'shards', "'auto', a number or array of numbers"); @@ -1046,12 +664,15 @@ class Client extends BaseClient { if (!Array.isArray(options.partials)) { throw new TypeError('CLIENT_INVALID_OPTION', 'partials', 'an Array'); } - if (typeof options.waitGuildTimeout !== 'number' || isNaN(options.waitGuildTimeout)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'waitGuildTimeout', 'a number'); - } if (typeof options.messageCreateEventGuildTimeout !== 'number' || isNaN(options.messageCreateEventGuildTimeout)) { throw new TypeError('CLIENT_INVALID_OPTION', 'messageCreateEventGuildTimeout', 'a number'); } + if (typeof options.DMChannelVoiceStatusSync !== 'number' || isNaN(options.DMChannelVoiceStatusSync)) { + throw new TypeError('CLIENT_INVALID_OPTION', 'DMChannelVoiceStatusSync', 'a number'); + } + if (typeof options.waitGuildTimeout !== 'number' || isNaN(options.waitGuildTimeout)) { + throw new TypeError('CLIENT_INVALID_OPTION', 'waitGuildTimeout', 'a number'); + } if (typeof options.restWsBridgeTimeout !== 'number' || isNaN(options.restWsBridgeTimeout)) { throw new TypeError('CLIENT_INVALID_OPTION', 'restWsBridgeTimeout', 'a number'); } diff --git a/src/client/websocket/handlers/READY.js b/src/client/websocket/handlers/READY.js index 6233b09..dfb0c41 100644 --- a/src/client/websocket/handlers/READY.js +++ b/src/client/websocket/handlers/READY.js @@ -1,105 +1,13 @@ 'use strict'; +const USER_REQUIRED_ACTION = require('./USER_REQUIRED_ACTION_UPDATE'); +const { Opcodes } = require('../../../util/Constants'); + let ClientUser; -const { VoiceConnection } = require('@discordjs/voice'); -const chalk = require('chalk'); -const { Events, Opcodes } = require('../../../util/Constants'); -const Util = require('../../../util/Util'); -const { VoiceConnection: VoiceConnection_patch } = require('../../../util/Voice'); -let firstReady = false; -function patchVoice(client) { - try { - /* eslint-disable */ - VoiceConnection.prototype.configureNetworking = VoiceConnection_patch.prototype.configureNetworking; - client.emit( - 'debug', - `${chalk.greenBright('[OK]')} Patched ${chalk.cyanBright( - 'VoiceConnection.prototype.configureNetworking', - )} [${chalk.bgMagentaBright('@discordjs/voice')} - ${chalk.redBright('v0.16.0')}]`, - ); - /* eslint-enable */ - } catch (e) { - client.emit( - 'debug', - `${chalk.redBright('[Fail]')} Patched ${chalk.cyanBright( - 'VoiceConnection.prototype.configureNetworking', - )} [${chalk.bgMagentaBright('@discordjs/voice')} - ${chalk.redBright('v0.16.0')}]\n${e.stack}`, - ); - client.emit( - Events.ERROR, - `${chalk.redBright('[Fail]')} Patched ${chalk.cyanBright( - 'VoiceConnection.prototype.configureNetworking', - )} [${chalk.bgMagentaBright('@discordjs/voice')} - ${chalk.redBright('v0.16.0')}]`, - ); - client.emit( - Events.ERROR, - `${chalk.redBright('[Error]')} Please install ${chalk.bgMagentaBright( - '@discordjs/voice', - )} version ${chalk.redBright('v0.16.0')}`, - ); - } -} - -module.exports = async (client, { d: data }, shard) => { - Util.clientRequiredAction(client, data.required_action); - if (!firstReady) { - if (client.options.checkUpdate) { - client.once('update', (currentVersion, newVersion) => { - if (!newVersion) { - console.log(` - ${chalk.redBright('[WARNING]')} Cannot check new Discord.js-selfbot-v13 version. - Current: ${chalk.blueBright(currentVersion)} - - If you don't want to show this message, set ${chalk.cyanBright('checkUpdate')} to false - - const client = new Client({ - checkUpdate: false, - }); - - and using event update - https://discordjs-self-v13.netlify.app/#/docs/docs/main/class/Client?scrollTo=e-update\n`); - } else if (currentVersion !== newVersion && !currentVersion.includes('-')) { - console.log(` - ${chalk.yellowBright('[WARNING]')} New Discord.js-selfbot-v13 version. - Current: ${chalk.redBright(currentVersion)} => Latest: ${chalk.greenBright(newVersion)} - - If you don't want to show this message, set ${chalk.cyanBright('checkUpdate')} to false - - const client = new Client({ - checkUpdate: false, - }); - - and using event update - https://discordjs-self-v13.netlify.app/#/docs/docs/main/class/Client?scrollTo=e-update\n`); - } else { - console.log( - ` - ${chalk.greenBright('[OK]')} Discord.js-selfbot-v13 is up to date. Current: ${chalk.blueBright(currentVersion)} - - If you don't want to show this message, set ${chalk.cyanBright('checkUpdate')} to false - - const client = new Client({ - checkUpdate: false, - }); - - and using event update - https://discordjs-self-v13.netlify.app/#/docs/docs/main/class/Client?scrollTo=e-update\n`, - ); - } - }); - client.checkUpdate(); - } - - if (client.options.patchVoice) { - patchVoice(client); - } - - if (client.options.syncStatus) { - client.customStatusAuto(client); - } - firstReady = true; - } +module.exports = (client, { d: data }, shard) => { + // Check + USER_REQUIRED_ACTION(client, { d: data }); if (client.user) { client.user._patch(data.user); @@ -109,31 +17,8 @@ module.exports = async (client, { d: data }, shard) => { client.users.cache.set(client.user.id, client.user); } - client.settings._patch(data.user_settings); - - client.user.connectedAccounts = data.connected_accounts ?? []; - - client.relationships._setup(data.relationships); - - client.user._patchNote(data.notes); - - const syncTime = Date.now(); for (const private_channel of data.private_channels) { - const channel = client.channels._add(private_channel); - // Rate limit warning - if (client.options.DMSync) { - client.ws.broadcast({ - op: Opcodes.DM_UPDATE, - d: { - channel_id: channel.id, - }, - }); - } - } - if (client.options.DMSync) { - console.warn( - `Gateway Rate Limit Warning: Sending ${data.private_channels.length} Requests / ${Date.now() - syncTime || 1} ms`, - ); + client.channels._add(private_channel); } for (const guild of data.guilds) { @@ -141,31 +26,41 @@ module.exports = async (client, { d: data }, shard) => { client.guilds._add(guild); } - for (const gSetting of Array.isArray(data.user_guild_settings) ? data.user_guild_settings : []) { - const guild = client.guilds.cache.get(gSetting.guild_id); - if (guild) guild.settings._patch(gSetting); - } - const largeGuilds = data.guilds.filter(g => g.large); - client.emit('debug', `[READY] Received ${data.guilds.length} guilds, ${largeGuilds.length} large guilds`); - // Receive messages in large guilds - for (const guild of largeGuilds) { - await client.sleep(client.options.messageCreateEventGuildTimeout); - client.ws.broadcast({ - op: Opcodes.GUILD_SUBSCRIPTIONS, - d: { - guild_id: guild.id, - typing: true, - threads: true, - activities: true, - thread_member_lists: [], - members: [], - channels: {}, - }, - }); - } + // User Notes + client.notes._reload(data.notes); - shard.checkReady(); + // Relationship + client.relationships._setup(data.relationships); + + Promise.all( + largeGuilds.map(async (guild, index) => { + client.ws.broadcast({ + op: Opcodes.GUILD_SUBSCRIPTIONS, + d: { + guild_id: guild.id, + typing: true, + threads: true, + activities: true, + thread_member_lists: [], + members: [], + channels: {}, + }, + }); + client.emit('debug', `[READY] Register guild ${guild.id}`); + await client.sleep(client.options.messageCreateEventGuildTimeout * index); + }), + data.private_channels.map(async (c, index) => { + if (client.options.DMChannelVoiceStatusSync < 1) return; + client.ws.broadcast({ + op: Opcodes.DM_UPDATE, + d: { + channel_id: c.id, + }, + }); + await client.sleep(client.options.DMChannelVoiceStatusSync * index); + }), + ).then(() => shard.checkReady()); }; diff --git a/src/client/websocket/handlers/USER_GUILD_SETTINGS_UPDATE.js b/src/client/websocket/handlers/USER_GUILD_SETTINGS_UPDATE.js deleted file mode 100644 index deddaf1..00000000 --- a/src/client/websocket/handlers/USER_GUILD_SETTINGS_UPDATE.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; -const { Events } = require('../../../util/Constants'); -module.exports = (client, { d: data }) => { - const guild = client.guilds.cache.get(data.guild_id); - guild?.settings._patch(data); - /** - * Emitted whenever guild settings are updated - * @event Client#userGuildSettingsUpdate - * @param {Guild} guild Guild - */ - return client.emit(Events.USER_GUILD_SETTINGS_UPDATE, guild); -}; diff --git a/src/client/websocket/handlers/USER_SETTINGS_UPDATE.js b/src/client/websocket/handlers/USER_SETTINGS_UPDATE.js deleted file mode 100644 index 7f90916..00000000 --- a/src/client/websocket/handlers/USER_SETTINGS_UPDATE.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; -const { Events } = require('../../../util/Constants'); -module.exports = (client, { d: data }) => { - client.settings._patch(data); - if (('status' in data || 'custom_status' in data) && client.options.syncStatus) { - client.customStatusAuto(client); - } - return client.emit(Events.USER_SETTINGS_UPDATE, data); -}; diff --git a/src/client/websocket/handlers/index.js b/src/client/websocket/handlers/index.js index 3b046e8..5281ef3 100644 --- a/src/client/websocket/handlers/index.js +++ b/src/client/websocket/handlers/index.js @@ -3,10 +3,6 @@ const handlers = Object.fromEntries([ ['READY', require('./READY')], ['RESUMED', require('./RESUMED')], - ['RELATIONSHIP_ADD', require('./RELATIONSHIP_ADD')], - ['RELATIONSHIP_REMOVE', require('./RELATIONSHIP_REMOVE')], - ['RELATIONSHIP_UPDATE', require('./RELATIONSHIP_UPDATE')], - ['APPLICATION_COMMAND_AUTOCOMPLETE_RESPONSE', require('./APPLICATION_COMMAND_AUTOCOMPLETE_RESPONSE')], ['APPLICATION_COMMAND_CREATE', require('./APPLICATION_COMMAND_CREATE')], ['APPLICATION_COMMAND_DELETE', require('./APPLICATION_COMMAND_DELETE')], ['APPLICATION_COMMAND_UPDATE', require('./APPLICATION_COMMAND_UPDATE')], @@ -15,9 +11,6 @@ const handlers = Object.fromEntries([ ['AUTO_MODERATION_RULE_CREATE', require('./AUTO_MODERATION_RULE_CREATE')], ['AUTO_MODERATION_RULE_DELETE', require('./AUTO_MODERATION_RULE_DELETE')], ['AUTO_MODERATION_RULE_UPDATE', require('./AUTO_MODERATION_RULE_UPDATE')], - ['CALL_CREATE', require('./CALL_CREATE')], - ['CALL_UPDATE', require('./CALL_UPDATE')], - ['CALL_DELETE', require('./CALL_DELETE')], ['GUILD_CREATE', require('./GUILD_CREATE')], ['GUILD_DELETE', require('./GUILD_DELETE')], ['GUILD_UPDATE', require('./GUILD_UPDATE')], @@ -27,8 +20,6 @@ const handlers = Object.fromEntries([ ['GUILD_MEMBER_REMOVE', require('./GUILD_MEMBER_REMOVE')], ['GUILD_MEMBER_UPDATE', require('./GUILD_MEMBER_UPDATE')], ['GUILD_MEMBERS_CHUNK', require('./GUILD_MEMBERS_CHUNK')], - ['GUILD_MEMBER_LIST_UPDATE', require('./GUILD_MEMBER_LIST_UPDATE.js')], - ['GUILD_APPLICATION_COMMANDS_UPDATE', require('./GUILD_APPLICATION_COMMANDS_UPDATE.js')], ['GUILD_INTEGRATIONS_UPDATE', require('./GUILD_INTEGRATIONS_UPDATE')], ['GUILD_ROLE_CREATE', require('./GUILD_ROLE_CREATE')], ['GUILD_ROLE_DELETE', require('./GUILD_ROLE_DELETE')], @@ -40,9 +31,6 @@ const handlers = Object.fromEntries([ ['CHANNEL_DELETE', require('./CHANNEL_DELETE')], ['CHANNEL_UPDATE', require('./CHANNEL_UPDATE')], ['CHANNEL_PINS_UPDATE', require('./CHANNEL_PINS_UPDATE')], - ['CHANNEL_RECIPIENT_ADD', require('./CHANNEL_RECIPIENT_ADD')], - ['CHANNEL_RECIPIENT_REMOVE', require('./CHANNEL_RECIPIENT_REMOVE')], - ['MESSAGE_ACK', require('./MESSAGE_ACK')], ['MESSAGE_CREATE', require('./MESSAGE_CREATE')], ['MESSAGE_DELETE', require('./MESSAGE_DELETE')], ['MESSAGE_UPDATE', require('./MESSAGE_UPDATE')], @@ -57,21 +45,12 @@ const handlers = Object.fromEntries([ ['THREAD_LIST_SYNC', require('./THREAD_LIST_SYNC')], ['THREAD_MEMBER_UPDATE', require('./THREAD_MEMBER_UPDATE')], ['THREAD_MEMBERS_UPDATE', require('./THREAD_MEMBERS_UPDATE')], - ['USER_SETTINGS_UPDATE', require('./USER_SETTINGS_UPDATE')], // Opcode 0 - ['USER_GUILD_SETTINGS_UPDATE', require('./USER_GUILD_SETTINGS_UPDATE')], - // USER_SETTINGS_PROTO_UPDATE // opcode 0 - ['USER_NOTE_UPDATE', require('./USER_NOTE_UPDATE')], - ['USER_REQUIRED_ACTION_UPDATE', require('./USER_REQUIRED_ACTION_UPDATE')], ['USER_UPDATE', require('./USER_UPDATE')], ['PRESENCE_UPDATE', require('./PRESENCE_UPDATE')], ['TYPING_START', require('./TYPING_START')], ['VOICE_STATE_UPDATE', require('./VOICE_STATE_UPDATE')], ['VOICE_SERVER_UPDATE', require('./VOICE_SERVER_UPDATE')], ['WEBHOOKS_UPDATE', require('./WEBHOOKS_UPDATE')], - ['INTERACTION_CREATE', require('./INTERACTION_CREATE')], - ['INTERACTION_SUCCESS', require('./INTERACTION_SUCCESS')], - ['INTERACTION_MODAL_CREATE', require('./INTERACTION_MODAL_CREATE')], - ['INTERACTION_FAILURE', require('./INTERACTION_FAILURE')], ['STAGE_INSTANCE_CREATE', require('./STAGE_INSTANCE_CREATE')], ['STAGE_INSTANCE_UPDATE', require('./STAGE_INSTANCE_UPDATE')], ['STAGE_INSTANCE_DELETE', require('./STAGE_INSTANCE_DELETE')], @@ -82,6 +61,18 @@ const handlers = Object.fromEntries([ ['GUILD_SCHEDULED_EVENT_USER_ADD', require('./GUILD_SCHEDULED_EVENT_USER_ADD')], ['GUILD_SCHEDULED_EVENT_USER_REMOVE', require('./GUILD_SCHEDULED_EVENT_USER_REMOVE')], ['GUILD_AUDIT_LOG_ENTRY_CREATE', require('./GUILD_AUDIT_LOG_ENTRY_CREATE')], + // Selfbot + ['RELATIONSHIP_ADD', require('./RELATIONSHIP_ADD')], + ['RELATIONSHIP_REMOVE', require('./RELATIONSHIP_REMOVE')], + ['RELATIONSHIP_UPDATE', require('./RELATIONSHIP_UPDATE')], + ['USER_NOTE_UPDATE', require('./USER_NOTE_UPDATE')], + ['CHANNEL_RECIPIENT_ADD', require('./CHANNEL_RECIPIENT_ADD')], + ['CHANNEL_RECIPIENT_REMOVE', require('./CHANNEL_RECIPIENT_REMOVE')], + ['INTERACTION_MODAL_CREATE', require('./INTERACTION_MODAL_CREATE')], + ['USER_REQUIRED_ACTION_UPDATE', require('./USER_REQUIRED_ACTION_UPDATE')], + ['CALL_CREATE', require('./CALL_CREATE')], + ['CALL_UPDATE', require('./CALL_UPDATE')], + ['CALL_DELETE', require('./CALL_DELETE')], ]); module.exports = handlers; diff --git a/src/errors/Messages.js b/src/errors/Messages.js index f4c8126..bf0ebec 100644 --- a/src/errors/Messages.js +++ b/src/errors/Messages.js @@ -11,8 +11,6 @@ const Messages = { TOKEN_INVALID: 'An invalid token was provided.', TOKEN_MISSING: 'Request to use token, but token was unavailable to the client.', - MFA_INVALID: 'An invalid mfa code was provided', - WS_CLOSE_REQUESTED: 'WebSocket closed due to user request.', WS_CONNECTION_EXISTS: 'There is already an existing WebSocket connection.', WS_NOT_OPEN: (data = 'data') => `WebSocket not open to send ${data}`, @@ -20,8 +18,8 @@ const Messages = { BITFIELD_INVALID: bit => `Invalid bitfield flag or number: ${bit}.`, - SHARDING_INVALID: 'Invalid shard settings were provided.', - SHARDING_REQUIRED: 'This session would have handled too many guilds - Sharding is required.', + SHARDING_INVALID: '[Bot Token] Invalid shard settings were provided.', + SHARDING_REQUIRED: '[Bot Token] This session would have handled too many guilds - Sharding is required.', INVALID_INTENTS: '[Bot Token] Invalid intent provided for WebSocket intents.', DISALLOWED_INTENTS: '[Bot Token] Privileged intent provided is not enabled or whitelisted.', SHARDING_NO_SHARDS: 'No shards have been spawned.', @@ -49,12 +47,6 @@ const Messages = { EMBED_FOOTER_TEXT: 'MessageEmbed footer text must be a string.', EMBED_DESCRIPTION: 'MessageEmbed description must be a string.', EMBED_AUTHOR_NAME: 'MessageEmbed author name must be a string.', - /* Add */ - MISSING_PERMISSIONS: (...permission) => `You can't do this action [Missing Permission(s): ${permission.join(', ')}]`, - EMBED_PROVIDER_NAME: 'MessageEmbed provider name must be a string.', - INVALID_COMMAND_NAME: allCMD => `Could not parse subGroupCommand and subCommand due to too long: ${allCMD.join(' ')}`, - INVALID_RANGE_QUERY_MEMBER: 'Invalid range query member. (0 `Collector received no interactions before ending with reason: ${reason}`, FILE_NOT_FOUND: file => `File could not be found: ${file}`, USER_BANNER_NOT_FETCHED: "You must fetch this user's banner before trying to generate its URL!", USER_NO_DM_CHANNEL: 'No DM Channel exists!', - CLIENT_NO_CALL: 'No call exists!', VOICE_NOT_STAGE_CHANNEL: 'You are only allowed to do this in stage channels.', - VOICE_NOT_IN_GUILD: 'You are only allowed to do this in guild channels.', VOICE_STATE_NOT_OWN: 'You cannot self-deafen/mute/request to speak on VoiceStates that do not belong to the ClientUser.', @@ -103,17 +101,11 @@ const Messages = { GUILD_CHANNEL_UNOWNED: "The fetched channel does not belong to this manager's guild.", GUILD_OWNED: 'Guild is owned by the client.', GUILD_MEMBERS_TIMEOUT: "Members didn't arrive in time.", - GUILD_APPLICATION_COMMANDS_SEARCH_TIMEOUT: "Application commands didn't arrive in time.", GUILD_UNCACHED_ME: 'The client user as a member of this guild is uncached.', CHANNEL_NOT_CACHED: 'Could not find the channel where this message came from in the cache!', STAGE_CHANNEL_RESOLVE: 'Could not resolve channel to a stage channel.', GUILD_SCHEDULED_EVENT_RESOLVE: 'Could not resolve the guild scheduled event.', - REQUIRE_PASSWORD: 'You must provide a password.', - INVALIDATE_MEMBER: range => `Invalid member range: [${range[0]}, ${range[1]}]`, - - MISSING_VALUE: (where, type) => `Missing value for ${where} (${type})`, - INVALID_TYPE: (name, expected, an = false) => `Supplied ${name} is not a${an ? 'n' : ''} ${expected}.`, INVALID_ELEMENT: (type, name, elem) => `Supplied ${type} ${name} includes an invalid element: ${elem}`, @@ -141,10 +133,6 @@ const Messages = { INVITE_NOT_FOUND: 'Could not find the requested invite.', - 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_NOT_IN_GROUP_DM_CHANNEL: 'User is not in the channel.', - DELETE_GROUP_DM_CHANNEL: "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", @@ -169,60 +157,28 @@ const Messages = { COMMAND_INTERACTION_OPTION_NO_SUB_COMMAND_GROUP: 'No subcommand group specified for interaction.', AUTOCOMPLETE_INTERACTION_OPTION_NO_FOCUSED_OPTION: 'No focused option for autocomplete interaction.', + MODAL_SUBMIT_INTERACTION_FIELD_NOT_FOUND: customId => `Required field with custom id "${customId}" not found.`, + MODAL_SUBMIT_INTERACTION_FIELD_TYPE: (customId, type, expected) => + `Field with custom id "${customId}" is of type: ${type}; expected ${expected}.`, + INVITE_MISSING_SCOPES: 'At least one valid scope must be provided for the invite', NOT_IMPLEMENTED: (what, name) => `Method ${what} not implemented on ${name}.`, SWEEP_FILTER_RETURN: 'The return value of the sweepFilter function was not false or a Function', - INVALID_BOT_METHOD: 'Bot accounts cannot use this method', - INVALID_USER_METHOD: 'User accounts cannot use this method', - BOT_ONLY: 'This method only for bots', - USER_ONLY: 'This method only for users', - - INTERACTION_SEND_FAILURE: msg => `${msg}`, - - INVALID_LOCALE: 'Unable to select this location', - FOLDER_NOT_FOUND: 'Server directory not found', - FOLDER_POSITION_INVALID: 'The server index in the directory is invalid', - APPLICATION_ID_INVALID: "The application isn't BOT", - INVALID_NITRO: 'Invalid Nitro Code', - MESSAGE_ID_NOT_FOUND: 'Message ID not found', - MESSAGE_EMBED_LINK_LENGTH: 'Message content with embed link length is too long', - GUILD_MEMBERS_FETCH: msg => `${msg}`, - USER_NOT_STREAMING: 'User is not streaming', - // Djs v13.7 - TEXT_INPUT_CUSTOM_ID: 'TextInputComponent customId must be a string', - TEXT_INPUT_LABEL: 'TextInputComponent label must be a string', - TEXT_INPUT_PLACEHOLDER: 'TextInputComponent placeholder must be a string', - TEXT_INPUT_VALUE: 'TextInputComponent value must be a string', - - MODAL_CUSTOM_ID: 'Modal customId must be a string', - MODAL_TITLE: 'Modal title must be a string', - - MODAL_SUBMIT_INTERACTION_FIELD_NOT_FOUND: customId => `Required field with custom id "${customId}" not found.`, - MODAL_SUBMIT_INTERACTION_FIELD_TYPE: (customId, type, expected) => - `Field with custom id "${customId}" is of type: ${type}; expected ${expected}.`, - - INVALID_REMOTE_AUTH_URL: 'Invalid remote auth URL (https://discord.com/ra/{hash})', - INVALID_URL: url => - `Invalid URL: ${url}.\nMake sure you are using a valid URL (https://discord.com/oauth2/authorize?...)`, - - NITRO_REQUIRED: 'This feature is only available for Nitro users.', - NITRO_BOOST_REQUIRED: feature => `This feature (${feature}) is only available for Nitro Boost users.`, - ONLY_ME: 'This feature is only available for self.', - MISSING_CAPTCHA_SERVICE: 'This feature is only available for enabled captcha handler.', - GUILD_FORUM_MESSAGE_REQUIRED: 'You must provide a message to create a guild forum thread', - NORMAL_LOGIN: 'Username and password are required for normal login', - LOGIN_FAILED_UNKNOWN: 'Login failed', - LOGIN_FAILED_2FA: 'Login failed, 2FA code is required', - GUILD_IS_LARGE: 'This guild is too large to fetch all members with this method', - TEAM_MEMBER_FORMAT: 'The member provided is either not real or not of the User class', - - MISSING_MODULE: (name, installCommand) => - `The module "${name}" is missing. Please install it with "${installCommand}" and try again.`, + // Selfbot + INVALID_USER_API: 'User accounts cannot use this endpoint', + INVALID_COMMAND_NAME: allCMD => `Could not parse subGroupCommand and subCommand due to too long: ${allCMD.join(' ')}`, + INVALID_SLASH_COMMAND_CHOICES: (parentOptions, value) => + `${value} is not a valid choice for this option (${parentOptions})`, + SLASH_COMMAND_REQUIRED_OPTIONS_MISSING: (req, opt) => `Value required (${req}) missing (Options: ${opt})`, + SLASH_COMMAND_SUB_COMMAND_GROUP_INVALID: n => `${n} is not a valid sub command group`, + SLASH_COMMAND_SUB_COMMAND_INVALID: n => `${n} is not a valid sub command`, + INTERACTION_FAILED: 'No responsed from Application Command', + USER_NOT_STREAMING: 'User is not streaming', }; for (const [name, message] of Object.entries(Messages)) register(name, message); diff --git a/src/index.js b/src/index.js index 7edd79e..c3ac8c4 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,5 @@ 'use strict'; -const tls = require('tls'); -// Cipher -tls.DEFAULT_CIPHERS = tls.DEFAULT_CIPHERS.split(':') - .sort(() => Math.random() - 0.5) - .join(':'); - // "Root" classes (starting points) exports.BaseClient = require('./client/BaseClient'); exports.Client = require('./client/Client'); @@ -15,7 +9,6 @@ exports.ShardingManager = require('./sharding/ShardingManager'); exports.WebhookClient = require('./client/WebhookClient'); // Utilities -exports.DiscordRPCServer = require('./util/arRPC/index'); exports.ActivityFlags = require('./util/ActivityFlags'); exports.ApplicationFlags = require('./util/ApplicationFlags'); exports.AttachmentFlags = require('./util/AttachmentFlags'); @@ -42,8 +35,6 @@ exports.ThreadMemberFlags = require('./util/ThreadMemberFlags'); exports.UserFlags = require('./util/UserFlags'); exports.Util = require('./util/Util'); exports.version = require('../package.json').version; -exports.DiscordAuthWebsocket = require('./util/RemoteAuth'); -exports.PurchasedFlags = require('./util/PurchasedFlags'); // Managers exports.ApplicationCommandManager = require('./managers/ApplicationCommandManager'); @@ -71,7 +62,6 @@ exports.PresenceManager = require('./managers/PresenceManager'); exports.ReactionManager = require('./managers/ReactionManager'); exports.ReactionUserManager = require('./managers/ReactionUserManager'); exports.RoleManager = require('./managers/RoleManager'); -exports.SessionManager = require('./managers/SessionManager'); exports.StageInstanceManager = require('./managers/StageInstanceManager'); exports.ThreadManager = require('./managers/ThreadManager'); exports.ThreadMemberManager = require('./managers/ThreadMemberManager'); @@ -80,6 +70,7 @@ exports.VoiceStateManager = require('./managers/VoiceStateManager'); exports.WebSocketManager = require('./client/websocket/WebSocketManager'); exports.WebSocketShard = require('./client/websocket/WebSocketShard'); exports.RelationshipManager = require('./managers/RelationshipManager'); +exports.UserNoteManager = require('./managers/UserNoteManager'); // Structures exports.Activity = require('./structures/Presence').Activity; @@ -88,26 +79,18 @@ exports.Application = require('./structures/interfaces/Application'); exports.ApplicationCommand = require('./structures/ApplicationCommand'); exports.ApplicationRoleConnectionMetadata = require('./structures/ApplicationRoleConnectionMetadata').ApplicationRoleConnectionMetadata; -exports.AutocompleteInteraction = require('./structures/AutocompleteInteraction'); exports.AutoModerationActionExecution = require('./structures/AutoModerationActionExecution'); exports.AutoModerationRule = require('./structures/AutoModerationRule'); exports.Base = require('./structures/Base'); -exports.BaseCommandInteraction = require('./structures/BaseCommandInteraction'); exports.BaseGuild = require('./structures/BaseGuild'); exports.BaseGuildEmoji = require('./structures/BaseGuildEmoji'); exports.BaseGuildTextChannel = require('./structures/BaseGuildTextChannel'); exports.BaseGuildVoiceChannel = require('./structures/BaseGuildVoiceChannel'); -exports.BaseMessageComponent = require('./structures/BaseMessageComponent'); -exports.ButtonInteraction = require('./structures/ButtonInteraction'); exports.CategoryChannel = require('./structures/CategoryChannel'); exports.Channel = require('./structures/Channel').Channel; -exports.ClientApplication = require('./structures/ClientApplication'); exports.ClientPresence = require('./structures/ClientPresence'); exports.ClientUser = require('./structures/ClientUser'); exports.Collector = require('./structures/interfaces/Collector'); -exports.CommandInteraction = require('./structures/CommandInteraction'); -exports.CommandInteractionOptionResolver = require('./structures/CommandInteractionOptionResolver'); -exports.ContextMenuInteraction = require('./structures/ContextMenuInteraction'); exports.DMChannel = require('./structures/DMChannel'); exports.Emoji = require('./structures/Emoji').Emoji; exports.Guild = require('./structures/Guild').Guild; @@ -123,49 +106,27 @@ exports.GuildScheduledEvent = require('./structures/GuildScheduledEvent').GuildS exports.GuildTemplate = require('./structures/GuildTemplate'); exports.Integration = require('./structures/Integration'); exports.IntegrationApplication = require('./structures/IntegrationApplication'); -exports.Interaction = require('./structures/Interaction'); -exports.InteractionCollector = require('./structures/InteractionCollector'); -exports.InteractionWebhook = require('./structures/InteractionWebhook'); exports.Invite = require('./structures/Invite'); exports.InviteStageInstance = require('./structures/InviteStageInstance'); exports.InviteGuild = require('./structures/InviteGuild'); exports.Message = require('./structures/Message').Message; exports.MessageActionRow = require('./structures/MessageActionRow'); exports.MessageAttachment = require('./structures/MessageAttachment'); -exports.MessageButton = require('./structures/MessageButton'); exports.MessageCollector = require('./structures/MessageCollector'); -exports.MessageComponentInteraction = require('./structures/MessageComponentInteraction'); -exports.MessageContextMenuInteraction = require('./structures/MessageContextMenuInteraction'); exports.MessageEmbed = require('./structures/MessageEmbed'); -exports.WebEmbed = require('./structures/WebEmbed'); exports.MessageMentions = require('./structures/MessageMentions'); exports.MessagePayload = require('./structures/MessagePayload'); exports.MessageReaction = require('./structures/MessageReaction'); -exports.MessageSelectMenu = require('./structures/MessageSelectMenu'); exports.Modal = require('./structures/Modal'); -exports.ModalSubmitInteraction = require('./structures/ModalSubmitInteraction'); exports.NewsChannel = require('./structures/NewsChannel'); exports.OAuth2Guild = require('./structures/OAuth2Guild'); -exports.PartialGroupDMChannel = require('./structures/PartialGroupDMChannel'); +exports.GroupDMChannel = require('./structures/GroupDMChannel'); exports.PermissionOverwrites = require('./structures/PermissionOverwrites'); exports.Presence = require('./structures/Presence').Presence; exports.ReactionCollector = require('./structures/ReactionCollector'); exports.ReactionEmoji = require('./structures/ReactionEmoji'); exports.RichPresenceAssets = require('./structures/Presence').RichPresenceAssets; exports.Role = require('./structures/Role').Role; -exports.Session = require('./structures/Session'); -// RPC -exports.getUUID = require('./structures/RichPresence').getUUID; -exports.CustomStatus = require('./structures/RichPresence').CustomStatus; -exports.RichPresence = require('./structures/RichPresence').RichPresence; -exports.SpotifyRPC = require('./structures/RichPresence').SpotifyRPC; -// SelectMenu -exports.ChannelSelectInteraction = require('./structures/SelectMenuInteraction').ChannelSelectInteraction; -exports.MentionableSelectInteraction = require('./structures/SelectMenuInteraction').MentionableSelectInteraction; -exports.RoleSelectInteraction = require('./structures/SelectMenuInteraction').RoleSelectInteraction; -exports.SelectMenuInteraction = require('./structures/SelectMenuInteraction').SelectMenuInteraction; -exports.UserSelectInteraction = require('./structures/SelectMenuInteraction').UserSelectInteraction; -// exports.StageChannel = require('./structures/StageChannel'); exports.StageInstance = require('./structures/StageInstance').StageInstance; exports.Sticker = require('./structures/Sticker').Sticker; @@ -179,7 +140,6 @@ exports.ThreadChannel = require('./structures/ThreadChannel'); exports.ThreadMember = require('./structures/ThreadMember'); exports.Typing = require('./structures/Typing'); exports.User = require('./structures/User'); -exports.UserContextMenuInteraction = require('./structures/UserContextMenuInteraction'); exports.VoiceChannel = require('./structures/VoiceChannel'); exports.VoiceRegion = require('./structures/VoiceRegion'); exports.VoiceState = require('./structures/VoiceState'); @@ -188,7 +148,12 @@ exports.Widget = require('./structures/Widget'); exports.WidgetMember = require('./structures/WidgetMember'); exports.WelcomeChannel = require('./structures/WelcomeChannel'); exports.WelcomeScreen = require('./structures/WelcomeScreen'); + exports.WebSocket = require('./WebSocket'); -// DiscordJSVoice Patch -exports.DiscordJSVoice = require('./util/Voice'); +exports.CustomStatus = require('./structures/RichPresence').CustomStatus; +exports.RichPresence = require('./structures/RichPresence').RichPresence; +exports.SpotifyRPC = require('./structures/RichPresence').SpotifyRPC; +exports.WebEmbed = require('./structures/WebEmbed'); +exports.DiscordAuthWebsocket = require('./util/RemoteAuth'); +exports.PurchasedFlags = require('./util/PurchasedFlags'); diff --git a/src/managers/ClientUserSettingManager.js b/src/managers/ClientUserSettingManager.js deleted file mode 100644 index e49959c..00000000 --- a/src/managers/ClientUserSettingManager.js +++ /dev/null @@ -1,490 +0,0 @@ -'use strict'; - -const { Collection } = require('@discordjs/collection'); -const BaseManager = require('./BaseManager'); -const GuildFolderManager = require('./GuildFolderManager'); -const { Error, TypeError } = require('../errors/DJSError'); -const GuildFolder = require('../structures/GuildFolder'); -const { CustomStatus } = require('../structures/RichPresence'); -const { localeSetting, DMScanLevel, stickerAnimationMode } = require('../util/Constants'); -/** - * Manages API methods for users and stores their cache. - * @extends {BaseManager} - * @see {@link https://luna.gitlab.io/discord-unofficial-docs/user_settings.html} - */ -class ClientUserSettingManager extends BaseManager { - constructor(client) { - super(client); - /** - * Raw data - * @type {Object} - */ - this.rawSetting = {}; - /** - * Language - * @type {?string} - */ - this.locale = null; - /** - * From: Setting => ACTIVITY SETTINGS => Activity Status => Display current activity as a status message - * @type {?boolean} - */ - this.activityDisplay = null; - /** - * Disable Direct Message from servers - * @type {Collection} - */ - this.disableDMfromServer = new Collection(); - /** - * Allow direct messages from server members - * @type {?boolean} - */ - this.DMfromServerMode = null; - /** - * Display images - * @type {?boolean} - */ - this.displayImage = null; - /** - * Display linked images - * @type {?boolean} - */ - this.linkedImageDisplay = null; - /** - * From: Setting => APP SETTINGS => Accessibility => Automatically play GIFs when Discord is focused. - * @type {?boolean} - */ - this.autoplayGIF = null; - /** - * Show embeds and preview website links pasted into chat - * @type {?boolean} - */ - this.previewLink = null; - /** - * From: Setting => APP SETTINGS => Accessibility => Play Animated Emojis - * @type {?boolean} - */ - this.animatedEmojis = null; - /** - * From: Setting => APP SETTINGS => Accessibility => Text-to-speech => Allow playback - * @type {?boolean} - */ - this.allowTTS = null; - /** - * From: Setting => APP SETTINGS => Appearance => Message Display => Compact Mode - * @type {?boolean} - */ - this.compactMode = null; - /** - * From: Setting => APP SETTINGS => Text & Images => Emoji => Convert Emoticons - * @type {?boolean} - */ - this.convertEmoticons = null; - /** - * SAFE DIRECT MESSAGING - * @type {?DMScanLevel} - */ - this.DMScanLevel = null; - /** - * From: Setting => APP SETTINGS => Appearance => Theme - * @type {'dark' | 'light' | null} - */ - this.theme = ''; - /** - * Developer Mode (Copy ID, etc.) - * @type {?boolean} - */ - this.developerMode = null; - /** - * AFK timeout (receives notifications) - * @type {?number} - */ - this.afkTimeout = null; - /** - * Sticker animation mode - * @type {?stickerAnimationMode} - */ - this.stickerAnimationMode = null; - /** - * WHO CAN ADD YOU AS A FRIEND ? - * @type {?object} - * @see {@link https://luna.gitlab.io/discord-unofficial-docs/user_settings.html#friend-source-flags-structure} - */ - this.addFriendFrom = { - all: null, - mutual_friends: null, - mutual_guilds: null, - }; - /** - * From: Setting => APP SETTINGS => Text & Images => Emoji => Show emoji reactions - * @type {?boolean} - */ - this.showEmojiReactions = null; - /** - * Custom Stauts - * @type {?object} - * @see {@link https://luna.gitlab.io/discord-unofficial-docs/custom_status.html#customstatus-structure} - */ - this.customStatus = null; - /** - * Guild folder and position - * @type {GuildFolderManager} - */ - this.guildFolder = new GuildFolderManager(client); - // Todo: add new method from Discum - } - /** - * Patch data file - * https://github.com/Merubokkusu/Discord-S.C.U.M/blob/master/discum/user/user.py - * @private - * @param {Object} data Raw Data to patch - */ - _patch(data = {}) { - this.rawSetting = Object.assign(this.rawSetting, data); - if ('locale' in data) { - this.locale = localeSetting[data.locale]; - } - if ('show_current_game' in data) { - this.activityDisplay = data.show_current_game; - } - if ('default_guilds_restricted' in data) { - this.DMfromServerMode = data.default_guilds_restricted; - } - if ('inline_attachment_media' in data) { - this.displayImage = data.inline_attachment_media; - } - if ('inline_embed_media' in data) { - this.linkedImageDisplay = data.inline_embed_media; - } - if ('gif_auto_play' in data) { - this.autoplayGIF = data.gif_auto_play; - } - if ('render_embeds' in data) { - this.previewLink = data.render_embeds; - } - if ('animate_emoji' in data) { - this.animatedEmojis = data.animate_emoji; - } - if ('enable_tts_command' in data) { - this.allowTTS = data.enable_tts_command; - } - if ('message_display_compact' in data) { - this.compactMode = data.message_display_compact; - } - if ('convert_emoticons' in data) { - this.convertEmoticons = data.convert_emoticons; - } - if ('explicit_content_filter' in data) { - this.DMScanLevel = DMScanLevel[data.explicit_content_filter]; - } - if ('theme' in data) { - this.theme = data.theme; - } - if ('developer_mode' in data) { - this.developerMode = data.developer_mode; - } - if ('afk_timeout' in data) { - this.afkTimeout = data.afk_timeout * 1000; // Second => milisecond - } - if ('animate_stickers' in data) { - this.stickerAnimationMode = stickerAnimationMode[data.animate_stickers]; - } - if ('render_reactions' in data) { - this.showEmojiReactions = data.render_reactions; - } - if ('custom_status' in data) { - this.customStatus = data.custom_status || {}; // Thanks PinkDuwc._#3443 reported this issue - this.customStatus.status = data.status; - } - if ('friend_source_flags' in data) { - this.addFriendFrom = { - all: data.friend_source_flags.all || false, - 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, - }; - } - if ('guild_folders' in data) { - data.guild_folders.map((folder, index) => - this.guildFolder.cache.set(index, new GuildFolder(this.client, folder)), - ); - } - if ('restricted_guilds' in data) { - this.disableDMfromServer = new Collection(data.restricted_guilds.map(guildId => [guildId, true])); - } - } - async fetch() { - if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); - const data = await this.client.api.users('@me').settings.get(); - this._patch(data); - return this; - } - /** - * Edit data - * @param {Object} data Data to edit - * @private - */ - async edit(data) { - if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); - const res = await this.client.api.users('@me').settings.patch({ data }); - this._patch(res); - return this; - } - /** - * Set compact mode - * @param {boolean | null} value Compact mode enable or disable - * @returns {boolean} - */ - async setDisplayCompactMode(value) { - if (typeof value !== 'boolean' && value !== null) { - throw new TypeError('INVALID_TYPE', 'value', 'boolean | null', true); - } - if (!value) value = !this.compactMode; - if (value !== this.compactMode) { - await this.edit({ message_display_compact: value }); - } - return this.compactMode; - } - /** - * Discord Theme - * @param {null |dark |light} value Theme to set - * @returns {theme} - */ - async setTheme(value) { - const validValues = ['dark', 'light']; - if (typeof value !== 'string' && value !== null) { - throw new TypeError('INVALID_TYPE', 'value', 'string | null', true); - } - if (!validValues.includes(value)) { - if (value == validValues[0]) value = validValues[1]; - else value = validValues[0]; - } - if (value !== this.theme) { - await this.edit({ theme: value }); - } - return this.theme; - } - - /** - * CustomStatus Object - * @typedef {Object} CustomStatusOption - * @property {string | null} text Text to set - * @property {string | null} status The status to set: 'online', 'idle', 'dnd', 'invisible' or null. - * @property {EmojiResolvable | null} emoji UnicodeEmoji, DiscordEmoji, or null. - * @property {number | null} expires The number of seconds until the status expires, or null. - */ - - /** - * Set custom status - * @param {?CustomStatus | CustomStatusOption} options CustomStatus - */ - setCustomStatus(options) { - if (typeof options !== 'object') { - this.edit({ custom_status: null }); - } else if (options instanceof CustomStatus) { - options = options.toJSON(); - let data = { - emoji_name: null, - expires_at: null, - text: null, - }; - if (typeof options.state === 'string') { - data.text = options.state; - } - if (options.emoji) { - if (options.emoji?.id) { - data.emoji_name = options.emoji?.name; - data.emoji_id = options.emoji?.id; - } else { - data.emoji_name = typeof options.emoji?.name === 'string' ? options.emoji?.name : null; - } - } - this.edit({ custom_status: data }); - } else { - let data = { - emoji_name: null, - expires_at: null, - text: null, - }; - if (typeof options.text === 'string') { - if (options.text.length > 128) { - throw new RangeError('[INVALID_VALUE] Custom status text must be less than 128 characters'); - } - data.text = options.text; - } - if (options.emoji) { - const emoji = this.client.emojis.resolve(options.emoji); - if (emoji) { - data.emoji_name = emoji.name; - data.emoji_id = emoji.id; - } else { - data.emoji_name = typeof options.emoji === 'string' ? options.emoji : null; - } - } - if (typeof options.expires === 'number') { - if (options.expires < Date.now()) { - throw new RangeError(`[INVALID_VALUE] Custom status expiration must be greater than ${Date.now()}`); - } - data.expires_at = new Date(options.expires).toISOString(); - } - if (['online', 'idle', 'dnd', 'invisible'].includes(options.status)) this.edit({ status: options.status }); - this.edit({ custom_status: data }); - } - } - - /** - * * Locale Setting, must be one of: - * * `DANISH` - * * `GERMAN` - * * `ENGLISH_UK` - * * `ENGLISH_US` - * * `SPANISH` - * * `FRENCH` - * * `CROATIAN` - * * `ITALIAN` - * * `LITHUANIAN` - * * `HUNGARIAN` - * * `DUTCH` - * * `NORWEGIAN` - * * `POLISH` - * * `BRAZILIAN_PORTUGUESE` - * * `ROMANIA_ROMANIAN` - * * `FINNISH` - * * `SWEDISH` - * * `VIETNAMESE` - * * `TURKISH` - * * `CZECH` - * * `GREEK` - * * `BULGARIAN` - * * `RUSSIAN` - * * `UKRAINIAN` - * * `HINDI` - * * `THAI` - * * `CHINA_CHINESE` - * * `JAPANESE` - * * `TAIWAN_CHINESE` - * * `KOREAN` - * @param {localeSetting} value Locale to set - * @returns {locale} - */ - async setLocale(value) { - if (typeof value !== 'string') { - throw new TypeError('INVALID_TYPE', 'value', 'string', true); - } - if (!localeSetting[value]) throw new Error('INVALID_LOCALE'); - if (localeSetting[value] !== this.locale) { - await this.edit({ locale: localeSetting[value] }); - } - return this.locale; - } - // TODO: Guild positions & folders - // Change Index in Array [Hidden] - /** - * - * @param {Array} array Array - * @param {number} from Index1 - * @param {number} to Index2 - * @returns {Array} - * @private - */ - _move(array, from, to) { - array.splice(to, 0, array.splice(from, 1)[0]); - return array; - } - // TODO: Move Guild - // folder to folder - // folder to home - // home to home - // home to folder - /** - * Change Guild Position (from * to Folder or Home) - * @param {GuildIDResolve} guildId guild.id - * @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 - * * `FOLDER`: 1 - * * `HOME`: 2 - * @param {FolderID} folderId If you want to move to folder - * @private - */ - guildChangePosition(guildId, newPosition, type, folderId) { - // Get Guild default position - // Escape - const oldGuildFolderPosition = this.rawSetting.guild_folders.findIndex(value => value.guild_ids.includes(guildId)); - const newGuildFolderPosition = this.rawSetting.guild_folders.findIndex(value => - value.guild_ids.includes(this.rawSetting.guild_positions[newPosition]), - ); - if (type == 2 || `${type}`.toUpperCase() == 'HOME') { - // Delete GuildID from Folder and create new Folder - // Check it is folder - const folder = this.rawSetting.guild_folders[oldGuildFolderPosition]; - if (folder.id) { - this.rawSetting.guild_folders[oldGuildFolderPosition].guild_ids = this.rawSetting.guild_folders[ - oldGuildFolderPosition - ].guild_ids.filter(v => v !== guildId); - } - this.rawSetting.guild_folders = this._move( - this.rawSetting.guild_folders, - 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 }); - } - - /** - * Restricted guilds setting - * @param {boolean} status Restricted status - * @returns {Promise} - */ - restrictedGuilds(status) { - if (typeof status !== 'boolean') { - throw new TypeError('INVALID_TYPE', 'status', 'boolean', true); - } - return this.edit({ - default_guilds_restricted: status, - restricted_guilds: status ? this.client.guilds.cache.map(v => v.id) : [], - }); - } - /** - * Add a guild to the list of restricted guilds. - * @param {GuildIDResolve} guildId The guild to add - * @returns {Promise} - */ - addRestrictedGuild(guildId) { - const temp = Object.assign( - [], - this.disableDMfromServer.map((v, k) => k), - ); - if (temp.includes(guildId)) throw new Error('Guild is already restricted'); - temp.push(guildId); - return this.edit({ restricted_guilds: temp }); - } - - /** - * Remove a guild from the list of restricted guilds. - * @param {GuildIDResolve} guildId The guild to remove - * @returns {Promise} - */ - removeRestrictedGuild(guildId) { - if (!this.disableDMfromServer.delete(guildId)) throw new Error('Guild is already restricted'); - return this.edit({ restricted_guilds: this.disableDMfromServer.map((v, k) => k) }); - } -} - -module.exports = ClientUserSettingManager; diff --git a/src/managers/DeveloperPortalManager.js b/src/managers/DeveloperPortalManager.js deleted file mode 100644 index a907db0..00000000 --- a/src/managers/DeveloperPortalManager.js +++ /dev/null @@ -1,104 +0,0 @@ -'use strict'; - -const { Collection } = require('@discordjs/collection'); -const BaseManager = require('./BaseManager'); -const DeveloperPortalApplication = require('../structures/DeveloperPortalApplication'); -const Team = require('../structures/Team'); - -/** - * Manages API methods for users and stores their cache. - * @extends {BaseManager} - */ -class DeveloperPortalManager extends BaseManager { - constructor(client) { - super(client); - /** - * A collection of all the applications the client has. - * @type {Collection} - * @readonly - */ - this.applications = new Collection(); - /** - * A collection of all the teams the client has. - * @type {Collection} - * @readonly - */ - this.teams = new Collection(); // Collection - } - /** - * Fetches all the applications & teams the client has. - * @returns {Promise} - */ - async fetch() { - const promise1 = this.client.api.applications.get({ - query: { - with_team_applications: true, - }, - }); - const promise2 = this.client.api.teams.get(); - const [applications, teams] = await Promise.all([promise1, promise2]); - for (const team of teams) { - this.teams.set(team.id, new Team(this.client, team)); - } - for (const application of applications) { - this.applications.set(application.id, new DeveloperPortalApplication(this.client, application)); - } - return this; - } - - /** - * Creates a new Team. - * @param {string} name Name of the team - * @returns {Promise} - */ - async createTeam(name) { - const team = await this.client.api.teams.post({ - data: { - name: name, - }, - }); - - this.teams.set(team.id, new Team(this.client, team)); - return this.teams.get(team.id); - } - - /** - * Creates a new application. - * @param {string} name Name of the application - * @param {?Snowflake | Team} teamId The team to create the application in - * @returns {Promise} - */ - async createApplication(name, teamId = null) { - teamId = teamId instanceof Team ? teamId.id : teamId; - const application = await this.client.api.applications.post({ - data: { - name, - team_id: teamId, - }, - }); - this.applications.set(application.id, new DeveloperPortalApplication(this.client, application)); - return this.applications.get(application.id); - } - - /** - * Deletes an application. - * @param {Snowflake} id Application ID - * @param {?number} MFACode 2FA code (if 2FA is enabled) - * @returns {Promise} - */ - async deleteApplication(id, MFACode) { - if (MFACode) { - await this.client.api.applications[`${id}/delete`].post({ - query: { - code: MFACode, - }, - }); - } else { - await this.client.api.applications[`${id}/delete`].post(); - } - this.applications.delete(id); - return undefined; - } -} - -module.exports = DeveloperPortalManager; diff --git a/src/managers/GuildFolderManager.js b/src/managers/GuildFolderManager.js deleted file mode 100644 index a5c98b4..00000000 --- a/src/managers/GuildFolderManager.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict'; - -const { Collection } = require('@discordjs/collection'); -const BaseManager = require('./BaseManager'); - -/** - * Manages API methods for users and stores their cache. - * @extends {BaseManager} - */ -class GuildFolderManager extends BaseManager { - constructor(client) { - super(client); - /** - * The guild folder cache (Index, GuildFolder) - * @type {Collection} - */ - this.cache = new Collection(); - } - _refresh() { - this.cache.clear(); - } -} - -module.exports = GuildFolderManager; diff --git a/src/managers/GuildSettingManager.js b/src/managers/GuildSettingManager.js deleted file mode 100644 index dd0131d..00000000 --- a/src/managers/GuildSettingManager.js +++ /dev/null @@ -1,148 +0,0 @@ -'use strict'; - -const BaseManager = require('./BaseManager'); -/** - * Manages API methods for users and stores their cache. - * @extends {BaseManager} - * @see {@link https://luna.gitlab.io/discord-unofficial-docs/user_settings.html} - */ -class GuildSettingManager extends BaseManager { - constructor(client, guildId = null) { - super(client); - /** - * Raw data - * @type {Object} - */ - this.rawSetting = {}; - /** - * Guild Id - * @type {?Snowflake} - */ - this.guildId = guildId; - } - /** - * Get the guild - * @type {?Guild} - * @readonly - */ - get guild() { - return this.client.guilds.cache.get(this.guildId); - } - /** - * Patch data file - * @private - * @param {Object} data Raw Data to patch - */ - _patch(data = {}) { - this.rawSetting = Object.assign(this.rawSetting, data); - if ('suppress_everyone' in data) { - /** - * Notification setting > Suppress `@everyone` and `@here` - * @type {?boolean} - */ - this.suppressEveryone = data.suppress_everyone; - } - if ('suppress_roles' in data) { - /** - * Notification setting > Suppress all role `@mention` - * @type {?boolean} - */ - this.suppressRoles = data.suppress_roles; - } - if ('mute_scheduled_events' in data) { - /** - * Notification setting > Mute new events - * @type {?boolean} - */ - this.muteScheduledEvents = data.mute_scheduled_events; - } - if ('message_notifications' in data) { - /** - * Notification setting > Message notifications - * * `0` = All messages - * * `1` = Only @mentions - * * `2` = Nothing - * @type {?number} - */ - this.messageNotifications = data.message_notifications; - } - if ('flags' in data) { - /** - * Flags (unknown) - * @type {?number} - */ - this.flags = data.flags; - } - if ('mobile_push' in data) { - /** - * Notification setting > Mobile push notifications - * @type {?boolean} - */ - this.mobilePush = data.mobile_push; - } - if ('muted' in data) { - /** - * Mute server - * @type {?boolean} - */ - this.muted = data.muted; - } - if ('mute_config' in data && data.mute_config !== null) { - /** - * Mute config (muted = true) - * * `muteConfig.endTime`: End time (Date) - * * `muteConfig.selectedTimeWindow`: Selected time window (seconds) (number) - * @type {?Object} - */ - this.muteConfig = { - endTime: new Date(data.mute_config.end_time), - selectedTimeWindow: data.mute_config.selected_time_window, - }; - } else { - this.muteConfig = null; - } - if ('hide_muted_channels' in data) { - /** - * Hide muted channels - * @type {?boolean} - */ - this.hideMutedChannels = data.hide_muted_channels; - } - if ('channel_overrides' in data) { - /** - * Channel overrides (unknown) - * @type {?Array} - */ - this.channelOverrides = data.channel_overrides; - } - if ('notify_highlights' in data) { - /** - * Notification setting > Suppress highlights - * * `0` = ??? (unknown) - * * `1` = Enable - * * `2` = Disable - * @type {?number} - */ - this.notifyHighlights = data.notify_highlights; - } - if ('version' in data) { - /** - * Version (unknown) - * @type {?number} - */ - this.version = data.version; - } - } - /** - * Edit guild settings - * @param {Object} data Data to edit - * @returns {Promise} - */ - async edit(data) { - const data_ = await this.client.api.users('@me').settings.patch(data); - this._patch(data_); - return this; - } -} - -module.exports = GuildSettingManager; diff --git a/src/managers/SessionManager.js b/src/managers/SessionManager.js deleted file mode 100644 index 185a4e9..00000000 --- a/src/managers/SessionManager.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict'; - -const CachedManager = require('./CachedManager'); -const { Error } = require('../errors/DJSError'); -const Session = require('../structures/Session'); -/** - * Manages API methods for users and stores their cache. - * @extends {CachedManager} - */ -class SessionManager extends CachedManager { - constructor(client, iterable) { - super(client, Session, iterable); - } - /** - * The cache of Sessions - * @type {Collection} - * @name SessionManager#cache - */ - - /** - * Fetch all sessions of the client. - * @returns {Promise} - */ - fetch() { - return new Promise((resolve, reject) => { - this.client.api.auth.sessions - .get() - .then(data => { - const allData = data.user_sessions; - this.cache.clear(); - for (const session of allData) { - this._add(new Session(this.client, session), true, { id: session.id_hash }); - } - resolve(this); - }) - .catch(reject); - }); - } - - /** - * Logout the client (remote). - * @param {string | null} mfaCode MFA code (if 2FA is enabled) - * @returns {Promise} - */ - logoutAllDevices(mfaCode) { - if (typeof this.client.password !== 'string') throw new Error('REQUIRE_PASSWORD'); - return this.client.api.auth.sessions.logout({ - data: { - session_id_hashes: this.cache.map(session => session.id), - password: this.client.password, - code: typeof mfaCode === 'string' ? mfaCode : undefined, - }, - }); - } -} - -module.exports = SessionManager; diff --git a/src/structures/DeveloperPortalApplication.js b/src/structures/DeveloperPortalApplication.js deleted file mode 100644 index 7834d7c..00000000 --- a/src/structures/DeveloperPortalApplication.js +++ /dev/null @@ -1,520 +0,0 @@ -'use strict'; -const { Collection } = require('@discordjs/collection'); -const { ApplicationRoleConnectionMetadata } = require('./ApplicationRoleConnectionMetadata'); -const Base = require('./Base'); -const ApplicationFlags = require('../util/ApplicationFlags'); -const { ClientApplicationAssetTypes, Endpoints, ApplicationRoleConnectionMetadataTypes } = require('../util/Constants'); -const DataResolver = require('../util/DataResolver'); -const Permissions = require('../util/Permissions'); -const SnowflakeUtil = require('../util/SnowflakeUtil'); - -const AssetTypes = Object.keys(ClientApplicationAssetTypes); - -/** - * Represents an OAuth2 Application. - * @extends {Base} - * @abstract - */ -class DeveloperPortalApplication extends Base { - constructor(client, data) { - super(client); - this._patch(data); - } - _patch(data) { - /** - * The application's id - * @type {Snowflake} - */ - this.id = data.id; - - if ('name' in data) { - /** - * The name of the application - * @type {?string} - */ - this.name = data.name; - } else { - this.name ??= null; - } - - if ('description' in data) { - /** - * The application's description - * @type {?string} - */ - this.description = data.description; - } else { - this.description ??= null; - } - - if ('icon' in data) { - /** - * The application's icon hash - * @type {?string} - */ - this.icon = data.icon; - } else { - this.icon ??= null; - } - - if ('bot' in data) { - /** - * Bot application - * @type {User} - */ - this.bot = this.client.users._add(data.bot); - } - - /** - * The tags this application has (max of 5) - * @type {string[]} - */ - this.tags = data.tags ?? []; - - if ('install_params' in data) { - /** - * Settings for this application's default in-app authorization - * @type {?ClientApplicationInstallParams} - */ - this.installParams = { - scopes: data.install_params.scopes, - permissions: new Permissions(data.install_params.permissions).freeze(), - }; - } else { - this.installParams ??= null; - } - - if ('custom_install_url' in data) { - /** - * This application's custom installation URL - * @type {?string} - */ - this.customInstallURL = data.custom_install_url; - } else { - this.customInstallURL = null; - } - - if ('flags' in data) { - /** - * The flags this application has - * @type {ApplicationFlags} - */ - this.flags = new ApplicationFlags(data.flags).freeze(); - } - - if ('cover_image' in data) { - /** - * The hash of the application's cover image - * @type {?string} - */ - this.cover = data.cover_image; - } else { - this.cover ??= null; - } - - if ('rpc_origins' in data) { - /** - * The application's RPC origins, if enabled - * @type {string[]} - */ - this.rpcOrigins = data.rpc_origins; - } else { - this.rpcOrigins ??= []; - } - - if ('bot_require_code_grant' in data) { - /** - * If this application's bot requires a code grant when using the OAuth2 flow - * @type {?boolean} - */ - this.botRequireCodeGrant = data.bot_require_code_grant; - } else { - this.botRequireCodeGrant ??= null; - } - - if ('bot_public' in data) { - /** - * If this application's bot is public - * @type {?boolean} - */ - this.botPublic = data.bot_public; - } else { - this.botPublic ??= null; - } - - /** - * The owner of this OAuth application - * @type {?(User|Team)} - */ - this.owner = null; - if (data.owner.username == `team${data.owner.id}` && data.owner.discriminator == '0000') { - this.owner = this.client.developerPortal.teams.get(data.owner.id); - } else { - this.owner = data.owner ? this.client.users._add(data.owner) : this.owner ?? null; - } - - /** - * Redirect URIs for this application - * @type {Array} - */ - this.redirectURIs = data.redirect_uris ?? []; - - /** - * BOT_HTTP_INTERACTIONS feature flag - * @type {?string} - */ - this.interactionEndpointURL = data.interactions_endpoint_url ?? null; - - /** - * Public key - * @type {?string} - */ - this.publicKey = data.verify_key ?? null; - - /** - * @typedef {Object} Tester - * @property {number} state The state of the tester (2: Accepted, 1: Pending) - * @property {User} user The user that the tester is - */ - /** - * User tester - * @type {Collection} - */ - this.testers = new Collection(); // - - /** - * Terms of service URL - * @type {?string} - */ - this.TermsOfService = data.terms_of_service_url ?? null; - - /** - * Privacy policy URL - * @type {?string} - */ - this.PrivacyPolicy = data.privacy_policy_url ?? null; - - if ('role_connections_verification_url' in data) { - /** - * This application's role connection verification entry point URL - * @type {?string} - */ - this.roleConnectionsVerificationURL = data.role_connections_verification_url; - } else { - this.roleConnectionsVerificationURL ??= null; - } - } - /** - * The timestamp the application was created at - * @type {number} - * @readonly - */ - get createdTimestamp() { - return SnowflakeUtil.timestampFrom(this.id); - } - - /** - * The time the application was created at - * @type {Date} - * @readonly - */ - get createdAt() { - return new Date(this.createdTimestamp); - } - - /** - * A link to the application's icon. - * @param {StaticImageURLOptions} [options={}] Options for the Image URL - * @returns {?string} - */ - iconURL({ format, size } = {}) { - if (!this.icon) return null; - return this.client.rest.cdn.AppIcon(this.id, this.icon, { format, size }); - } - - /** - * A link to this application's cover image. - * @param {StaticImageURLOptions} [options={}] Options for the Image URL - * @returns {?string} - */ - coverURL({ format, size } = {}) { - if (!this.cover) return null; - return Endpoints.CDN(this.client.options.http.cdn).AppIcon(this.id, this.cover, { format, size }); - } - - /** - * Asset data. - * @typedef {Object} ApplicationAsset - * @property {Snowflake} id The asset's id - * @property {string} name The asset's name - * @property {string} type The asset's type - */ - - /** - * Gets the application's rich presence assets. - * @returns {Promise>} - * @deprecated This will be removed in the next major as it is unsupported functionality. - */ - async fetchAssets() { - const assets = await this.client.api.oauth2.applications(this.id).assets.get(); - return assets.map(a => ({ - id: a.id, - name: a.name, - type: AssetTypes[a.type - 1], - })); - } - - /** - * Whether this application is partial - * @type {boolean} - * @readonly - */ - get partial() { - return !this.name; - } - - /** - * Obtains this application from Discord. - * @returns {Promise} - */ - async fetch() { - const app = await this.client.api.applications[this.id].get(); - this._patch(app); - return this; - } - - /** - * Gets all testers for this application. - * @returns {Promise} - */ - async fetchTesters() { - const app = await this.client.api.applications[this.id].allowlist.get(); - this.testers = new Collection(); - for (const tester of app || []) { - this.testers.set(tester.user.id, { - state: tester.state, - user: this.client.users._add(tester.user), - }); - } - return this; - } - - /** - * Add user to this application's allowlist. - * @param {string} username Username of the user to add - * @param {string} discriminator Discriminator of the user to add - * @returns {Promise} - */ - async addTester(username, discriminator) { - const app = await this.client.api.applications[this.id].allowlist.post({ - data: { - username, - discriminator, - }, - }); - this.testers.set(app.user.id, { - state: app.state, - user: this.client.users._add(app.user), - }); - return this; - } - - /** - * Delete user from this application's allowlist. - * @param {UserResolvable} user User - * @returns {Promise} - */ - async deleteTester(user) { - const userId = this.client.users.resolveId(user); - await this.client.api.applications[this.id].allowlist[userId].delete(); - this.testers.delete(userId); - return this; - } - - /** - * The data for editing a application. - * @typedef {Object} ApplicationEditData - * @property {string} [name] The name of the app - * @property {string} [description] The description of the app - * @property {?(BufferResolvable|Base64Resolvable)} [icon] The icon of the app - * @property {?(BufferResolvable|Base64Resolvable)} [cover] The application's default rich presence invite - * @property {boolean} [botPublic] When false only app owner can join the app's bot to guilds - * @property {boolean} [botRequireCodeGrant] When true the app's bot will only join upon completion of the full oauth2 code grant flow - * @property {?string} [TermsOfService] ToS URL - * @property {?string} [PrivacyPolicy] Privacy policy URL - * @property {number} [flags] The application's public flags - * @property {Array} [redirectURIs] Redirect URIs (OAuth2 only) - * @property {Array} [tags] Up to 5 tags describing the content and functionality of the application - */ - /** - * Edits this application. - * @param {ApplicationEditData} data Edit data for the application - * @returns {Promise} - */ - async edit(data) { - const _data = {}; - if (data.name) _data.name = data.name; - if (typeof data.icon !== 'undefined') { - _data.icon = await DataResolver.resolveImage(data.icon); - } - if (data.description) _data.description = data.description; - if (typeof data.cover !== 'undefined') { - _data.cover = await DataResolver.resolveImage(data.cover); - } - if (data.botPublic) _data.bot_public = data.botPublic; - if (data.botRequireCodeGrant) _data.bot_require_code_grant = data.botRequireCodeGrant; - if (data.TermsOfService) _data.terms_of_service_url = data.TermsOfService; - if (data.PrivacyPolicy) _data.privacy_policy_url = data.PrivacyPolicy; - if (data.flags) _data.flags = data.flags; - if (data.redirectURIs) _data.redirect_uris = data.redirectURIs; - if (data.tags) _data.tags = data.tags; - // - const app = await this.client.api.applications[this.id].patch({ data: _data }); - this._patch(app); - return this; - } - - /** - * Creates a new bot for this application. - * @returns {Promise} - */ - async createBot() { - if (this.bot) throw new Error('Application already has a bot.'); - await this.client.api.applications[this.id].bot.post(); - const app = await this.fetch(); - return app; - } - - /** - * Reset CLient Secret for this application. - * @param {number} MFACode The MFA code (if required) - * @returns {Promise} - */ - async resetClientSecret(MFACode) { - const app = MFACode - ? await this.client.api.applications[this.id].reset.post({ - data: { - code: MFACode, - }, - }) - : await this.client.api.applications[this.id].reset.post(); - return app.secret; - } - - /** - * Reset Bot Token for this application. - * @param {number} MFACode The MFA code (if required) - * @returns {Promise} - */ - async resetBotToken(MFACode) { - const app = MFACode - ? await this.client.api.applications[this.id].bot.reset.post({ - data: { - code: MFACode, - }, - }) - : await this.client.api.applications[this.id].bot.reset.post(); - return app.token; - } - - /** - * Deletes this application. - * @param {number} MFACode The MFA code (if required) - * @returns {Promise} - */ - delete(MFACode) { - return this.client.developerPortal.deleteApplication(this.id, MFACode); - } - - /** - * Add new image to this application. (RPC) - * @param {BufferResolvable|Base64Resolvable} image Image Resolvable - * @param {string} name Name of the image - * @returns {ApplicationAsset} - */ - async addAsset(image, name) { - const data = await DataResolver.resolveImage(image); - const asset = await this.client.api.applications[this.id].assets.post({ - data: { - type: 1, - name, - image: data, - }, - }); - return { - id: asset.id, - name: asset.name, - type: AssetTypes[asset.type - 1], - }; - } - - /** - * Delete an image from this application. (RPC) - * @param {Snowflake} id ID of the image - * @returns {Promise} - */ - async deleteAsset(id) { - await this.client.api.applications[this.id].assets[id].delete(); - return undefined; - } - - /** - * Gets this application's role connection metadata records - * @returns {Promise} - */ - async fetchRoleConnectionMetadataRecords() { - const metadata = await this.client.api.applications(this.id)('role-connections').metadata.get(); - return metadata.map(data => new ApplicationRoleConnectionMetadata(data)); - } - - /** - * Data for creating or editing an application role connection metadata. - * @typedef {Object} ApplicationRoleConnectionMetadataEditOptions - * @property {string} name The name of the metadata field - * @property {?Object} [nameLocalizations] The name localizations for the metadata field - * @property {string} description The description of the metadata field - * @property {?Object} [descriptionLocalizations] The description localizations for the metadata field - * @property {string} key The dictionary key of the metadata field - * @property {ApplicationRoleConnectionMetadataType} type The type of the metadata field - */ - - /** - * Updates this application's role connection metadata records - * @param {ApplicationRoleConnectionMetadataEditOptions[]} records The new role connection metadata records - * @returns {Promise} - */ - async editRoleConnectionMetadataRecords(records) { - const newRecords = await this.client.api - .applications(this.client.user.id)('role-connections') - .metadata.put({ - data: records.map(record => ({ - type: typeof record.type === 'string' ? ApplicationRoleConnectionMetadataTypes[record.type] : record.type, - key: record.key, - name: record.name, - name_localizations: record.nameLocalizations, - description: record.description, - description_localizations: record.descriptionLocalizations, - })), - }); - - return newRecords.map(data => new ApplicationRoleConnectionMetadata(data)); - } - - /** - * When concatenated with a string, this automatically returns the application's name instead of the - * Application object. - * @returns {?string} - * @example - * // Logs: Application name: My App - * console.log(`Application name: ${application}`); - */ - toString() { - return this.name; - } - - toJSON() { - return super.toJSON({ createdTimestamp: true }); - } -} - -module.exports = DeveloperPortalApplication; diff --git a/src/structures/GuildFolder.js b/src/structures/GuildFolder.js deleted file mode 100644 index 7614ef9..00000000 --- a/src/structures/GuildFolder.js +++ /dev/null @@ -1,75 +0,0 @@ -'use strict'; - -const Base = require('./Base'); - -/** - * Guild Folder. - * @abstract - */ -class GuildFolder extends Base { - constructor(client, data) { - super(client); - this._patch(data); - } - _patch(data) { - if ('id' in data) { - /** - * The guild folder's id - * @type {Snowflake} - */ - this.id = data.id; - } - - if ('name' in data) { - /** - * The guild folder's name - * @type {string} - */ - this.name = data.name; - } - - if ('color' in data) { - /** - * The base 10 color of the folder - * @type {number} - */ - this.color = data.color; - } - - if ('guild_ids' in data) { - /** - * The guild folder's guild ids - * @type {Snowflake[]} - */ - this.guild_ids = data.guild_ids; - } - } - /** - * The hexadecimal version of the folder color, with a leading hashtag - * @type {string} - * @readonly - */ - get hexColor() { - return `#${this.color.toString(16).padStart(6, '0')}`; - } - - /** - * Guilds in the folder - * @type {Collection} - * @readonly - */ - get guilds() { - return this.client.guilds.cache.filter(guild => this.guild_ids.includes(guild.id)); - } - - toJSON() { - return { - id: this.id, - name: this.name, - color: this.color, - guild_ids: this.guild_ids, - }; - } -} - -module.exports = GuildFolder; diff --git a/src/structures/Session.js b/src/structures/Session.js deleted file mode 100644 index 1ec045e..00000000 --- a/src/structures/Session.js +++ /dev/null @@ -1,81 +0,0 @@ -'use strict'; - -const Base = require('./Base'); - -/** - * @typedef {Object} SessionClientInfo - * @property {string} location Location of the client (using IP address) - * @property {string} platform Platform of the client - * @property {string} os Operating system of the client - */ - -/** - * Represents a Client OAuth2 Application Team. - * @extends {Base} - */ -class Session extends Base { - constructor(client, data) { - super(client); - this._patch(data); - } - - _patch(data) { - if ('id_hash' in data) { - /** - * The session hash id - * @type {string} - */ - this.id = data.id_hash; - } - if ('approx_last_used_time' in data) { - this.approxLastUsedTime = data.approx_last_used_time; - } - if ('client_info' in data) { - /** - * The client info - * @type {SessionClientInfo} - */ - this.clientInfo = data.client_info; - } - } - - /** - * The timestamp the client was last used at. - * @type {number} - * @readonly - */ - get createdTimestamp() { - return this.createdAt.getTime(); - } - - /** - * The time the client was last used at. - * @type {Date} - * @readonly - */ - get createdAt() { - return new Date(this.approxLastUsedTime); - } - - /** - * Logout the client (remote). - * @param {string | null} mfaCode MFA code (if 2FA is enabled) - * @returns {Promise} - */ - logout(mfaCode) { - if (typeof this.client.password !== 'string') throw new Error('REQUIRE_PASSWORD', 'You must provide a password.'); - return this.client.api.auth.sessions.logout({ - data: { - session_id_hashes: [this.id], - password: this.client.password, - code: typeof mfaCode === 'string' ? mfaCode : undefined, - }, - }); - } - - toJSON() { - return super.toJSON(); - } -} - -module.exports = Session; diff --git a/src/util/RemoteAuth.js b/src/util/RemoteAuth.js index 5d6ca5f..18ad720 100644 --- a/src/util/RemoteAuth.js +++ b/src/util/RemoteAuth.js @@ -1,14 +1,13 @@ 'use strict'; -const { Buffer } = require('buffer'); -const crypto = require('crypto'); + +const { Buffer } = require('node:buffer'); +const crypto = require('node:crypto'); const EventEmitter = require('node:events'); +const { StringDecoder } = require('node:string_decoder'); const { setTimeout } = require('node:timers'); -const { StringDecoder } = require('string_decoder'); -const chalk = require('chalk'); const fetch = require('node-fetch'); -const { encode: urlsafe_b64encode } = require('safe-base64'); const WebSocket = require('ws'); -const { defaultUA } = require('./Constants'); +const { UserAgent } = require('./Constants'); const Options = require('./Options'); const defaultClientOptions = Options.createDefault(); @@ -22,9 +21,9 @@ const receiveEvent = { NONCE_PROOF: 'nonce_proof', PENDING_REMOTE_INIT: 'pending_remote_init', HEARTBEAT_ACK: 'heartbeat_ack', - PENDING_LOGIN: 'pending_ticket', + PENDING_TICKET: 'pending_ticket', CANCEL: 'cancel', - SUCCESS: 'pending_login', + PENDING_LOGIN: 'pending_login', }; const sendEvent = { @@ -37,266 +36,167 @@ const Event = { READY: 'ready', ERROR: 'error', CANCEL: 'cancel', - WAIT: 'pending', - SUCCESS: 'success', + WAIT_SCAN: 'pending', FINISH: 'finish', CLOSED: 'closed', + DEBUG: 'debug', }; /** - * @typedef {Object} DiscordAuthWebsocketOptions - * @property {?boolean} [debug=false] Log debug info - * @property {?boolean} [hiddenLog=false] Hide log ? - * @property {?boolean} [autoLogin=false] Automatically login (DiscordJS.Client Login) ? - * @property {?boolean} [failIfError=true] Throw error ? - * @property {?boolean} [generateQR=true] Create QR Code ? - * @property {?number} [apiVersion=9] API Version - * @property {?string} [userAgent] User Agent - * @property {?Object.} [wsProperties] Web Socket Properties - */ - -/** - * Discord Auth QR (Discord.RemoteAuth will be removed in the future, v13.9.0 release) + * Discord Auth QR * @extends {EventEmitter} * @abstract */ class DiscordAuthWebsocket extends EventEmitter { + #ws = null; + #heartbeatInterval = null; + #expire = null; + #publicKey = null; + #privateKey = null; + #ticket = null; + #fingerprint = ''; + #userDecryptString = ''; + /** * Creates a new DiscordAuthWebsocket instance. - * @param {?DiscordAuthWebsocketOptions} options Options */ - constructor(options) { + constructor() { super(); - /** - * WebSocket - * @type {?WebSocket} - */ - this.ws = null; - /** - * Heartbeat Interval - * @type {?number} - */ - this.heartbeatInterval = NaN; - this._expire = NaN; - this.key = null; - /** - * User (Scan QR Code) - * @type {?Object} - */ - this.user = null; - /** - * Temporary Token (Scan QR Code) - * @type {?string} - */ - this.token = undefined; - /** - * Real Token (Login) - * @type {?string} - */ - this.realToken = undefined; - /** - * Fingerprint (QR Code) - * @type {?string} - */ - this.fingerprint = null; - - /** - * Captcha Handler - * @type {Function} - * @param {Captcha} data hcaptcha data - * @returns {Promise} Captcha token - */ - // eslint-disable-next-line no-unused-vars - this.captchaSolver = data => - new Promise((resolve, reject) => { - reject( - new Error(` -Captcha Handler not found - Please set captchaSolver option -Example captchaSolver function: - -new DiscordAuthWebsocket({ - captchaSolver: async (data) => { - const token = await hcaptchaSolver(data.captcha_sitekey, 'discord.com'); - return token; + this.token = ''; } -}); -`), - ); - }); - - /** - * Captcha Cache - * @type {?Captcha} - */ - this.captchaCache = null; - - this._validateOptions(options); - - this.callFindRealTokenCount = 0; - } /** - * Get expire time - * @type {string} Expire time - * @readonly + * @type {string} */ - get exprireTime() { - return this._expire.toLocaleString('en-US'); + get AuthURL() { + return baseURL + this.#fingerprint; } - _validateOptions(options = {}) { - /** - * Options - * @type {?DiscordAuthWebsocketOptions} - */ - this.options = { - debug: false, - hiddenLog: false, - autoLogin: false, - failIfError: true, - generateQR: true, - apiVersion: 9, - userAgent: defaultUA, - wsProperties: defaultClientOptions.ws.properties, - captchaSolver: () => new Error('Captcha Handler not found. Please set captchaSolver option.'), - }; - if (typeof options == 'object') { - if (typeof options.debug == 'boolean') this.options.debug = options.debug; - if (typeof options.hiddenLog == 'boolean') this.options.hiddenLog = options.hiddenLog; - if (typeof options.autoLogin == 'boolean') this.options.autoLogin = options.autoLogin; - if (typeof options.failIfError == 'boolean') this.options.failIfError = options.failIfError; - if (typeof options.generateQR == 'boolean') this.options.generateQR = options.generateQR; - if (typeof options.apiVersion == 'number') this.options.apiVersion = options.apiVersion; - if (typeof options.userAgent == 'string') this.options.userAgent = options.userAgent; - if (typeof options.wsProperties == 'object') this.options.wsProperties = options.wsProperties; - if (typeof options.captchaSolver == 'function') this.captchaSolver = options.captchaSolver; - } + + /** + * @type {Date} + */ + get exprire() { + return this.#expire; } - _createWebSocket(url) { - this.ws = new WebSocket(url, { + + /** + * @type {UserRaw} + */ + get user() { + return DiscordAuthWebsocket.decryptUser(this.#userDecryptString); + } + + #createWebSocket(url) { + this.#ws = new WebSocket(url, { headers: { Origin: 'https://discord.com', - 'User-Agent': this.options.userAgent, + 'User-Agent': UserAgent, }, }); - this._handleWebSocket(); + this.#handleWebSocket(); } - _handleWebSocket() { - this.ws.on('error', error => { - this._logger('error', error); + + #handleWebSocket() { + this.#ws.on('error', error => { + /** + * WS Error + * @event DiscordAuthWebsocket#error + * @param {Error} error Error + */ + this.emit(Event.ERROR, error); }); - this.ws.on('open', () => { - this._logger('debug', 'Client Connected'); + this.#ws.on('open', () => { + /** + * Debug Event + * @event DiscordAuthWebsocket#debug + * @param {string} msg Debug msg + */ + this.emit(Event.DEBUG, '[WS] Client Connected'); }); - this.ws.on('close', () => { - this._logger('debug', 'Connection closed.'); - }); - this.ws.on('message', message => { - this._handleMessage(JSON.parse(message)); + this.#ws.on('close', () => { + this.emit(Event.DEBUG, '[WS] Connection closed'); }); + this.#ws.on('message', this.#handleMessage.bind(this)); } - _handleMessage(message) { + + #handleMessage(message) { + message = JSON.parse(message); switch (message.op) { case receiveEvent.HELLO: { - this._ready(message); + this.#ready(message); break; } + case receiveEvent.NONCE_PROOF: { - this._receiveNonceProof(message); + this.#receiveNonceProof(message); break; } + case receiveEvent.PENDING_REMOTE_INIT: { - this._pendingRemoteInit(message); - break; - } - case receiveEvent.HEARTBEAT_ACK: { - this._logger('debug', 'Heartbeat acknowledged.'); - this._heartbeatAck(); - break; - } - case receiveEvent.PENDING_LOGIN: { - this._pendingLogin(message); - break; - } - case receiveEvent.CANCEL: { - this._logger('debug', 'Cancel login.'); + this.#fingerprint = message.fingerprint; /** - * Emitted whenever a user cancels the login process. - * @event DiscordAuthWebsocket#cancel - * @param {object} user User (Raw) + * Ready Event + * @event DiscordAuthWebsocket#ready + * @param {DiscordAuthWebsocket} client WS */ - this.emit(Event.CANCEL, this.user); + this.emit(Event.READY, this); + break; + } + + case receiveEvent.HEARTBEAT_ACK: { + this.emit(Event.DEBUG, `Heartbeat acknowledged.`); + this.#heartbeatAck(); + break; + } + + case receiveEvent.PENDING_TICKET: { + this.#pendingLogin(message); + break; + } + + case receiveEvent.CANCEL: { + /** + * Cancel + * @event DiscordAuthWebsocket#cancel + * @param {DiscordAuthWebsocket} client WS + */ + this.emit(Event.CANCEL, this); this.destroy(); break; } - case receiveEvent.SUCCESS: { - this._logger('debug', 'Receive Token - Login Success.', message.ticket); - /** - * Emitted whenever a token is created. (Fake token) - * @event DiscordAuthWebsocket#success - * @param {object} user Discord User - * @param {string} token Discord Token (Fake) - */ - this.emit(Event.SUCCESS, this.user, message.ticket); - this.token = message.ticket; - this._findRealToken(); - this._logger('default', 'Get token success.'); - break; - } - default: { - this._logger('debug', `Unknown op: ${message.op}`, message); - } - } - } - _logger(type = 'default', ...message) { - if (this.options.hiddenLog) return; - switch (type.toLowerCase()) { - case 'error': { - // eslint-disable-next-line no-unused-expressions - this.options.failIfError - ? this._throwError(new Error(message[0])) - : console.error(chalk.red(`[DiscordRemoteAuth] ERROR`), ...message); - break; - } - case 'default': { - console.log(chalk.green(`[DiscordRemoteAuth]`), ...message); - break; - } - case 'debug': { - if (this.options.debug) console.log(chalk.yellow(`[DiscordRemoteAuth] DEBUG`), ...message); + + case receiveEvent.PENDING_LOGIN: { + this.#ticket = message.ticket; + this.#findRealToken(); break; } } } - _throwError(error) { - console.log(chalk.red(`[DiscordRemoteAuth] ERROR`), error); - throw error; - } - _send(op, data) { - if (!this.ws) this._throwError(new Error('WebSocket is not connected.')); + + #send(op, data) { + if (!this.#ws) return; let payload = { op: op }; if (data !== null) payload = { ...payload, ...data }; - this._logger('debug', `Send Data:`, payload); - this.ws.send(JSON.stringify(payload)); + this.#ws.send(JSON.stringify(payload)); } - _heartbeat() { - this._send(sendEvent.HEARTBEAT); - } - _heartbeatAck() { + + #heartbeatAck() { setTimeout(() => { - this._heartbeat(); - }, this.heartbeatInterval).unref(); + this.#send(sendEvent.HEARTBEAT); + }, this.#heartbeatInterval).unref(); } - _ready(data) { - this._logger('debug', 'Attempting server handshake...'); - this._expire = new Date(Date.now() + data.timeout_ms); - this.heartbeatInterval = data.heartbeat_interval; - this._createKey(); - this._heartbeatAck(); - this._init(); + + #ready(data) { + this.emit(Event.DEBUG, 'Attempting server handshake...'); + this.#expire = new Date(Date.now() + data.timeout_ms); + this.#heartbeatInterval = data.heartbeat_interval; + this.#createKey(); + this.#heartbeatAck(); + this.#init(); } - _createKey() { - if (this.key) this._throwError(new Error('Key is already created.')); - this.key = crypto.generateKeyPairSync('rsa', { + + #createKey() { + const key = crypto.generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', @@ -307,35 +207,41 @@ new DiscordAuthWebsocket({ format: 'pem', }, }); + this.#privateKey = key.privateKey; + this.#publicKey = key.publicKey; } - _createPublicKey() { - if (!this.key) this._throwError(new Error('Key is not created.')); - this._logger('debug', 'Generating public key...'); + + #encodePublicKey() { const decoder = new StringDecoder('utf-8'); - let pub_key = decoder.write(this.key.publicKey); + let pub_key = decoder.write(this.#publicKey); pub_key = pub_key.split('\n').slice(1, -2).join(''); - this._logger('debug', 'Public key generated.', pub_key); return pub_key; } - _init() { - const public_key = this._createPublicKey(); - this._send(sendEvent.INIT, { encoded_public_key: public_key }); + + #init() { + const public_key = this.#encodePublicKey(); + this.#send(sendEvent.INIT, { encoded_public_key: public_key }); } - _receiveNonceProof(data) { + + #receiveNonceProof(data) { const nonce = data.encrypted_nonce; - const decrypted_nonce = this._decryptPayload(nonce); - let proof = crypto.createHash('sha256').update(decrypted_nonce).digest(); - proof = urlsafe_b64encode(proof); - proof = proof.replace(/\s+$/, ''); - this._send(sendEvent.NONCE_PROOF, { proof: proof }); - this._logger('debug', `Nonce proof decrypted:`, proof); + const decrypted_nonce = this.#decryptPayload(nonce); + const proof = crypto + .createHash('sha256') + .update(decrypted_nonce) + .digest() + .toString('base64') + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=+/, '') + .replace(/\s+$/, ''); + this.#send(sendEvent.NONCE_PROOF, { proof: proof }); } - _decryptPayload(encrypted_payload) { - if (!this.key) this._throwError(new Error('Key is not created.')); + + #decryptPayload(encrypted_payload) { const payload = Buffer.from(encrypted_payload, 'base64'); - this._logger('debug', `Encrypted Payload (Buffer):`, payload); const decoder = new StringDecoder('utf-8'); - const private_key = decoder.write(this.key.privateKey); + const private_key = decoder.write(this.#privateKey); const data = crypto.privateDecrypt( { key: private_key, @@ -344,170 +250,129 @@ new DiscordAuthWebsocket({ }, payload, ); - this._logger('debug', `Decrypted Payload:`, data.toString()); return data; } - _pendingLogin(data) { - const user_data = this._decryptPayload(data.encrypted_user_payload); - const user = new User(user_data.toString()); - this.user = user; + + #pendingLogin(data) { + const user_data = this.#decryptPayload(data.encrypted_user_payload); + this.#userDecryptString = user_data.toString(); + + /** + * @typedef {Object} UserRaw + * @property {Snowflake} id + * @property {string} username + * @property {number} discriminator + * @property {string} avatar + */ + /** * Emitted whenever a user is scan QR Code. * @event DiscordAuthWebsocket#pending - * @param {object} user Discord User Raw + * @param {UserRaw} user Discord User Raw */ - this.emit(Event.WAIT, user); - this._logger('debug', 'Waiting for user to finish login...'); - this.user.prettyPrint(this); - this._logger('default', 'Please check your phone again to confirm login.'); + this.emit(Event.WAIT_SCAN, this.user); } - _pendingRemoteInit(data) { - this._logger('debug', `Pending Remote Init:`, data); - /** - * Emitted whenever a url is created. - * @event DiscordAuthWebsocket#ready - * @param {string} fingerprint Fingerprint - * @param {string} url DiscordAuthWebsocket - */ - this.emit(Event.READY, data.fingerprint, `${baseURL}${data.fingerprint}`); - this.fingerprint = data.fingerprint; - if (this.options.generateQR) this.generateQR(); - } - _awaitLogin(client) { - this.once(Event.FINISH, (user, token) => { - this._logger('debug', 'Create login state...', user, token); - client.login(token); + + #awaitLogin(client) { + return new Promise(r => { + this.once(Event.FINISH, token => { + r(client.login(token)); + }); }); } + /** - * Connect to DiscordAuthWebsocket. - * @param {?Client} client Using only for auto login. - * @returns {undefined} + * Connect WS + * @param {Client} [client] DiscordJS Client + * @returns {Promise} */ connect(client) { - this._createWebSocket(wsURL); - if (client && this.options.autoLogin) this._awaitLogin(client); + this.#createWebSocket(wsURL); + if (client) { + return this.#awaitLogin(client); + } else { + return Promise.resolve(); + } } + /** - * Disconnect from DiscordAuthWebsocket. - * @returns {undefined} + * Destroy client + * @returns {void} */ destroy() { - if (!this.ws) this._throwError(new Error('WebSocket is not connected.')); + if (!this.ws) return; this.ws.close(); + this.emit(Event.DEBUG, 'WebSocket closed.'); /** * Emitted whenever a connection is closed. * @event DiscordAuthWebsocket#closed - * @param {boolean} loginState Login state */ - this.emit(Event.CLOSED, this.token); - this._logger('debug', 'WebSocket closed.'); + this.emit(Event.CLOSED); } + /** * Generate QR code for user to scan (Terminal) - * @returns {undefined} + * @returns {void} */ generateQR() { - if (!this.fingerprint) this._throwError(new Error('Fingerprint is not created.')); - require('@aikochan2k6/qrcode-terminal').generate(`${baseURL}${this.fingerprint}`, { + if (!this.#fingerprint) return; + require('@aikochan2k6/qrcode-terminal').generate(this.AuthURL, { small: true, }); - this._logger('default', `Please scan the QR code to continue.\nQR Code will expire in ${this.exprireTime}`); } - async _findRealToken(captchaSolveData) { - this.callFindRealTokenCount++; - if (!this.token) return this._throwError(new Error('Token is not created.')); - if (!captchaSolveData && this.captchaCache) return this._throwError(new Error('Captcha is not solved.')); - if (this.callFindRealTokenCount > 5) { - return this._throwError( - new Error( - `Failed to find real token (${this.callFindRealTokenCount} times) ${this.captchaCache ? '[Captcha]' : ''}`, - ), - ); - } - this._logger('debug', 'Find real token...'); - const res = await ( - await fetch(`https://discord.com/api/v${this.options.apiVersion}/users/@me/remote-auth/login`, { - method: 'POST', - headers: { - Accept: '*/*', - 'Accept-Language': 'en-US', - 'Content-Type': 'application/json', - 'Sec-Fetch-Dest': 'empty', - 'Sec-Fetch-Mode': 'cors', - 'Sec-Fetch-Site': 'same-origin', - 'X-Debug-Options': 'bugReporterEnabled', - 'X-Super-Properties': `${Buffer.from(JSON.stringify(this.options.wsProperties), 'ascii').toString('base64')}`, - 'X-Discord-Locale': 'en-US', - 'User-Agent': this.options.userAgent, - Referer: 'https://discord.com/channels/@me', - Connection: 'keep-alive', - Origin: 'https://discord.com', - }, - body: JSON.stringify( - captchaSolveData - ? { - ticket: this.token, - captcha_rqtoken: this.captchaCache.captcha_rqtoken, - captcha_key: captchaSolveData, - } - : { - ticket: this.token, - }, - ), + #findRealToken() { + return fetch(`https://discord.com/api/v9/users/@me/remote-auth/login`, { + method: 'POST', + headers: { + Accept: '*/*', + 'Accept-Language': 'en-US', + 'Content-Type': 'application/json', + 'Sec-Fetch-Dest': 'empty', + 'Sec-Fetch-Mode': 'cors', + 'Sec-Fetch-Site': 'same-origin', + 'X-Debug-Options': 'bugReporterEnabled', + 'X-Super-Properties': `${Buffer.from(JSON.stringify(defaultClientOptions.ws.properties), 'ascii').toString( + 'base64', + )}`, + 'X-Discord-Locale': 'en-US', + 'User-Agent': UserAgent, + Referer: 'https://discord.com/channels/@me', + Connection: 'keep-alive', + Origin: 'https://discord.com', + }, + body: JSON.stringify({ + ticket: this.#ticket, + }), + }) + .then(r => r.json()) + .then(res => { + if (res.encrypted_token) { + this.token = this.#decryptPayload(res.encrypted_token).toString(); + } + /** + * Emitted whenever a real token is found. + * @event DiscordAuthWebsocket#finish + * @param {string} token Discord Token + */ + this.emit(Event.FINISH, this.token); + this.destroy(); }) - ).json(); - if (res?.captcha_key) { - this.captchaCache = res; - } else if (!res.encrypted_token) { - this._throwError(new Error('Request failed. Please try again.', res)); - this.captchaCache = null; - } - if (!res && this.captchaCache) { - this._logger('default', 'Captcha is detected. Please solve the captcha to continue.'); - this._logger('debug', 'Try call captchaSolver()', this.captchaCache); - const token = await this.options.captchaSolver(this.captchaCache); - return this._findRealToken(token); - } - this.realToken = this._decryptPayload(res.encrypted_token).toString(); - /** - * Emitted whenever a real token is found. - * @event DiscordAuthWebsocket#finish - * @param {object} user User - * @param {string} token Real token - */ - this.emit(Event.FINISH, this.user, this.realToken); - return this; + .catch(() => false); } -} -class User { - constructor(payload) { + static decryptUser(payload) { const values = payload.split(':'); - this.id = values[0]; - this.username = values[3]; - this.discriminator = values[1]; - this.avatar = values[2]; - return this; - } - get avatarURL() { - return `https://cdn.discordapp.com/avatars/${this.id}/${this.avatar}.${ - this.avatar.startsWith('a_') ? 'gif' : 'png' - }`; - } - get tag() { - return `${this.username}#${this.discriminator}`; - } - prettyPrint(RemoteAuth) { - let string = `\n`; - string += ` ${chalk.bgBlue('User:')} `; - string += `${this.tag} (${this.id})\n`; - string += ` ${chalk.bgGreen('Avatar URL:')} `; - string += chalk.cyan(`${this.avatarURL}\n`); - string += ` ${chalk.bgMagenta('Token:')} `; - string += chalk.red(`${this.token ? this.token : 'Unknown'}`); - RemoteAuth._logger('default', string); + const id = values[0]; + const username = values[3]; + const discriminator = values[1]; + const avatar = values[2]; + return { + id, + username, + discriminator, + avatar, + }; } } diff --git a/src/util/Util.js b/src/util/Util.js index f0d956a..e6c7aa9 100644 --- a/src/util/Util.js +++ b/src/util/Util.js @@ -5,12 +5,13 @@ const process = require('node:process'); const { Collection } = require('@discordjs/collection'); const fetch = require('node-fetch'); const { Colors } = require('./Constants'); -const { RangeError, TypeError, Error: DJSError } = require('../errors'); +const { Error: DiscordError, RangeError, TypeError } = require('../errors'); const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k); const isObject = d => typeof d === 'object' && d !== null; let deprecationEmittedForSplitMessage = false; let deprecationEmittedForRemoveMentions = false; +let deprecationEmittedForResolveAutoArchiveMaxLimit = false; const TextSortableGroupTypes = ['GUILD_TEXT', 'GUILD_ANNOUCMENT', 'GUILD_FORUM']; const VoiceSortableGroupTypes = ['GUILD_VOICE', 'GUILD_STAGE_VOICE']; @@ -138,6 +139,7 @@ class Util extends null { * @property {boolean} [numberedList=false] Whether to escape numbered lists * @property {boolean} [maskedLink=false] Whether to escape masked links */ + /** * Escapes any Discord-flavour markdown in a string. * @param {string} text Content to escape @@ -220,6 +222,7 @@ class Util extends null { if (maskedLink) text = Util.escapeMaskedLink(text); return text; } + /** * Escapes code block markdown in a string. * @param {string} text Content to escape @@ -228,6 +231,7 @@ class Util extends null { static escapeCodeBlock(text) { return text.replaceAll('```', '\\`\\`\\`'); } + /** * Escapes inline code markdown in a string. * @param {string} text Content to escape @@ -236,6 +240,7 @@ class Util extends null { static escapeInlineCode(text) { return text.replace(/(?<=^|[^`])``?(?=[^`]|$)/g, match => (match.length === 2 ? '\\`\\`' : '\\`')); } + /** * Escapes italic markdown in a string. * @param {string} text Content to escape @@ -253,6 +258,7 @@ class Util extends null { return `\\_${match}`; }); } + /** * Escapes bold markdown in a string. * @param {string} text Content to escape @@ -265,6 +271,7 @@ class Util extends null { return '\\*\\*'; }); } + /** * Escapes underline markdown in a string. * @param {string} text Content to escape @@ -277,6 +284,7 @@ class Util extends null { return '\\_\\_'; }); } + /** * Escapes strikethrough markdown in a string. * @param {string} text Content to escape @@ -285,6 +293,7 @@ class Util extends null { static escapeStrikethrough(text) { return text.replaceAll('~~', '\\~\\~'); } + /** * Escapes spoiler markdown in a string. * @param {string} text Content to escape @@ -293,6 +302,7 @@ class Util extends null { static escapeSpoiler(text) { return text.replaceAll('||', '\\|\\|'); } + /** * Escapes escape characters in a string. * @param {string} text Content to escape @@ -301,6 +311,7 @@ class Util extends null { static escapeEscape(text) { return text.replaceAll('\\', '\\\\'); } + /** * Escapes heading characters in a string. * @param {string} text Content to escape @@ -309,6 +320,7 @@ class Util extends null { static escapeHeading(text) { return text.replaceAll(/^( {0,2}[*-] +)?(#{1,3} )/gm, '$1\\$2'); } + /** * Escapes bulleted list characters in a string. * @param {string} text Content to escape @@ -317,6 +329,7 @@ class Util extends null { static escapeBulletedList(text) { return text.replaceAll(/^( *)[*-]( +)/gm, '$1\\-$2'); } + /** * Escapes numbered list characters in a string. * @param {string} text Content to escape @@ -325,6 +338,7 @@ class Util extends null { static escapeNumberedList(text) { return text.replaceAll(/^( *\d+)\./gm, '$1\\.'); } + /** * Escapes masked link characters in a string. * @param {string} text Content to escape @@ -334,6 +348,16 @@ class Util extends null { return text.replaceAll(/\[.+\]\(.+\)/gm, '\\$&'); } + /** + * @typedef {Object} FetchRecommendedShardsOptions + * @property {number} [guildsPerShard=1000] Number of guilds assigned per shard + * @property {number} [multipleOf=1] The multiple the shard count should round up to. (16 for large bot sharding) + */ + + static fetchRecommendedShards() { + throw new DiscordError('INVALID_USER_API'); + } + /** * Parses emoji info out of a string. The string must be one of: * * A UTF-8 emoji (no id) @@ -623,26 +647,22 @@ class Util extends null { /** * Resolves the maximum time a guild's thread channels should automatically archive in case of no recent activity. - * @deprecated + * @param {Guild} guild The guild to resolve this limit from. + * @deprecated This will be removed in the next major version. * @returns {number} */ static resolveAutoArchiveMaxLimit() { + if (!deprecationEmittedForResolveAutoArchiveMaxLimit) { + process.emitWarning( + // eslint-disable-next-line max-len + "The Util.resolveAutoArchiveMaxLimit method and the 'MAX' option are deprecated and will be removed in the next major version.", + 'DeprecationWarning', + ); + deprecationEmittedForResolveAutoArchiveMaxLimit = true; + } return 10080; } - /** - * Lazily evaluates a callback function (yea it's v14 :yay:) - * @param {Function} cb The callback to lazily evaluate - * @returns {Function} - * @example - * const User = lazy(() => require('./User')); - * const user = new (User())(client, data); - */ - static lazy(cb) { - let defaultValue; - return () => (defaultValue ??= cb()); - } - /** * Transforms an API guild forum tag to camel-cased guild forum tag. * @param {APIGuildForumTag} tag The tag to transform @@ -708,95 +728,6 @@ class Util extends null { }; } - static async getAttachments(client, channelId, ...files) { - files = files.flat(2); - if (!files.length) return []; - files = files.map((file, i) => ({ - filename: file.name ?? file.attachment?.name ?? file.attachment?.filename ?? 'file.jpg', - // 25MB = 26_214_400bytes - file_size: Math.floor((26_214_400 / 10) * Math.random()), - id: `${i}`, - })); - const { attachments } = await client.api.channels[channelId].attachments.post({ - data: { - files, - }, - }); - return attachments; - } - - static uploadFile(data, url) { - return new Promise((resolve, reject) => { - fetch(url, { - method: 'PUT', - body: data, - }) - .then(res => { - if (res.ok) { - resolve(res); - } else { - reject(res); - } - }) - .catch(reject); - }); - } - - static testImportModule(name) { - try { - require.resolve(name); - return true; - } catch { - return false; - } - } - - static getProxyObject(proxy) { - const protocol = new URL(proxy).protocol.slice(0, -1); - const mapObject = { - http: 'https', // Cuz we can't use http for discord - https: 'https', - socks4: 'socks', - socks5: 'socks', - 'pac+http': 'pac', - 'pac+https': 'pac', - }; - const proxyType = mapObject[protocol]; - switch (proxyType) { - case 'https': { - if (!Util.testImportModule('https-proxy-agent')) { - throw new DJSError('MISSING_MODULE', 'https-proxy-agent', 'npm install https-proxy-agent'); - } - const httpsProxyAgent = require('https-proxy-agent'); - return new httpsProxyAgent.HttpsProxyAgent(proxy); - } - - case 'socks': { - if (!Util.testImportModule('socks-proxy-agent')) { - throw new DJSError('MISSING_MODULE', 'socks-proxy-agent', 'npm install socks-proxy-agent'); - } - const socksProxyAgent = require('socks-proxy-agent'); - return new socksProxyAgent.SocksProxyAgent(proxy); - } - - case 'pac': { - if (!Util.testImportModule('pac-proxy-agent')) { - throw new DJSError('MISSING_MODULE', 'pac-proxy-agent', 'npm install pac-proxy-agent'); - } - const pacProxyAgent = require('pac-proxy-agent'); - return new pacProxyAgent.PacProxyAgent(proxy); - } - - default: { - if (!Util.testImportModule('proxy-agent')) { - throw new DJSError('MISSING_MODULE', 'proxy-agent', 'npm install proxy-agent@5'); - } - const proxyAgent = require('proxy-agent'); - return new proxyAgent(proxy); - } - } - } - /** * Gets an array of the channel types that can be moved in the channel group. For example, a GuildText channel would * return an array containing the types that can be ordered within the text channels (always at the top), and a voice @@ -831,94 +762,38 @@ class Util extends null { return Number(BigInt(userId) >> 22n) % 6; } - static clientRequiredAction(client, code) { - let msg = ''; - let stopClient = false; - switch (code) { - case null: { - msg = 'All required actions have been completed.'; - break; - } - case 'AGREEMENTS': { - msg = 'You need to accept the new Terms of Service and Privacy Policy.'; - // https://discord.com/api/v9/users/@me/agreements - client.api - .users('@me') - .agreements.patch({ - data: { - terms: true, - privacy: true, - }, - }) - .then(() => { - client.emit( - 'debug', - '[USER_REQUIRED_ACTION] Successfully accepted the new Terms of Service and Privacy Policy.', - ); - }) - .catch(e => { - client.emit( - 'debug', - `[USER_REQUIRED_ACTION] Failed to accept the new Terms of Service and Privacy Policy: ${e}`, - ); - }); - break; - } - case 'REQUIRE_CAPTCHA': { - msg = 'You need to complete a captcha.'; - stopClient = true; - break; - } - case 'REQUIRE_VERIFIED_EMAIL': { - msg = 'You need to verify your email.'; - stopClient = true; - break; - } - case 'REQUIRE_REVERIFIED_EMAIL': { - msg = 'You need to reverify your email.'; - stopClient = true; - break; - } - case 'REQUIRE_VERIFIED_PHONE': { - msg = 'You need to verify your phone number.'; - stopClient = true; - break; - } - case 'REQUIRE_REVERIFIED_PHONE': { - msg = 'You need to reverify your phone number.'; - stopClient = true; - break; - } - case 'REQUIRE_VERIFIED_EMAIL_OR_VERIFIED_PHONE': { - msg = 'You need to verify your email or verify your phone number.'; - stopClient = true; // Maybe not - break; - } - case 'REQUIRE_REVERIFIED_EMAIL_OR_VERIFIED_PHONE': { - msg = 'You need to reverify your email or verify your phone number.'; - stopClient = true; - break; - } - case 'REQUIRE_VERIFIED_EMAIL_OR_REVERIFIED_PHONE': { - msg = 'You need to verify your email or reverify your phone number.'; - stopClient = true; - break; - } - case 'REQUIRE_REVERIFIED_EMAIL_OR_REVERIFIED_PHONE': { - msg = 'You need to reverify your email or reverify your phone number.'; - stopClient = true; - break; - } - default: { - msg = `Unknown required action: ${code}`; - break; - } - } - if (stopClient) { - client.emit('error', new Error(`[USER_REQUIRED_ACTION] ${msg}`)); - } else { - client.emit('debug', `[USER_REQUIRED_ACTION] ${msg}`); - } + static async getUploadURL(client, channelId, files) { + if (!files.length) return []; + files = files.map((file, i) => ({ + filename: file.name, + // 25MB = 26_214_400bytes + file_size: Math.floor((26_214_400 / 10) * Math.random()), + id: `${i}`, + })); + const { attachments } = await client.api.channels[channelId].attachments.post({ + data: { + files, + }, + }); + return attachments; + } + + static uploadFile(data, url) { + return new Promise((resolve, reject) => { + fetch(url, { + method: 'PUT', + body: data, + duplex: 'half', // Node.js v20 + }) + .then(res => { + if (res.ok) { + resolve(res); + } else { + reject(res); + } + }) + .catch(reject); + }); } } diff --git a/typings/enums.d.ts b/typings/enums.d.ts index dd30437..56581f5 100644 --- a/typings/enums.d.ts +++ b/typings/enums.d.ts @@ -10,67 +10,6 @@ export const enum ActivityTypes { COMPETING = 5, } -export const enum DMScanLevel { - NOT_SCAN = 0, - NOT_FRIEND = 1, - EVERYONE = 2, -} - -export const enum stickerAnimationMode { - ALWAYS = 0, - INTERACTION = 1, - NEVER = 2, -} - -export const enum NitroType { - NONE = 0, - NITRO_CLASSIC = 1, - NITRO_BOOST = 2, - NITRO_BASIC = 3, -} - -export const enum RelationshipTypes { - NONE = 0, - FRIEND = 1, - BLOCKED = 2, - PENDING_INCOMING = 3, - PENDING_OUTGOING = 4, - IMPLICIT = 5, -} - -export const enum localeSetting { - DANISH = 'da', - GERMAN = 'de', - ENGLISH_UK = 'en-GB', - ENGLISH_US = 'en-US', - SPANISH = 'es-ES', - FRENCH = 'fr', - CROATIAN = 'hr', - ITALIAN = 'it', - LITHUANIAN = 'lt', - HUNGARIAN = 'hu', - DUTCH = 'nl', - NORWEGIAN = 'no', - POLISH = 'pl', - BRAZILIAN_PORTUGUESE = 'pt-BR', - ROMANIA_ROMANIAN = 'ro', - FINNISH = 'fi', - SWEDISH = 'sv-SE', - VIETNAMESE = 'vi', - TURKISH = 'tr', - CZECH = 'cs', - GREEK = 'el', - BULGARIAN = 'bg', - RUSSIAN = 'ru', - UKRAINIAN = 'uk', - HINDI = 'hi', - THAI = 'th', - CHINA_CHINESE = 'zh-CN', - JAPANESE = 'ja', - TAIWAN_CHINESE = 'zh-TW', - KOREAN = 'ko', -} - export const enum ApplicationCommandTypes { CHAT_INPUT = 1, USER = 2, @@ -203,13 +142,6 @@ export const enum GuildScheduledEventStatuses { CANCELED = 4, } -export const enum HypeSquadType { - LEAVE = 0, - HOUSE_BRAVERY = 1, - HOUSE_BRILLIANCE = 2, - HOUSE_BALANCE = 3, -} - export const enum InteractionResponseTypes { PONG = 1, CHANNEL_MESSAGE_WITH_SOURCE = 4, @@ -249,8 +181,6 @@ export const enum MessageButtonStyles { export const enum MessageComponentTypes { ACTION_ROW = 1, BUTTON = 2, - /** @deprecated Use `STRING_SELECT` instead */ - SELECT_MENU = 3, STRING_SELECT = 3, TEXT_INPUT = 4, USER_SELECT = 5, @@ -260,8 +190,6 @@ export const enum MessageComponentTypes { } export const enum SelectMenuComponentTypes { - /** @deprecated Use `STRING_SELECT` instead */ - SELECT_MENU = 3, STRING_SELECT = 3, USER_SELECT = 5, ROLE_SELECT = 6, @@ -344,3 +272,12 @@ export enum ApplicationRoleConnectionMetadataTypes { BOOLEAN_EQUAL, BOOLEAN_NOT_EQUAL, } + +export const enum RelationshipTypes { + NONE = 0, + FRIEND = 1, + BLOCKED = 2, + PENDING_INCOMING = 3, + PENDING_OUTGOING = 4, + IMPLICIT = 5, +} diff --git a/typings/index.d.ts b/typings/index.d.ts index 464632b..75d394b 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2,7 +2,6 @@ import { blockQuote, bold, channelMention, - chatInputApplicationCommandMention, codeBlock, ContextMenuCommandBuilder, formatEmoji, @@ -21,7 +20,6 @@ import { underscore, userMention, } from '@discordjs/builders'; -import { VoiceConnection } from '@discordjs/voice'; import { Collection } from '@discordjs/collection'; import { APIActionRowComponent, @@ -47,13 +45,13 @@ import { APISelectMenuComponent, APITemplateSerializedSourceGuild, APIUser, - MessageActivityType, - GatewayOpcodes, GatewayVoiceServerUpdateDispatchData, GatewayVoiceStateUpdateDispatchData, + MessageActivityType, RESTPostAPIApplicationCommandsJSONBody, Snowflake, LocalizationMap, + LocaleString, APIGuildMember, APIChannel, } from 'discord-api-types/v9'; @@ -74,10 +72,6 @@ import { AutoModerationRuleKeywordPresetTypes, AutoModerationRuleTriggerTypes, ChannelTypes, - RelationshipTypes, - localeSetting, - stickerAnimationMode, - DMScanLevel, DefaultMessageNotificationLevels, ExplicitContentFilterLevels, InteractionResponseTypes, @@ -88,9 +82,6 @@ import { MessageComponentTypes, MessageTypes, MFALevels, - NitroType as NitroTypes, - HypeSquadType as HypeSquadTypes, - localeSetting as localeSettings, NSFWLevels, OverwriteTypes, PremiumTiers, @@ -103,12 +94,12 @@ import { GuildScheduledEventEntityTypes, GuildScheduledEventStatuses, GuildScheduledEventPrivacyLevels, - HypeSquadType, VideoQualityModes, SortOrderType, - SelectMenuComponentTypes, ForumLayoutType, ApplicationRoleConnectionMetadataTypes, + RelationshipTypes, + SelectMenuComponentTypes, } from './enums'; import { APIApplicationRoleConnectionMetadata, @@ -120,7 +111,6 @@ import { RawApplicationData, RawBaseGuildData, RawChannelData, - RawClientApplicationData, RawDMChannelData, RawEmojiData, RawGuildAuditLogData, @@ -173,106 +163,9 @@ import { RawWidgetData, RawWidgetMemberData, } from './rawDataTypes'; -// @ts-ignore + //#region Classes -export class SessionManager extends CachedManager { - private constructor(client: Client, iterable: Iterable); - public fetch(): Promise; - public logoutAllDevices(mfaCode?: string): Promise; -} - -export class BillingManager extends BaseManager { - constructor(client: Client); - public paymentSources: Collection; - public fetchPaymentSources(): Promise>; - public guildBoosts: Collection; - public fetchGuildBoosts(): Promise>; - public currentSubscription: Collection; - public fetchCurrentSubscription(): Promise>; -} - -export class GuildBoost extends Base { - constructor(client: Client, data: object); - public id: Snowflake; - public guildId?: Snowflake; - public readonly guild: Guild | null; - public subscriptionId: Snowflake; - public premiumGuildSubscriptionId?: Snowflake; - public ended?: boolean; - public canceled: boolean; - public cooldownEndsAt: Date; - public unsubscribe(): Promise; - public subscribe(guild: GuildResolvable): Promise; -} - -export class Session extends Base { - constructor(client: Client); - public id?: string; - public clientInfo?: SessionClientInfo; - public readonly createdTimestamp: number; - public readonly createdAt: Date; - public logout(mfaCode?: string): Promise; -} - -export interface SessionClientInfo { - location?: string; - platform?: string; - os?: string; -} - -export class DiscordAuthWebsocket extends EventEmitter { - constructor(options?: DiscordAuthWebsocketOptions); - public fingerprint?: string; - public heartbeatInterval?: number; - public ws?: WebSocket; - public token?: string; - public realToken?: string; - public user?: RawUserData; - public captchaCache?: Captcha; - public captchaHandler(data: Captcha): Promise; - public readonly exprireTime: string; - public connect(client?: Client): void; - public destroy(): void; - public generateQR(): void; - public on(event: 'ready', listener: (fingerprint: string, authURL: string) => void): this; - public on(event: 'finish', listener: (user: RawUserData, token: string) => void): this; - public on(event: 'cancel' | 'pending', listener: (user: RawUserData) => void): this; - public on(event: 'closed', listener: (token?: string) => void): this; - public on(event: string, listener: (...args: any[]) => Awaitable): this; -} - -export class DiscordRPCServer extends EventEmitter { - constructor(client: Client, debug?: boolean); - public debug?: boolean; - public client: Client; - public on(event: 'activity', listener: (data: RPCActivityData) => void): this; -} - -export class Call extends Base { - constructor(client: Client, data: object); - public channelId: Snowflake; - public ringing: Collection; - public region?: string; - public readonly channel: PartialDMChannel | PartialGroupDMChannel; - public setVoiceRegion(region: string): Promise; -} - -export interface RPCActivityData { - activity?: RichPresence; - pid: number; - socketId: string; -} - -export interface DiscordAuthWebsocketOptions { - debug: boolean; - hiddenLog: boolean; - autoLogin: boolean; - failIfError: boolean; - generateQR: boolean; - userAgent?: string; - wsProperties?: object; -} // RPC by aiko-chan-ai export interface RichButton { name: string; @@ -319,11 +212,32 @@ export class RichPresence { image1: string, image2: string, ): Promise; - public static getUUID(): string; public toJSON(): object; public toString(): string; } +export class DiscordAuthWebsocket extends EventEmitter { + constructor(); + public token: string; + public readonly user: { + id: Snowflake; + username: string; + discriminator: number; + avatar: string; + }; + public readonly exprire: Date; + public readonly AuthURL: string; + public connect(client?: Client): Promise; + public destroy(): void; + public generateQR(): void; + public on(event: 'ready', listener: (client: this) => void): this; + public on(event: 'finish', listener: (token: string) => void): this; + public on(event: 'cancel' | 'pending', listener: (user: RawUserData) => void): this; + public on(event: 'closed', listener: () => void): this; + public on(event: string, listener: (...args: any[]) => Awaitable): this; +} + + export interface ExternalAssets { url: string; external_asset_path: string; @@ -442,103 +356,43 @@ export abstract class Application extends Base { public id: Snowflake; public name: string | null; public roleConnectionsVerificationURL: string | null; - public coverURL(options?: StaticImageURLOptions): string | null; - /** @deprecated This method is deprecated as it is unsupported and will be removed in the next major version. */ - public fetchAssets(): Promise; - public invite(guildID: Snowflake, permissions?: PermissionResolvable, captcha?: string): Promise; - public iconURL(options?: StaticImageURLOptions): string | null; - public toJSON(): unknown; - public toString(): string | null; -} - -export interface Tester { - state: number; - user: User; -} - -export interface ApplicationEditData { - name?: string; - description?: string; - icon?: BufferResolvable | Base64Resolvable; - cover?: BufferResolvable | Base64Resolvable; - botPublic?: boolean; - botRequireCodeGrant?: boolean; - TermsOfService?: string; - PrivacyPolicy?: string; - flags?: number; - redirectURIs?: string[]; - tags?: string[]; -} -export class DeveloperPortalApplication extends Base { - private constructor(client: Client, data: object); + public approximateGuildCount: number | null; public botPublic: boolean | null; public botRequireCodeGrant: boolean | null; public commands: ApplicationCommandManager; public cover: string | null; public flags: Readonly; + public guildId: Snowflake | null; + public readonly guild: Guild | null; public tags: string[]; public installParams: ClientApplicationInstallParams | null; public customInstallURL: string | null; public owner: User | Team | null; public readonly partial: boolean; public rpcOrigins: string[]; - public readonly createdAt: Date; - public readonly createdTimestamp: number; - public description: string | null; - public icon: string | null; - public id: Snowflake; - public name: string | null; - public redirectURIs: string[]; - public interactionEndpointURL: string | null; - public publicKey: string; - public testers: Collection; - public TermsOfService: string | null; - public PrivacyPolicy: string | null; - public roleConnectionsVerificationURL: string | null; - public fetch(): Promise; + public fetch(): Promise; + public fetchRoleConnectionMetadataRecords(): Promise; public coverURL(options?: StaticImageURLOptions): string | null; /** @deprecated This method is deprecated as it is unsupported and will be removed in the next major version. */ public fetchAssets(): Promise; public iconURL(options?: StaticImageURLOptions): string | null; public toJSON(): unknown; public toString(): string | null; - public fetchTesters(): Promise; - public addTester(username: string, discriminator: string): Promise; - public deleteTester(user: UserResolvable): Promise; - public edit(data: ApplicationEditData): Promise; - public createBot(): Promise; - public resetClientSecret(MFACode?: number): Promise; - public resetBotToken(MFACode?: number): Promise; - public delete(MFACode?: number): Promise; - public addAsset(image: BufferResolvable | Base64Resolvable, name: string): Promise; - public deleteAsset(id: string): Promise; - public fetchRoleConnectionMetadataRecords(): Promise; - public editRoleConnectionMetadataRecords( - records: ApplicationRoleConnectionMetadataEditOptions[], - ): Promise; -} - -export class DeveloperPortalManager extends BaseManager { - constructor(client: Client); - public applications: Collection; - public teams: Collection; - public fetch(): Promise; - public createTeam(name: string): Promise; - public createApplication(name: string, teamId?: Team | Snowflake): Promise; - public deleteApplication(id: Snowflake, MFACode?: number): Promise; } export class ApplicationCommand extends Base { - private constructor(client: Client, data: RawApplicationCommandData); + private constructor(client: Client, data: RawApplicationCommandData, guild?: Guild, guildId?: Snowflake); public applicationId: Snowflake; public readonly createdAt: Date; public readonly createdTimestamp: number; /** @deprecated Use {@link defaultMemberPermissions} and {@link dmPermission} instead. */ public defaultPermission: boolean; + public defaultMemberPermissions: Readonly | null; public description: string; public descriptionLocalizations: LocalizationMap | null; public descriptionLocalized: string | null; - public readonly guild: Guild | null; + public dmPermission: boolean | null; + public guild: Guild | null; public guildId: Snowflake | null; public readonly manager: ApplicationCommandManager; public id: Snowflake; @@ -587,13 +441,6 @@ export class ApplicationCommand extends Base { private static transformOption(option: ApplicationCommandOptionData, received?: boolean): unknown; private static transformCommand(command: ApplicationCommandData): RESTPostAPIApplicationCommandsJSONBody; private static isAPICommandData(command: object): command is RESTPostAPIApplicationCommandsJSONBody; - // Add - public static sendSlashCommand( - message: Message, - subCommandArray?: string[], - options?: any[], - ): Promise; - public static sendContextMenu(message: Message): Promise; } export class ApplicationRoleConnectionMetadata { @@ -886,7 +733,6 @@ export class CategoryChannel extends GuildChannel { name: string, options: CategoryCreateChannelOptions & { type: T }, ): Promise; - /** @deprecated See [Self-serve Game Selling Deprecation](https://support-dev.discord.com/hc/en-us/articles/6309018858647) for more information */ public createChannel( name: string, @@ -918,30 +764,12 @@ export abstract class Channel extends Base { export type If = T extends true ? A : T extends false ? B : A | B; -export interface OAuth2AuthorizeOptions { - guild_id: Snowflake; - permissions?: PermissionResolvable; - authorize?: boolean; - code?: string; - webhook_channel_id?: Snowflake; -} - export class Client extends BaseClient { - public constructor(options?: ClientOptions); /* Bug report by Mavri#0001 [721347809667973141] */ + public constructor(options: ClientOptions); private actions: unknown; private presence: ClientPresence; private _eval(script: string): unknown; private _validateOptions(options: ClientOptions): void; - - public application: If; - // Added - public settings: ClientUserSettingManager; - public relationships: RelationshipManager; - public readonly callVoice?: VoiceConnection; - public voiceStates: VoiceStateManager; - public sessions: SessionManager; - public billing: BillingManager; - // End public channels: ChannelManager; public readonly emojis: BaseGuildEmojiManager; public guilds: GuildManager; @@ -956,36 +784,30 @@ export class Client extends BaseClient { public users: UserManager; public voice: ClientVoiceManager; public ws: WebSocketManager; - public password: string | null; - public readonly sessionId: string | null; + public notes: UserNoteManager; + public relationships: RelationshipManager; + public voiceStates: VoiceStateManager; + public presences: PresenceManager; public destroy(): void; - public logout(): Promise; public fetchGuildPreview(guild: GuildResolvable): Promise; public fetchInvite(invite: InviteResolvable, options?: ClientFetchInviteOptions): Promise; - public acceptInvite(invite: InviteResolvable): Promise; public fetchGuildTemplate(template: GuildTemplateResolvable): Promise; public fetchVoiceRegions(): Promise>; public fetchSticker(id: Snowflake): Promise; public fetchPremiumStickerPacks(): Promise>; public fetchWebhook(id: Snowflake, token?: string): Promise; public fetchGuildWidget(guild: GuildResolvable): Promise; - public redeemNitro(code: string, channel?: TextChannelResolvable, paymentSourceId?: Snowflake): object; - public generateInvite(options?: InviteGenerationOptions): string; + public sleep(timeout: number): Promise; public login(token?: string): Promise; - public normalLogin(username: string, password?: string, mfaCode?: string): Promise; - public switchUser(token: string): void; - public QRLogin(options?: DiscordAuthWebsocketOptions): DiscordAuthWebsocket; - public remoteAuth(url: string): Promise; - public createToken(): Promise; - public checkUpdate(): Promise; + public QRLogin(): Promise; + public logout(): Promise; public isReady(): this is Client; /** @deprecated Use {@link Sweepers#sweepMessages} instead */ public sweepMessages(lifetime?: number): number; - private customStatusAuto(client?: this): undefined; - public authorizeURL(url: string, options?: object): Promise; - public sleep(milliseconds: number): Promise | null; - private _clearCache(cache: Collection): void; public toJSON(): unknown; + public acceptInvite(invite: InviteResolvable): Promise; + public redeemNitro(nitro: string, channel?: TextChannelResolvable, paymentSourceId?: Snowflake): Promise; + public authorizeURL(url: string, options?: OAuth2AuthorizeOptions): Promise; public on(event: K, listener: (...args: ClientEvents[K]) => Awaitable): this; public on( @@ -1012,28 +834,12 @@ export class Client extends BaseClient { public removeAllListeners(event?: Exclude): this; } -export class ClientApplication extends Application { - private constructor(client: Client, data: RawClientApplicationData); - public approximateGuildCount: number | null; - public botPublic: boolean | null; - public popularCommands: Collection | undefined; - public botRequireCodeGrant: boolean | null; - public commands: ApplicationCommandManager; - public cover: string | null; - public flags: Readonly; - public guildId: Snowflake | null; - public readonly guild: Guild | null; - public tags: string[]; - public installParams: ClientApplicationInstallParams | null; - public customInstallURL: string | null; - public owner: User | Team | null; - public readonly partial: boolean; - public rpcOrigins: string[]; - public fetch(): Promise; - public fetchRoleConnectionMetadataRecords(): Promise; - public editRoleConnectionMetadataRecords( - records: ApplicationRoleConnectionMetadataEditOptions[], - ): Promise; +export interface OAuth2AuthorizeOptions { + guild_id?: Snowflake; + permissions?: PermissionResolvable; + authorize?: boolean; + code?: string; + webhook_channel_id?: Snowflake; } export class ClientPresence extends Presence { @@ -1047,57 +853,36 @@ export class ClientUser extends User { public mfaEnabled: boolean; public readonly presence: ClientPresence; public verified: boolean; - public notes: Collection; - public friendNicknames: Collection; - public purchasedFlags: PurchasedFlags; - public premiumUsageFlags: PremiumUsageFlags; - public setThemeColors(primary?: ColorResolvable, accent?: ColorResolvable): ClientUser; public edit(data: ClientUserEditData): Promise; - public setActivity(options?: ActivitiesOptions): ClientPresence; + public setActivity(options?: ActivityOptions): ClientPresence; public setActivity(name: string, options?: Omit): ClientPresence; public setAFK(afk?: boolean, shardId?: number | number[]): ClientPresence; public setAvatar(avatar: BufferResolvable | Base64Resolvable | null): Promise; - public setBanner(banner: BufferResolvable | Base64Resolvable | null): Promise; public setPresence(data: PresenceData): ClientPresence; public setStatus(status: PresenceStatusData, shardId?: number | number[]): ClientPresence; - public setUsername(username: string, password: string): Promise; - public setHypeSquad(type: HypeSquadType): Promise; + public setUsername(username: string): Promise; + public purchasedFlags: PurchasedFlags; + public premiumUsageFlags: PremiumUsageFlags; + public phone: string | null; + public nsfwAllowed?: boolean; + public email: string | null; + public bio?: string; + public pronouns?: string; + public premiumType: number; + public setBanner(banner: BufferResolvable | Base64Resolvable | null): Promise; + public setHypeSquad(hypesquad: 0 | 1 | 2 | 3 | 'LEAVE' | 'HOUSE_BRAVERY' | 'HOUSE_BRILLIANCE' | 'HOUSE_BALANCE'): Promise; public setAccentColor(color: ColorResolvable): Promise; - public setDiscriminator(discriminator: string, password: string): Promise; public setAboutMe(bio: string | null): Promise; - public setEmail(email: string, password: string): Promise; - public setPassword(oldPassword: string, newPassword: string): Promise; - public disableAccount(password: string): Promise; - public deleteAccount(password: string): Promise; - public setDeaf(status: boolean): Promise; - public setMute(status: boolean): Promise; public createFriendInvite(): Promise; public getAllFriendInvites(): Promise>; - public revokeAllFriendInvites(): Promise>; - public setSamsungActivity(packageName: string, type?: 'START' | 'UPDATE' | 'STOP'): Promise; - public getMentions( - limit?: number, - mentionRoles?: boolean, - mentionEveryone?: boolean, - ): Promise>; - /** - * Nitro Status - * `0`: None - * `1`: Classic - * `2`: Boost - * @external https://discord.com/developers/docs/resources/user#user-object-premium-types - */ - public readonly nitroType: NitroType; - public readonly phoneNumber: string; - public readonly nsfwAllowed: boolean; - public readonly emailAddress: string; + public revokeAllFriendInvites(): Promise; + public setSamsungActivity(packageName: string, type: 'START' | 'UPDATE' | 'STOP'): Promise; public stopRinging(channel: ChannelResolvable): Promise; - public setGlobalName(globalName?: string): Promise; - public setPronouns(pronouns?: string): Promise; + public fetchBurstCredit(): Promise; + public setPronouns(pronouns?: string | null): Promise; + public setGlobalName(globalName?: string | null): Promise; } -type NitroType = 'NONE' | 'NITRO_CLASSIC' | 'NITRO_BOOST' | 'NITRO_BASIC'; - export class Options extends null { private constructor(); public static defaultMakeCacheSettings: CacheWithLimitsOptions; @@ -1121,6 +906,12 @@ export interface CollectorEventTypes { end: [collected: Collection, reason: string]; } +export type ChannelFlagsString = 'PINNED' | 'REQUIRE_TAG'; +export class ChannelFlags extends BitField { + public static FLAGS: Record; + public static resolve(bit?: BitFieldResolvable): number; +} + export abstract class Collector extends EventEmitter { protected constructor(client: Client, options?: CollectorOptions<[V, ...F]>); private _timeout: NodeJS.Timeout | null; @@ -1306,27 +1097,28 @@ export class DataResolver extends null { public static resolveGuildTemplateCode(data: GuildTemplateResolvable): string; } -export interface Captcha { - captcha_key: string[]; - captcha_service: string; - captcha_sitekey: string; - captcha_rqdata?: string; - captcha_rqtoken?: string; -} export class DiscordAPIError extends Error { private constructor(error: unknown, status: number, request: unknown); private static flattenErrors(obj: unknown, key: string): string[]; - public captcha?: Captcha; + public code: number; public method: string; public path: string; public httpStatus: number; public requestData: HTTPErrorData; public retries: number; + public captcha: Captcha | null; +} + +export interface Captcha { + captcha_key: string[]; + captcha_sitekey: string; + captcha_service: 'hcaptcha'; + captcha_rqdata?: string; + captcha_rqtoken?: string; } export class DMChannel extends TextBasedChannelMixin(Channel, [ - 'bulkDelete', 'fetchWebhooks', 'createWebhook', 'setRateLimitPerUser', @@ -1336,20 +1128,16 @@ export class DMChannel extends TextBasedChannelMixin(Channel, [ public recipient: User; public type: 'DM'; public flags: Readonly; + public messageRequest?: boolean; + public messageRequestTimestamp?: number; public fetch(force?: boolean): Promise; + public acceptMessageRequest(): Promise; + public cancelMessageRequest(): Promise; + public sync(): void; + public ring(): Promise; public readonly voiceAdapterCreator: InternalDiscordGatewayAdapterCreator; - public call(options?: CallOptions): Promise; - public ring(): Promise; - public sync(): undefined; public readonly shard: WebSocketShard; public readonly voiceUsers: Collection; - public readonly voiceConnection?: VoiceConnection; -} - -export interface CallOptions { - selfDeaf?: boolean; - selfMute?: boolean; - ring?: boolean; } export class Emoji extends Base { @@ -1373,17 +1161,18 @@ export class Guild extends AnonymousGuild { private _sortedChannels(channel: NonThreadGuildBasedChannel): Collection; public readonly afkChannel: VoiceChannel | null; - public topEmojis(): Promise>; public afkChannelId: Snowflake | null; public afkTimeout: number; public applicationId: Snowflake | null; + public maxVideoChannelUsers: number | null; + public maxStageVideoChannelUsers: number | null; public approximateMemberCount: number | null; public approximatePresenceCount: number | null; public autoModerationRules: AutoModerationRuleManager; public available: boolean; public bans: GuildBanManager; public channels: GuildChannelManager; - // public commands: GuildApplicationCommandManager; + public commands: GuildApplicationCommandManager; public defaultMessageNotifications: DefaultMessageNotificationLevel | number; /** @deprecated This will be removed in the next major version, see https://github.com/discordjs/discord.js/issues/7091 */ public deleted: boolean; @@ -1396,8 +1185,6 @@ export class Guild extends AnonymousGuild { public large: boolean; public maximumMembers: number | null; public maximumPresences: number | null; - public maxStageVideoChannelUsers: number | null; - public maxVideoChannelUsers: number | null; /** @deprecated Use {@link GuildMemberManager.me} instead. */ public readonly me: GuildMember | null; public memberCount: number; @@ -1405,11 +1192,9 @@ export class Guild extends AnonymousGuild { public mfaLevel: MFALevel; public ownerId: Snowflake; public preferredLocale: string; - public premiumSubscriptionCount: number | null; public premiumProgressBarEnabled: boolean; public premiumTier: PremiumTier; public presences: PresenceManager; - public readonly disableDM: boolean; public readonly publicUpdatesChannel: TextChannel | null; public publicUpdatesChannelId: Snowflake | null; public roles: RoleManager; @@ -1433,8 +1218,7 @@ export class Guild extends AnonymousGuild { public widgetEnabled: boolean | null; public readonly maximumBitrate: number; public createTemplate(name: string, description?: string): Promise; - public delete(mfaCode?: string): Promise; - public read(): Promise; + public delete(): Promise; public discoverySplashURL(options?: StaticImageURLOptions): string | null; public edit(data: GuildEditData, reason?: string): Promise; public editWelcomeScreen(data: WelcomeScreenEditData): Promise; @@ -1442,7 +1226,6 @@ export class Guild extends AnonymousGuild { public fetchAuditLogs( options?: GuildAuditLogsFetchOptions, ): Promise>; - public mute(mute: boolean, time: number | null): boolean; public fetchIntegrations(): Promise>; public fetchOwner(options?: BaseFetchOptions): Promise; public fetchPreview(): Promise; @@ -1452,8 +1235,8 @@ export class Guild extends AnonymousGuild { public fetchWelcomeScreen(): Promise; public fetchWidget(): Promise; public fetchWidgetSettings(): Promise; - public disableInvites(disabled?: boolean): Promise; public leave(): Promise; + public disableInvites(disabled?: boolean): Promise; public setAFKChannel(afkChannel: VoiceChannelResolvable | null, reason?: string): Promise; public setAFKTimeout(afkTimeout: number, reason?: string): Promise; public setBanner(banner: BufferResolvable | Base64Resolvable | null, reason?: string): Promise; @@ -1474,8 +1257,7 @@ export class Guild extends AnonymousGuild { public setIcon(icon: BufferResolvable | Base64Resolvable | null, reason?: string): Promise; public setName(name: string, reason?: string): Promise; public setOwner(owner: GuildMemberResolvable, reason?: string): Promise; - public setPosition(position: number, type: 'FOLDER' | 'HOME', folderID?: number): Promise; - public setPreferredLocale(preferredLocale: string, reason?: string): Promise; + public setPreferredLocale(preferredLocale: string | null, reason?: string): Promise; public setPublicUpdatesChannel(publicUpdatesChannel: TextChannelResolvable | null, reason?: string): Promise; /** @deprecated Use {@link RoleManager.setPositions} instead */ public setRolePositions(rolePositions: readonly RolePosition[]): Promise; @@ -1486,15 +1268,12 @@ export class Guild extends AnonymousGuild { public setSystemChannelFlags(systemChannelFlags: SystemChannelFlagsResolvable, reason?: string): Promise; public setVerificationLevel(verificationLevel: VerificationLevel | number | null, reason?: string): Promise; public setPremiumProgressBarEnabled(enabled?: boolean, reason?: string): Promise; - public setCommunity( - stats: boolean, - publicUpdatesChannel: TextChannelResolvable, - rulesChannel: TextChannelResolvable, - reason?: string, - ): Promise; public setWidgetSettings(settings: GuildWidgetSettingsData, reason?: string): Promise; - public setVanityCode(code?: string): Promise; public toJSON(): unknown; + public markAsRead(): Promise; + public setCommunity(stats: Boolean, publicUpdatesChannel?: GuildTextChannelResolvable, rulesChannel?: GuildTextChannelResolvable, reason?: string): Promise; + public topEmojis(): Promise>; + public setVanityCode(code?: string): Promise; } export class GuildAuditLogs { @@ -1517,16 +1296,16 @@ export class GuildAuditLogs { export class GuildAuditLogsEntry< TActionRaw extends GuildAuditLogsResolvable = 'ALL', TAction = TActionRaw extends keyof GuildAuditLogsIds - ? GuildAuditLogsIds[TActionRaw] - : TActionRaw extends null - ? 'ALL' - : TActionRaw, + ? GuildAuditLogsIds[TActionRaw] + : TActionRaw extends null + ? 'ALL' + : TActionRaw, TActionType extends GuildAuditLogsActionType = TAction extends keyof GuildAuditLogsTypes - ? GuildAuditLogsTypes[TAction][1] - : 'ALL', + ? GuildAuditLogsTypes[TAction][1] + : 'ALL', TTargetType extends GuildAuditLogsTarget = TAction extends keyof GuildAuditLogsTypes - ? GuildAuditLogsTypes[TAction][0] - : 'UNKNOWN', + ? GuildAuditLogsTypes[TAction][0] + : 'UNKNOWN', > { private constructor(guild: Guild, data: RawGuildAuditLogEntryData, logs?: GuildAuditLogs); public action: TAction; @@ -1545,8 +1324,6 @@ export class GuildAuditLogsEntry< : Role | GuildEmoji | { id: Snowflake } | null; public targetType: TTargetType; public toJSON(): unknown; - public addIntegration(applicationId: Snowflake): Promise; - public addBot(bot: UserResolvable | Snowflake, permissions?: PermissionResolvable): Promise; } export class GuildBan extends Base { @@ -1562,7 +1339,6 @@ export abstract class GuildChannel extends Channel { public constructor(guild: Guild, data?: RawGuildChannelData, client?: Client, immediatePatch?: boolean); private memberPermissions(member: GuildMember, checkAdmin: boolean): Readonly; private rolePermissions(role: Role, checkAdmin: boolean): Readonly; - public readonly createdAt: Date; public readonly createdTimestamp: number; public readonly calculatedPosition: number; @@ -1613,14 +1389,6 @@ export class GuildEmoji extends BaseGuildEmoji { public setName(name: string, reason?: string): Promise; } -export interface UserBadge { - name: string; - description: string; - icon: string; - link?: string; - iconURL(): string; -} - export class GuildMember extends PartialTextBasedChannel(Base) { private constructor(client: Client, data: RawGuildMemberData, guild: Guild); private _roles: Snowflake[]; @@ -1651,9 +1419,6 @@ export class GuildMember extends PartialTextBasedChannel(Base) { public readonly roles: GuildMemberRoleManager; public user: User; public readonly voice: VoiceState; - public themeColors?: [number, number]; - public readonly hexThemeColor: [string, string] | null; - public badges: UserBadge[] | null; public avatarURL(options?: ImageURLOptions): string | null; public ban(options?: BanOptions): Promise; public disableCommunicationUntil(timeout: DateResolvable | null, reason?: string): Promise; @@ -1671,14 +1436,12 @@ export class GuildMember extends PartialTextBasedChannel(Base) { public permissionsIn(channel: GuildChannelResolvable): Readonly; public setNickname(nickname: string | null, reason?: string): Promise; public setFlags(flags: GuildMemberFlagsResolvable): Promise; - public setAvatar(avatar: BufferResolvable | Base64Resolvable | null): Promise; - public setBanner(banner: BufferResolvable | Base64Resolvable | null): Promise; - public setAboutMe(bio: string | null): Promise; - public getProfile(): Promise; public toJSON(): unknown; public toString(): MemberMention; public valueOf(): string; - public setThemeColors(primary?: ColorResolvable, accent?: ColorResolvable): GuildMember; + public setAvatar(avatar: BufferResolvable | Base64Resolvable | null): Promise; + public setBanner(banner: BufferResolvable | Base64Resolvable | null): Promise; + public setAboutMe(bio: string | null): Promise; } export class GuildMemberFlags extends BitField { @@ -1732,6 +1495,7 @@ export class GuildScheduledEvent; @@ -1798,7 +1562,7 @@ export class HTTPError extends Error { } // 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 { private constructor(data: RateLimitData); public name: 'RateLimitError'; @@ -1842,6 +1606,7 @@ export class IntegrationApplication extends Application { export class Intents extends BitField { public static FLAGS: Record; public static resolve(bit?: BitFieldResolvable): number; + public static ALL: number; } export type CacheType = 'cached' | 'raw' | undefined; @@ -1899,14 +1664,7 @@ export class Interaction extends Base { public isMessageContextMenu(): this is MessageContextMenuInteraction; public isMessageComponent(): this is MessageComponentInteraction; public isModalSubmit(): this is ModalSubmitInteraction; - public isAnySelectMenu(): this is SelectMenuInteraction; - /** @deprecated Use {@link Interaction#isStringSelect()} instead */ - public isSelectMenu(): this is StringSelectInteraction; - public isStringSelect(): this is StringSelectInteraction; - public isUserSelect(): this is UserSelectInteraction; - public isMentionableSelect(): this is MentionableSelectInteraction; - public isRoleSelect(): this is RoleSelectInteraction; - public isChannelSelect(): this is ChannelSelectInteraction; + public isSelectMenu(): this is SelectMenuInteraction; public isRepliable(): this is this & InteractionResponseFields; } @@ -1946,7 +1704,7 @@ export class InteractionWebhook extends PartialWebhookMixin() { export class Invite extends Base { private constructor(client: Client, data: RawInviteData); - public channel: NonThreadGuildBasedChannel | PartialGroupDMChannel; + public channel: NonThreadGuildBasedChannel | GroupDMChannel; public channelId: Snowflake; public code: string; public readonly deletable: boolean; @@ -1970,7 +1728,6 @@ export class Invite extends Base { public delete(reason?: string): Promise; public toJSON(): unknown; public toString(): string; - public acceptInvite(autoVerify?: boolean): Promise; public static INVITES_PATTERN: RegExp; public stageInstance: InviteStageInstance | null; public guildScheduledEvent: GuildScheduledEvent | null; @@ -2023,14 +1780,13 @@ export type MessageChannelCollectorOptionsParams< export type AwaitMessageCollectorOptionsParams< T extends MessageComponentTypeResolvable, Cached extends boolean = boolean, -> = { - componentType?: T; -} & Pick[T]>, keyof AwaitMessageComponentOptions>; +> = { componentType?: T } & Pick< + InteractionCollectorOptions[T]>, + keyof AwaitMessageComponentOptions +>; export interface StringMappedInteractionTypes { BUTTON: ButtonInteraction; - /** @deprecated */ - SELECT_MENU: SelectMenuInteraction; STRING_SELECT: SelectMenuInteraction; USER_SELECT: SelectMenuInteraction; ROLE_SELECT: SelectMenuInteraction; @@ -2045,8 +1801,6 @@ export type MappedInteractionTypes = EnumValue typeof MessageComponentTypes, { BUTTON: ButtonInteraction>; - /** @deprecated */ - SELECT_MENU: StringSelectInteraction>; STRING_SELECT: StringSelectInteraction>; USER_SELECT: UserSelectInteraction>; ROLE_SELECT: RoleSelectInteraction>; @@ -2066,7 +1820,7 @@ export class Message extends Base { public applicationId: Snowflake | null; public attachments: Collection; public author: User; - public readonly bulkDeletable: boolean; + public get bulkDeletable(): boolean; public readonly channel: If; public channelId: Snowflake; public readonly cleanContent: string; @@ -2082,7 +1836,7 @@ export class Message extends Base { public readonly editedAt: Date | null; public editedTimestamp: number | null; public embeds: MessageEmbed[]; - public groupActivityApplication: ClientApplication | null; + public groupActivityApplication: Application | null; public guildId: If; public readonly guild: If; public readonly hasThread: boolean; @@ -2105,14 +1859,8 @@ export class Message extends Base { public flags: Readonly; public reference: MessageReference | null; public position: number | null; - public awaitMessageComponent( - options?: AwaitMessageCollectorOptionsParams, - ): Promise[T]>; public awaitReactions(options?: AwaitReactionsOptions): Promise>; public createReactionCollector(options?: ReactionCollectorOptions): ReactionCollector; - public createMessageComponentCollector( - options?: MessageCollectorOptionsParams, - ): InteractionCollector[T]>; public delete(): Promise; public edit(content: string | MessageEditOptions | MessagePayload): Promise; public equals(message: Message, rawData: unknown): boolean; @@ -2131,22 +1879,31 @@ export class Message extends Base { public toString(): string; public unpin(reason?: string): Promise; public inGuild(): this is Message & this; - // Added - public markUnread(): Promise; - public markRead(): Promise; - public clickButton(button?: MessageButton | MessageButtonLocation | string): Promise; - public selectMenu(menuID: MessageSelectMenu | Snowflake | number, options: any[]): Promise; - public selectMenu(options: any[]): Promise; - public contextMenu(botID: Snowflake, commandName: string): Promise; + + public readonly isMessage: true; + public clickButton(button?: { X: number, Y: number } | string): Promise; + public selectMenu(menu: 0 | 1 | 2 | 3 | 4 | string, vales: Array): Promise; + public markUnread(): Promise; + public markRead(): Promise; +} + +export class CallState extends Base { + private constructor(client: Client, data: any); + public channelId: Snowflake; + public region: string; + public readonly channel?: DMChannel | GroupDMChannel; + public readonly ringing: Collection; + public setRTCRegion(): Promise; } export class MessageActionRow< T extends MessageActionRowComponent | ModalActionRowComponent = MessageActionRowComponent, U = T extends ModalActionRowComponent ? ModalActionRowComponentResolvable : MessageActionRowComponentResolvable, V = T extends ModalActionRowComponent - ? APIActionRowComponent - : APIActionRowComponent, + ? APIActionRowComponent + : APIActionRowComponent, > extends BaseMessageComponent { + // tslint:disable-next-line:ban-ts-ignore // @ts-ignore (TS:2344, Caused by TypeScript 4.8) // Fixed in DiscordJS >= 14.x / DiscordApiTypes >= 0.37.x, ignoring the type error here. public constructor(data?: MessageActionRow | MessageActionRowOptions | V); @@ -2183,11 +1940,6 @@ export class MessageAttachment { public toJSON(): unknown; } -export interface InteractionResponseBody { - id: Snowflake; - nonce: Snowflake; -} - export class AttachmentFlags extends BitField { public static FLAGS: Record; public static resolve(bit?: BitFieldResolvable): number; @@ -2211,7 +1963,6 @@ export class MessageButton extends BaseMessageComponent { public setStyle(style: MessageButtonStyleResolvable): this; public setURL(url: string): this; public toJSON(): APIButtonComponent; - public click(message: Message): Promise; private static resolveStyle(style: MessageButtonStyleResolvable): MessageButtonStyle; } @@ -2324,6 +2075,22 @@ export class MessageEmbed { public static normalizeFields(...fields: EmbedFieldData[] | EmbedFieldData[][]): Required[]; } +export interface WebEmbedOptions { + title?: string; + description?: string; + url?: string; + timestamp?: Date | number; + color?: ColorResolvable; + fields?: EmbedFieldData[]; + author?: Partial & { icon_url?: string; proxy_icon_url?: string }; + thumbnail?: Partial & { proxy_url?: string }; + image?: Partial & { proxy_url?: string }; + video?: Partial & { proxy_url?: string }; + footer?: Partial & { icon_url?: string; proxy_icon_url?: string }; + imageType?: 'thumbnail' | 'image'; + redirect?: string; +} + export class WebEmbed { public constructor(data?: WebEmbedOptions); public author: MessageEmbedAuthor | null; @@ -2335,8 +2102,6 @@ export class WebEmbed { public title: string | null; public url: string | null; public video: MessageEmbedVideo | null; - public hidden: boolean; - public shorten: boolean; public imageType: 'thumbnail' | 'image'; public redirect?: string; public setAuthor(options: EmbedAuthorData | null): this; @@ -2348,8 +2113,33 @@ export class WebEmbed { public setTitle(title: string): this; public setURL(url: string): this; public setProvider(options: MessageEmbedProvider | null): this; + public setRedirect(url: string): this; public toString(): string; - public toMessage(): Promise; + public static hiddenEmbed: string; +} + +export class BillingManager extends BaseManager { + constructor(client: Client); + public paymentSources: Collection; + public fetchPaymentSources(): Promise>; + public guildBoosts: Collection; + public fetchGuildBoosts(): Promise>; + public currentSubscription: Collection; + public fetchCurrentSubscription(): Promise>; +} + +export class GuildBoost extends Base { + constructor(client: Client, data: object); + public id: Snowflake; + public guildId?: Snowflake; + public readonly guild: Guild | null; + public subscriptionId: Snowflake; + public premiumGuildSubscriptionId?: Snowflake; + public ended?: boolean; + public canceled: boolean; + public cooldownEndsAt: Date; + public unsubscribe(): Promise; + public subscribe(guild: GuildResolvable): Promise; } export class MessageFlags extends BitField { @@ -2369,6 +2159,7 @@ export class MessageMentions { private readonly _content: string; private _members: Collection | null; private _parsedUsers: Collection | null; + public readonly channels: Collection; public readonly client: Client; public everyone: boolean; @@ -2391,7 +2182,6 @@ export class MessageMentions { export class MessagePayload { public constructor(target: MessageTarget, options: MessageOptions | WebhookMessageOptions); public data: RawMessagePayloadData | null; - public readonly usingNewAttachmentAPI: boolean; public readonly isUser: boolean; public readonly isWebhook: boolean; public readonly isMessage: boolean; @@ -2419,14 +2209,14 @@ export class MessageReaction { private constructor(client: Client, data: RawMessageReactionData, message: Message); private _emoji: GuildEmoji | ReactionEmoji; - public readonly client: Client; + public burstColors: string[]; + public readonly client: Client; public count: number; public burstCount: number; - public burstColors: string[]; + public countDetails: ReactionCountDetailsData; public isBurst: boolean; public readonly emoji: GuildEmoji | ReactionEmoji; public me: boolean; - public countDetails: ReactionCountDetailsData; public message: Message | PartialMessage; public readonly partial: false; public users: ReactionUserManager; @@ -2450,23 +2240,19 @@ export class MessageSelectMenu extends BaseMessageComponent { public options: MessageSelectOption[]; public placeholder: string | null; public type: SelectMenuComponentType; - public addChannelTypes(...channelTypes: ChannelTypes[]): this; public addOptions(...options: MessageSelectOptionData[] | MessageSelectOptionData[][]): this; public setOptions(...options: MessageSelectOptionData[] | MessageSelectOptionData[][]): this; - public setChannelTypes(...channelTypes: ChannelTypes[]): this; public setCustomId(customId: string): this; public setDisabled(disabled?: boolean): this; public setMaxValues(maxValues: number): this; public setMinValues(minValues: number): this; public setPlaceholder(placeholder: string): this; - public setType(type: SelectMenuComponentType | SelectMenuComponentTypes): this; public spliceOptions( index: number, deleteCount: number, ...options: MessageSelectOptionData[] | MessageSelectOptionData[][] ): this; public toJSON(): APISelectMenuComponent; - public select(message: Message, values?: any[]): Promise; } export class Modal { @@ -2474,45 +2260,13 @@ export class Modal { public components: MessageActionRow[]; public customId: string | null; public title: string | null; - public application: object | null; - public client: Client | null; - public nonce: Snowflake | null; - public readonly sendFromInteraction: InteractionResponse | null; - public addComponents( - ...components: ( - | MessageActionRow - | (Required & MessageActionRowOptions) - )[] - ): this; - public setComponents( - ...components: ( - | MessageActionRow - | (Required & MessageActionRowOptions) - )[] - ): this; - public setCustomId(customId: string): this; - public spliceComponents( - index: number, - deleteCount: number, - ...components: ( - | MessageActionRow - | (Required & MessageActionRowOptions) - )[] - ): this; - public setTitle(title: string): this; + public readonly isMessage: false; + public readonly guildId: Snowflake | null; + public channelId: Snowflake; + public readonly channel: TextBasedChannel; + public readonly guild: Guild | null; + public reply(): Promise; public toJSON(): RawModalSubmitInteractionData; - public reply(data: ModalReplyData): Promise; -} - -export interface ModalReplyData { - guild?: GuildResolvable; - channel?: TextChannelResolvable; - data?: TextInputComponentReplyData[]; -} - -export interface TextInputComponentReplyData { - customId: string; - value: string; } export class ModalSubmitFieldsResolver { @@ -2575,46 +2329,37 @@ export class OAuth2Guild extends BaseGuild { public permissions: Readonly; } -export class PartialGroupDMChannel extends TextBasedChannelMixin(Channel, [ - 'bulkDelete', +export class GroupDMChannel extends TextBasedChannelMixin(Channel, [ 'fetchWebhooks', 'createWebhook', 'setRateLimitPerUser', 'setNSFW', ]) { private constructor(client: Client, data: RawPartialGroupDMChannelData); - public type: 'GROUP_DM'; public name: string | null; public icon: string | null; + public flags: Readonly; + private _recipients: RawUserData[]; + public type: 'GROUP_DM'; + public ownerId: Snowflake; public readonly recipients: Collection; - public messages: MessageManager; - public invites: Collection; - public lastMessageId: Snowflake | null; - public lastPinTimestamp: number | null; - public owner: User | null; - public ownerId: Snowflake | null; - public flags: null; - public messageRequest: boolean | undefined; - public messageRequestTimestamp: Date | undefined; - public acceptMessageRequest(): Promise; - public cancelMessageRequest(): Promise; + public readonly owner: User; public iconURL(options?: StaticImageURLOptions): string | null; - public addMember(user: User): Promise; - public removeMember(user: User): Promise; - public setName(name: string): Promise; - public setIcon(icon: Base64Resolvable | null): Promise; + public leave(slient?: boolean): Promise; + public edit(data: GroupDMChannelEditData): Promise; + public setIcon(icon: BufferResolvable | Base64Resolvable | null): Promise; + public setName(name: string): Promise; + public setOwner(owner: UserResolvable): Promise; + public addUser(user: UserResolvable): Promise; + public removeUser(user: UserResolvable): Promise; public getInvite(): Promise; - public fetchInvite(force: boolean): Promise; - public removeInvite(invite: Invite): Promise; - public delete(slient?: boolean): Promise; - public setOwner(user: UserResolvable): Promise; + public fetchAllInvite(): Promise>; + public deleteInvite(invite: InviteResolvable): Promise; + public sync(): void; + public ring(recipients?: UserResolvable[]): Promise; public readonly voiceAdapterCreator: InternalDiscordGatewayAdapterCreator; - public call(options?: CallOptions): Promise; - public ring(recipients: UserResolvable[]): Promise; - public sync(): undefined; public readonly shard: WebSocketShard; public readonly voiceUsers: Collection; - public readonly voiceConnection?: VoiceConnection; } export class PermissionOverwrites extends Base { @@ -2650,8 +2395,9 @@ export class Permissions extends BitField { export class Presence extends Base { protected constructor(client: Client, data?: RawPresenceData); - public activities: Activity[]; + public activities: (Activity | CustomStatus | RichPresence | SpotifyRPC)[]; public clientStatus: ClientPresenceStatusData | null; + public lastModified: number | null; public guild: Guild | null; public readonly member: GuildMember | null; public status: PresenceStatus; @@ -2850,6 +2596,7 @@ export interface ShardEventTypes { error: [error: Error]; message: [message: any]; } + export class Shard extends EventEmitter { private constructor(manager: ShardingManager, id: number); private _evals: Map>; @@ -2953,6 +2700,11 @@ export class ShardingManager extends EventEmitter { public once(event: 'shardCreate', listener: (shard: Shard) => Awaitable): this; } +export interface FetchRecommendedShardsOptions { + guildsPerShard?: number; + multipleOf?: number; +} + export class SnowflakeUtil extends null { private constructor(); public static deconstruct(snowflake: Snowflake): DeconstructedSnowflake; @@ -3128,9 +2880,6 @@ export class Team extends Base { public readonly createdTimestamp: number; public iconURL(options?: StaticImageURLOptions): string | null; - public inviteMemeber(user: User, MFACode: number): Promise; - public removeMemeber(userID: Snowflake): boolean; - public delete(MFACode: string): Promise; public toJSON(): unknown; public toString(): string; } @@ -3152,6 +2901,58 @@ export class TextChannel extends BaseGuildTextChannel { public type: 'GUILD_TEXT'; } +export interface GuildForumTagEmoji { + id: Snowflake | null; + name: string | null; +} + +export interface GuildForumTag { + id: Snowflake; + name: string; + moderated: boolean; + emoji: GuildForumTagEmoji | null; +} + +export type GuildForumTagData = Partial & { name: string }; + +export interface DefaultReactionEmoji { + id: Snowflake | null; + name: string | null; +} + +export class ForumChannel extends TextBasedChannelMixin(GuildChannel, [ + 'send', + 'lastMessage', + 'lastPinAt', + 'sendTyping', + 'createMessageCollector', + 'awaitMessages', +]) { + public type: 'GUILD_FORUM'; + public threads: GuildForumThreadManager; + public availableTags: GuildForumTag[]; + public defaultReactionEmoji: DefaultReactionEmoji | null; + public defaultThreadRateLimitPerUser: number | null; + public rateLimitPerUser: number | null; + public defaultAutoArchiveDuration: ThreadAutoArchiveDuration | null; + public nsfw: boolean; + public topic: string | null; + public defaultSortOrder: SortOrderType | null; + public defaultForumLayout: ForumLayoutType; + public setAvailableTags(tags: GuildForumTagData[], reason?: string): Promise; + public setDefaultReactionEmoji(emojiId: DefaultReactionEmoji | null, reason?: string): Promise; + public setDefaultThreadRateLimitPerUser(rateLimit: number, reason?: string): Promise; + public createInvite(options?: CreateInviteOptions): Promise; + public fetchInvites(cache?: boolean): Promise>; + public setDefaultAutoArchiveDuration( + defaultAutoArchiveDuration: ThreadAutoArchiveDuration, + reason?: string, + ): Promise; + public setTopic(topic: string | null, reason?: string): Promise; + public setDefaultSortOrder(defaultSortOrder: SortOrderType | null, reason?: string): Promise; + public setDefaultForumLayout(defaultForumLayout: ForumLayoutType, reason?: string): Promise; +} + export class TextInputComponent extends BaseMessageComponent { public constructor(data?: TextInputComponent | TextInputComponentOptions); public customId: string | null; @@ -3162,13 +2963,6 @@ export class TextInputComponent extends BaseMessageComponent { public placeholder: string | null; public style: TextInputStyle; public value: string | null; - public setCustomId(customId: string): this; - public setLabel(label: string): this; - public setRequired(required?: boolean): this; - public setMaxLength(maxLength: number): this; - public setMinLength(minLength: number): this; - public setPlaceholder(placeholder: string): this; - public setStyle(style: TextInputStyleResolvable): this; public setValue(value: string): this; public toJSON(): RawTextInputComponentData; public static resolveStyle(style: TextInputStyleResolvable): TextInputStyle; @@ -3177,7 +2971,6 @@ export class TextInputComponent extends BaseMessageComponent { export class ThreadChannel extends TextBasedChannelMixin(Channel, ['fetchWebhooks', 'createWebhook', 'setNSFW']) { private constructor(guild: Guild, data?: RawThreadChannelData, client?: Client); public archived: boolean | null; - public readonly firstMessage: Message | null; public readonly archivedAt: Date | null; public archiveTimestamp: number | null; public readonly createdAt: Date | null; @@ -3273,15 +3066,15 @@ export class Typing extends Base { export class User extends PartialTextBasedChannel(Base) { protected constructor(client: Client, data: RawUserData); private _equals(user: APIUser): boolean; - public application: ClientApplication; + public accentColor: number | null | undefined; public avatar: string | null; public avatarDecoration: string | null; + public avatarDecorationSKUId: Snowflake | null; public banner: string | null | undefined; + public bannerColor: string | null | undefined; public bot: boolean; - public pronouns: string | null; public readonly createdAt: Date; - public readonly relationships: RelationshipTypes; public readonly createdTimestamp: number; public discriminator: string; public readonly displayName: string; @@ -3289,22 +3082,16 @@ export class User extends PartialTextBasedChannel(Base) { public readonly dmChannel: DMChannel | null; public flags: Readonly | null; public globalName: string | null; - public botInGuildsCount: number | null | undefined; public readonly hexAccentColor: HexColorString | null | undefined; public id: Snowflake; public readonly partial: false; public system: boolean; public readonly tag: string; public username: string; - public readonly note: string | null; - public readonly nickname: string | null; - public connectedAccounts: object[]; - public premiumSince: Date; - public premiumGuildSince: Date; - public bio: string | null; - public readonly mutualGuilds: Collection; - public readonly mutualFriends: Promise>; - public readonly voice: VoiceState; + public readonly note: string | undefined; + public readonly voice?: VoiceState; + public readonly relationship: RelationshipTypes; + public readonly friendNickname: string | null | undefined; public avatarURL(options?: ImageURLOptions): string | null; public avatarDecorationURL(options?: StaticImageURLOptions): string | null; public bannerURL(options?: ImageURLOptions): string | null; @@ -3314,19 +3101,12 @@ export class User extends PartialTextBasedChannel(Base) { public equals(user: User): boolean; public fetch(force?: boolean): Promise; public fetchFlags(force?: boolean): Promise; - public setFriend(): Promise; - public setBlock(): Promise; - public sendFriendRequest(): Promise; - public unFriend(): Promise; - public unBlock(): Promise; - public setNote(note?: any): Promise; - public getProfile(guildId?: Snowflake): Promise; - public setNickname(nickname: string | null): Promise; + public setNote(note: string | null | undefined): Promise; public toString(): UserMention; - /** @deprecated This method is deprecated as it is unsupported and will be removed in the next major version. */ - public ring(): Promise; - public themeColors?: [number, number]; - public readonly hexThemeColor: [string, string] | null; + public getProfile(guildId?: Snowflake): Promise; + public ring(): Promise; + public sendFriendRequest(): Promise; + public deleteRelationship(): Promise; } export class UserContextMenuInteraction extends ContextMenuInteraction { @@ -3369,6 +3149,7 @@ export class Util extends null { public static escapeNumberedList(text: string): string; public static escapeMaskedLink(text: string): string; public static cleanCodeBlockContent(text: string): string; + public static fetchRecommendedShards(token: string, options?: FetchRecommendedShardsOptions): Promise; public static flatten(obj: unknown, ...props: Record[]): unknown; public static makeError(obj: MakeErrorOptions): Error; public static makePlainError(err: Error): MakeErrorOptions; @@ -3397,7 +3178,21 @@ export class Formatters extends null { public static blockQuote: typeof blockQuote; public static bold: typeof bold; public static channelMention: typeof channelMention; - public static chatInputApplicationCommandMention: typeof chatInputApplicationCommandMention; + public static chatInputApplicationCommandMention< + N extends string, + G extends string, + S extends string, + I extends Snowflake, + >(commandName: N, subcommandGroupName: G, subcommandName: S, commandId: I): ``; + public static chatInputApplicationCommandMention( + commandName: N, + subcommandName: S, + commandId: I, + ): ``; + public static chatInputApplicationCommandMention( + commandName: N, + commandId: I, + ): ``; public static codeBlock: typeof codeBlock; public static formatEmoji: typeof formatEmoji; public static hideLinkEmbed: typeof hideLinkEmbed; @@ -3436,12 +3231,13 @@ export class VoiceRegion { export class VoiceState extends Base { private constructor(guild: Guild, data: RawVoiceStateData); - public readonly channel: VoiceBasedChannel | null; + public readonly channel: VoiceBasedChannel | DMChannel | GroupDMChannel | null; public channelId: Snowflake | null; public readonly deaf: boolean | null; public guild: Guild; public id: Snowflake; public readonly member: GuildMember | null; + public readonly user: User | null; public readonly mute: boolean | null; public selfDeaf: boolean | null; public selfMute: boolean | null; @@ -3452,14 +3248,14 @@ export class VoiceState extends Base { public selfVideo: boolean | null; public suppress: boolean; public requestToSpeakTimestamp: number | null; - public readonly user: User | null; + public setDeaf(deaf?: boolean, reason?: string): Promise; public setMute(mute?: boolean, reason?: string): Promise; public disconnect(reason?: string): Promise; public setChannel(channel: GuildVoiceChannelResolvable | null, reason?: string): Promise; - public setStatus(status: string): Promise; public setRequestToSpeak(request?: boolean): Promise; public setSuppressed(suppressed?: boolean): Promise; + public setStatus(status?: string): Promise; public getPreview(): Promise; public postPreview(base64Image: string): Promise; } @@ -3543,8 +3339,8 @@ export class WebSocketShard extends EventEmitter { private constructor(manager: WebSocketManager, id: number); private sequence: number; private closeSequence: number; - private sessionId: string | null; private resumeURL: string | null; + private sessionId: string | null; private lastPingTimestamp: number; private lastHeartbeatAcked: boolean; private ratelimit: { queue: unknown[]; total: number; remaining: number; time: 60e3; timer: NodeJS.Timeout | null }; @@ -3687,6 +3483,7 @@ export const Constants: { size: AllowedImageSize, dynamic: boolean, ): string; + AvatarDecoration(userId: Snowflake, hash: string, format: AllowedImageFormat, size: AllowedImageSize): string; Banner(id: Snowflake, hash: string, format: DynamicImageFormat, size: AllowedImageSize, dynamic: boolean): string; DefaultAvatar(index: number): string; DiscoverySplash(guildId: Snowflake, hash: string, format: AllowedImageFormat, size: AllowedImageSize): string; @@ -3727,10 +3524,8 @@ export const Constants: { GuildScheduledEventPrivacyLevels: EnumHolder; GuildScheduledEventStatuses: EnumHolder; IntegrationExpireBehaviors: IntegrationExpireBehaviors[]; - InteractionResponseTypes: EnumHolder; - InteractionTypes: EnumHolder; - InviteScopes: InviteScope[]; - MaxBulkDeletableMessageAge: 1_209_600_000; + SelectMenuComponentTypes: EnumHolder; + RelationshipTypes: EnumHolder; MembershipStates: EnumHolder; MessageButtonStyles: EnumHolder; MessageComponentTypes: EnumHolder; @@ -3787,15 +3582,6 @@ export const Constants: { WSEvents: { [K in WSEventType]: K; }; - // Add - defaultUA: string; - captchaServices: captchaServices[]; - DMScanLevel: EnumHolder; - stickerAnimationMode: EnumHolder; - NitroType: EnumHolder; - HypeSquadType: EnumHolder; - localeSetting: EnumHolder; - userGateway: string; }; export const version: string; @@ -3837,8 +3623,7 @@ export class ApplicationCommandManager< PermissionsOptionsExtras = { guild: GuildResolvable }, PermissionsGuildType = null, > extends CachedManager { - // @ts-ignore - protected constructor(client: Client, iterable?: Iterable, user: User); + protected constructor(client: Client, iterable?: Iterable); public permissions: ApplicationCommandPermissionsManager< { command?: ApplicationCommandResolvable } & PermissionsOptionsExtras, { command: ApplicationCommandResolvable } & PermissionsOptionsExtras, @@ -3883,10 +3668,7 @@ export class ApplicationCommandPermissionsManager< GuildType, CommandIdType, > extends BaseManager { - private constructor( - manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand, - user: User, - ); + private constructor(manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand); private manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand; public client: Client; @@ -3902,13 +3684,13 @@ export class ApplicationCommandPermissionsManager< public remove( options: | (FetchSingleOptions & { - users: UserResolvable | UserResolvable[]; - roles?: RoleResolvable | RoleResolvable[]; - }) + users: UserResolvable | UserResolvable[]; + roles?: RoleResolvable | RoleResolvable[]; + }) | (FetchSingleOptions & { - users?: UserResolvable | UserResolvable[]; - roles: RoleResolvable | RoleResolvable[]; - }), + users?: UserResolvable | UserResolvable[]; + roles: RoleResolvable | RoleResolvable[]; + }), ): Promise; public set( options: FetchSingleOptions & { permissions: ApplicationCommandPermissionData[] }, @@ -3934,97 +3716,47 @@ export class BaseGuildEmojiManager extends CachedManager { private constructor(client: Client, iterable: Iterable); public fetch(id: Snowflake, options?: FetchChannelOptions): Promise; - public createGroupDM(recipients: UserResolvable[]): Promise; + public createGroupDM(recipients: UserResolvable[]): Promise; +} + +export class RelationshipManager extends BaseManager { + constructor( + client: Client, + data: { + user: RawUserData; + type: RelationshipTypes; + since?: string; + nickname: string | null | undefined; + id: Snowflake; + }[], + ); + public cache: Collection; + public friendNicknames: Collection; + public sinceCache: Collection; + public readonly friendCache: Collection; + public readonly blockedCache: Collection; + public readonly incomingCache: Collection; + public readonly outgoingCache: Collection; + public toJSON(): { type: RelationshipTypes; since: string; nickname: string | null | undefined; id: Snowflake }[]; + public resolveId(user: UserResolvable): Snowflake | undefined; + public fetch(user?: UserResolvable, options?: BaseFetchOptions): Promise; + public deleteRelationship(user: UserResolvable): Promise; + public sendFriendRequest(options: FriendRequestOptions): Promise; + public addFriend(user: UserResolvable): Promise; + public setNickname(user: UserResolvable, nickname: string | null | undefined): Promise; + public addBlocked(user: UserResolvable): Promise; +} + +export class UserNoteManager extends BaseManager { + constructor(client: Client, users: { [key: Snowflake]: string }[]); + public cache: Collection; + private _reload(data: { [key: Snowflake]: string }): this; + public updateNote(id: Snowflake, note: string | null | undefined): Promise; + public fetch(user: UserResolvable, options?: BaseFetchOptions): Promise; } export type FetchGuildApplicationCommandFetchOptions = Omit; -export class GuildFolderManager extends BaseManager { - private constructor(client: Client); - public cache: Collection; -} - -export interface RawGuildFolderData { - id: number | null; - name: string | null; - guild_ids: Snowflake[]; - color: number | null; -} -export class GuildFolder extends Base { - private constructor(client: Client, data: RawGuildFolderData); - public id: number | null; - public name: string | null; - public guild_ids: Snowflake[]; - public color: number | null; - public readonly hexColor: string | null; - public readonly guilds: Collection; - public toJSON(): RawGuildFolderData; -} - -export class ClientUserSettingManager extends BaseManager { - private constructor(client: Client); - public rawSetting: RawUserSettingsData | object; - public locale: localeSetting | null; - public activityDisplay: boolean | null; - public DMfromServerMode: boolean | null; - public displayImage: boolean | null; - public linkedImageDisplay: boolean | null; - public autoplayGIF: boolean | null; - public previewLink: boolean | null; - public animatedEmojis: boolean | null; - public allowTTS: boolean | null; - public compactMode: boolean | null; - public convertEmoticons: boolean | null; - public DMScanLevel: DMScanLevel; - public theme: 'dark' | 'light' | null; - public developerMode: boolean | null; - public afkTimeout: number | null; // second - public stickerAnimationMode: stickerAnimationMode; - public showEmojiReactions: boolean | null; - public customStatus: - | { - 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 guildFolder: GuildFolderManager; - public disableDMfromServer: Collection; - public fetch(): Promise; - public edit(data: Partial): Promise; - public setDisplayCompactMode(value?: boolean): Promise; - public setTheme(value?: 'dark' | 'light'): Promise; - public setLocale(value: localeSetting): Promise; - // @ts-ignore - public setCustomStatus(value?: CustomStatusOption | CustomStatus): Promise; - public restrictedGuilds(status: boolean): Promise; - public addRestrictedGuild(guildId: GuildResolvable): Promise; - public removeRestrictedGuild(guildId: GuildResolvable): Promise; -} - -export class GuildSettingManager extends BaseManager { - private constructor(client: Client, guildId: Snowflake); - public rawSetting?: RawGuildSettingsData; - public suppressEveryone?: boolean; - public suppressRoles?: boolean; - public muteScheduledEvents?: boolean; - public messageNotifications?: number; - public flags?: number; - public mobilePush?: boolean; - public muted?: boolean; - public muteConfig?: MuteConfigData; - public hideMutedChannels?: boolean; - public channelOverrides?: object[]; - public notifyHighlights?: number; - public version?: number; - public guildId?: Snowflake; - public readonly guild?: Guild; - public edit(data: Partial): Promise; -} - export class GuildApplicationCommandManager extends ApplicationCommandManager { private constructor(guild: Guild, iterable?: Iterable); public guild: Guild; @@ -4131,49 +3863,35 @@ export class GuildManager extends CachedManager>; } -export interface BruteforceOptions { - limit?: number; - delay?: number; - depth?: number; -} - export class GuildMemberManager extends CachedManager { private constructor(guild: Guild, iterable?: Iterable); public guild: Guild; - public readonly me: GuildMember | null; + public get me(): GuildMember | null; public add( user: UserResolvable, options: AddGuildMemberOptions & { fetchWhenExisting: false }, ): Promise; public add(user: UserResolvable, options: AddGuildMemberOptions): Promise; + public addRole(user: UserResolvable, role: RoleResolvable, reason?: string): Promise; public ban(user: UserResolvable, options?: BanOptions): Promise; public edit(user: UserResolvable, data: GuildMemberEditData, reason?: string): Promise; public fetch( options: UserResolvable | FetchMemberOptions | (FetchMembersOptions & { user: UserResolvable }), ): Promise; public fetch(options?: FetchMembersOptions): Promise>; - public fetchMemberList( - channel: GuildTextChannelResolvable, - offset?: number, - double?: boolean, - retryMax?: number, - time?: number, - ): Promise>; - public fetchBruteforce(options?: BruteforceOptions): Promise>; - public fetchByMemberSafety(timeout?: number): Promise>; public fetchMe(options?: BaseFetchOptions): Promise; public kick(user: UserResolvable, reason?: string): Promise; public list(options?: GuildListMembersOptions): Promise>; public prune(options: GuildPruneMembersOptions & { dry?: false; count: false }): Promise; public prune(options?: GuildPruneMembersOptions): Promise; - public search(options: GuildSearchMembersOptions): Promise>; - public unban(user: UserResolvable, reason?: string): Promise; - public addRole(user: UserResolvable, role: RoleResolvable, reason?: string): Promise; public removeRole( user: UserResolvable, role: RoleResolvable, reason?: string, ): Promise; + public search(options: GuildSearchMembersOptions): Promise>; + public unban(user: UserResolvable, reason?: string): Promise; + public fetchByMemberSafety(timeout?: number): Promise>; } export class GuildBanManager extends CachedManager { @@ -4274,26 +3992,6 @@ export class MessageManager extends CachedManager; } -export class InteractionManager extends CachedManager { - private constructor(channel: TextBasedChannel, iterable?: Iterable); - public channel: TextBasedChannel; - public cache: Collection; -} - -export class InteractionResponse extends Base { - private constructor(client: Client, data: object); - public readonly channel: GuildTextBasedChannel | TextBasedChannel; - public channelId: Snowflake; - public readonly createdAt: Date; - public createdTimestamp: number; - public guildId: Snowflake | null; - public readonly guild: Snowflake | null; - public id: Snowflake; - public nonce: Snowflake; - public sendData: object; - public awaitModal(time: number): Modal; -} - export interface MessageSearchOptions { authors: UserResolvable[]; content: string; @@ -4374,6 +4072,7 @@ export class RoleManager extends CachedManager public edit(role: RoleResolvable, options: RoleData, reason?: string): Promise; public delete(role: RoleResolvable, reason?: string): Promise; public setPosition(role: RoleResolvable, position: number, options?: SetRolePositionOptions): Promise; + public setPositions(rolePositions: readonly RolePosition[]): Promise; public comparePositions(role1: RoleResolvable, role2: RoleResolvable): number; } @@ -4395,10 +4094,18 @@ export class ThreadManager extends CachedManager; } +export class GuildTextThreadManager extends ThreadManager { + public create(options: GuildTextThreadCreateOptions): Promise; +} + +export class GuildForumThreadManager extends ThreadManager { + public create(options: GuildForumThreadCreateOptions): Promise; +} + export class ThreadMemberManager extends CachedManager { private constructor(thread: ThreadChannel, iterable?: Iterable); public thread: ThreadChannel; - public readonly me: ThreadMember | null; + public get me(): ThreadMember | null; public add(member: UserResolvable | '@me', reason?: string): Promise; public fetch(options?: FetchThreadMembersWithoutGuildMemberDataOptions): Promise>; public fetch(member: ThreadMember, options?: FetchMemberOptions): Promise>; @@ -4410,8 +4117,19 @@ export class ThreadMemberManager extends CachedManager>>; public fetch(member: UserResolvable, options?: FetchThreadMemberOptions): Promise; - /** @deprecated Use `fetch(member, options)` instead. */ - public fetch(cache: boolean, options?: FetchMembersOptions): Promise>; + + /** @deprecated Use `fetch(options)` instead. */ + public fetch(cache: boolean, options?: FetchThreadMembersOptions): Promise>; + + public fetch( + x: undefined, + options: FetchThreadMembersWithGuildMemberDataOptions, + ): Promise>>; + public fetch( + x: undefined, + options?: FetchThreadMembersWithoutGuildMemberDataOptions, + ): Promise>; + public fetchMe(options?: BaseFetchOptions): Promise; public remove(id: Snowflake | '@me', reason?: string): Promise; } @@ -4421,30 +4139,11 @@ export class UserManager extends CachedManager private dmChannel(userId: Snowflake): DMChannel | null; public createDM(user: UserResolvable, options?: BaseFetchOptions): Promise; public deleteDM(user: UserResolvable): Promise; - public fetch(user: UserResolvable, options?: BaseFetchOptions & { guildId?: Snowflake }): Promise; + public fetch(user: UserResolvable, options?: BaseFetchOptions): Promise; public fetchFlags(user: UserResolvable, options?: BaseFetchOptions): Promise; public send(user: UserResolvable, options: string | MessagePayload | MessageOptions): Promise; } -export class RelationshipManager { - private constructor(client: Client, users?: object[]); - public cache: Collection; - public client: Client; - public readonly friendCache: Collection; - public readonly blockedCache: Collection; - public readonly incomingCache: Collection; - public readonly outgoingCache: Collection; - public fetch(user: UserResolvable, options?: BaseFetchOptions): Promise; - public deleteFriend(user: UserResolvable): Promise; - public deleteBlocked(user: UserResolvable): Promise; - public sendFriendRequest(username: string, discriminator?: number): Promise; - public cancelFriendRequest(user: UserResolvable): Promise; - public addFriend(user: UserResolvable): Promise; - public addBlocked(user: UserResolvable): Promise; - public setNickname(user: UserResolvable, nickname: string | null): Promise; - private __cancel(id: Snowflake): Promise; -} - export class VoiceStateManager extends CachedManager { private constructor(guild: Guild, iterable?: Iterable); public guild: Guild; @@ -4475,26 +4174,14 @@ export interface TextBasedChannelFields extends PartialTextBasedChannelFields { lastPinTimestamp: number | null; readonly lastPinAt: Date | null; messages: MessageManager; - interactions: InteractionManager; - awaitMessageComponent( - options?: AwaitMessageCollectorOptionsParams, - ): Promise; awaitMessages(options?: AwaitMessagesOptions): Promise>; - bulkDelete( - messages: Collection | readonly MessageResolvable[] | number, - filterOld?: boolean, - ): Promise>; - createMessageComponentCollector( - options?: MessageChannelCollectorOptionsParams, - ): InteractionCollector; createMessageCollector(options?: MessageCollectorOptions): MessageCollector; createWebhook(name: string, options?: ChannelWebhookCreateOptions): Promise; setRateLimitPerUser(rateLimitPerUser: number, reason?: string): Promise; setNSFW(nsfw?: boolean, reason?: string): Promise; fetchWebhooks(): Promise>; sendTyping(): Promise; - sendSlash(bot: UserResolvable, commandName: string, ...args: any): Promise; - searchInteraction(applicationId: Snowflake, type?: ApplicationCommandTypes): Promise; + sendSlash(target: UserResolvable, commandName: string, ...args: any[]): Promise; } export function PartialWebhookMixin(Base?: Constructable): Constructable; @@ -4527,9 +4214,6 @@ export interface WebhookFields extends PartialWebhookFields { //#endregion //#region Typedefs -export type PurchasedFlagsString = 'NITRO_CLASSIC' | 'NITRO' | 'GUILD_BOOST'; - -export type PremiumUsageFlagsString = 'PREMIUM_DISCRIMINATOR' | 'ANIMATED_AVATAR' | 'PROFILE_BANNER'; export type ActivityFlagsString = | 'INSTANCE' @@ -4542,7 +4226,11 @@ export type ActivityFlagsString = | 'PARTY_PRIVACY_VOICE_CHANNEL' | 'EMBEDDED'; -export type ActivitiesOptions = Omit; +export type PurchasedFlagsString = 'NITRO_CLASSIC' | 'NITRO' | 'GUILD_BOOST' | 'NITRO_BASIC'; + +export type PremiumUsageFlagsString = 'PREMIUM_DISCRIMINATOR' | 'ANIMATED_AVATAR' | 'PROFILE_BANNER'; + +export type ActivitiesOptions = Omit; export interface ActivityOptions { name: string; @@ -4580,6 +4268,7 @@ export interface ClientApplicationInstallParams { scopes: InviteScope[]; permissions: Readonly; } + export interface APIErrors { UNKNOWN_ACCOUNT: 10001; UNKNOWN_APPLICATION: 10002; @@ -4637,7 +4326,6 @@ export interface APIErrors { MAXIMUM_PINS: 30003; MAXIMUM_RECIPIENTS: 30004; MAXIMUM_ROLES: 30005; - MAXIMUM_USERNAMES: 30006; MAXIMUM_WEBHOOKS: 30007; MAXIMUM_EMOJIS: 30008; MAXIMUM_REACTIONS: 30010; @@ -4680,7 +4368,6 @@ export interface APIErrors { INVALID_AUTHENTICATION_TOKEN: 50014; NOTE_TOO_LONG: 50015; INVALID_BULK_DELETE_QUANTITY: 50016; - INVALID_MFA_LEVEL: 50017; CANNOT_PIN_MESSAGE_IN_OTHER_CHANNEL: 50019; INVALID_OR_TAKEN_INVITE_CODE: 50020; CANNOT_EXECUTE_ON_SYSTEM_MESSAGE: 50021; @@ -4696,7 +4383,6 @@ export interface APIErrors { INVALID_API_VERSION: 50041; FILE_UPLOADED_EXCEEDS_MAXIMUM_SIZE: 50045; INVALID_FILE_UPLOADED: 50046; - GIFT_CODE_CLAIMED: 50050; CANNOT_SELF_REDEEM_GIFT: 50054; INVALID_GUILD: 50055; INVALID_MESSAGE_TYPE: 50068; @@ -4718,7 +4404,6 @@ export interface APIErrors { YOU_CANNOT_SEND_VOICE_MESSAGES_IN_THIS_CHANNEL: 50173; CANNOT_CONVERT_PREMIUM_EMOJI_TO_NORMAL_EMOJI: 50145; TWO_FACTOR_REQUIRED: 60003; - INVALID_TWO_FACTOR_CODE: 60008; NO_USERS_WITH_DISCORDTAG_EXIST: 80004; REACTION_BLOCKED: 90001; RESOURCE_OVERLOADED: 130000; @@ -4753,376 +4438,6 @@ export interface ApplicationAsset { type: 'BIG' | 'SMALL'; } -export interface ClientEvents extends BaseClientEvents { - /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ - applicationCommandCreate: [command: ApplicationCommand]; - /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ - applicationCommandDelete: [command: ApplicationCommand]; - /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ - applicationCommandUpdate: [oldCommand: ApplicationCommand | null, newCommand: ApplicationCommand]; - applicationCommandPermissionsUpdate: [data: ApplicationCommandPermissionsUpdateData]; - autoModerationActionExecution: [autoModerationActionExecution: AutoModerationActionExecution]; - autoModerationRuleCreate: [autoModerationRule: AutoModerationRule]; - autoModerationRuleDelete: [autoModerationRule: AutoModerationRule]; - autoModerationRuleUpdate: [ - oldAutoModerationRule: AutoModerationRule | null, - newAutoModerationRule: AutoModerationRule, - ]; - cacheSweep: [message: string]; - channelCreate: [channel: NonThreadGuildBasedChannel]; - channelDelete: [channel: DMChannel | NonThreadGuildBasedChannel]; - channelPinsUpdate: [channel: TextBasedChannel, date: Date]; - channelRecipientAdd: [channel: PartialGroupDMChannel, user: User]; - channelRecipientRemove: [channel: PartialGroupDMChannel, user: User]; - channelUpdate: [ - oldChannel: DMChannel | NonThreadGuildBasedChannel, - newChannel: DMChannel | NonThreadGuildBasedChannel, - ]; - warn: [message: string]; - emojiCreate: [emoji: GuildEmoji]; - emojiDelete: [emoji: GuildEmoji]; - emojiUpdate: [oldEmoji: GuildEmoji, newEmoji: GuildEmoji]; - error: [error: Error]; - callCreate: [call: Call]; - callDelete: [call: Call]; - callUpdate: [call: Call]; - guildAvailable: [guild: Guild]; - guildBanAdd: [ban: GuildBan]; - guildBanRemove: [ban: GuildBan]; - guildCreate: [guild: Guild]; - guildDelete: [guild: Guild]; - guildUnavailable: [guild: Guild]; - guildIntegrationsUpdate: [guild: Guild]; - guildMemberAdd: [member: GuildMember]; - guildMemberAvailable: [member: GuildMember | PartialGuildMember]; - guildMemberRemove: [member: GuildMember | PartialGuildMember]; - guildMembersChunk: [ - members: Collection, - guild: Guild, - data: { count: number; index: number; nonce: string | undefined; notFound: unknown[] }, - ]; - guildMemberUpdate: [oldMember: GuildMember | PartialGuildMember, newMember: GuildMember]; - guildMemberListUpdate: [ - members: Collection, - guild: Guild, - data: {}, // see: https://luna.gitlab.io/discord-unofficial-docs/lazy_guilds.html - ]; - guildUpdate: [oldGuild: Guild, newGuild: Guild]; - inviteCreate: [invite: Invite]; - inviteDelete: [invite: Invite]; - /** @deprecated Use messageCreate instead */ - message: [message: Message]; - messageAck: [channel: TextChannel, message_id: Snowflake, isRead: boolean, raw: object]; - messageCreate: [message: Message]; - messageDelete: [message: Message | PartialMessage]; - messageReactionRemoveAll: [ - message: Message | PartialMessage, - reactions: Collection, - ]; - messageReactionRemoveEmoji: [reaction: MessageReaction | PartialMessageReaction]; - messageDeleteBulk: [messages: Collection]; - messageReactionAdd: [reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser]; - messageReactionRemove: [reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser]; - messageUpdate: [oldMessage: Message | PartialMessage, newMessage: Message | PartialMessage]; - presenceUpdate: [oldPresence: Presence | null, newPresence: Presence]; - ready: [client: Client]; - invalidated: []; - roleCreate: [role: Role]; - roleDelete: [role: Role]; - roleUpdate: [oldRole: Role, newRole: Role]; - threadCreate: [thread: ThreadChannel, newlyCreated: boolean]; - threadDelete: [thread: ThreadChannel]; - threadListSync: [threads: Collection]; - threadMemberUpdate: [oldMember: ThreadMember, newMember: ThreadMember]; - threadMembersUpdate: [ - oldMembers: Collection, - newMembers: Collection, - ]; - threadUpdate: [oldThread: ThreadChannel, newThread: ThreadChannel]; - typingStart: [typing: Typing]; - userUpdate: [oldUser: User | PartialUser, newUser: User]; - userSettingsUpdate: [setting: RawUserSettingsData]; - userGuildSettingsUpdate: [guild: Guild]; - voiceStateUpdate: [oldState: VoiceState, newState: VoiceState]; - webhookUpdate: [channel: TextChannel | NewsChannel | VoiceChannel | ForumChannel | StageChannel]; - /** @deprecated Use interactionCreate instead */ - interaction: [interaction: Interaction]; - interactionCreate: [interaction: Interaction | { nonce: Snowflake; id: Snowflake }]; - interactionSuccess: [interaction: { nonce: Snowflake; id: Snowflake }]; - interactionFailure: [interaction: { nonce: Snowflake; id: Snowflake }]; - interactionModalCreate: [modal: Modal]; - shardDisconnect: [closeEvent: CloseEvent, shardId: number]; - shardError: [error: Error, shardId: number]; - shardReady: [shardId: number, unavailableGuilds: Set | undefined]; - shardReconnecting: [shardId: number]; - shardResume: [shardId: number, replayedEvents: number]; - stageInstanceCreate: [stageInstance: StageInstance]; - stageInstanceUpdate: [oldStageInstance: StageInstance | null, newStageInstance: StageInstance]; - stageInstanceDelete: [stageInstance: StageInstance]; - stickerCreate: [sticker: Sticker]; - stickerDelete: [sticker: Sticker]; - stickerUpdate: [oldSticker: Sticker, newSticker: Sticker]; - guildScheduledEventCreate: [guildScheduledEvent: GuildScheduledEvent]; - guildScheduledEventUpdate: [oldGuildScheduledEvent: GuildScheduledEvent, newGuildScheduledEvent: GuildScheduledEvent]; - guildScheduledEventDelete: [guildScheduledEvent: GuildScheduledEvent]; - guildScheduledEventUserAdd: [guildScheduledEvent: GuildScheduledEvent, user: User]; - guildScheduledEventUserRemove: [guildScheduledEvent: GuildScheduledEvent, user: User]; - guildAuditLogEntryCreate: [auditLogEntry: GuildAuditLogsEntry, guild: Guild]; - relationshipAdd: [id: Snowflake, type: RelationshipTypes]; - relationshipRemove: [id: Snowflake]; - relationshipUpdate: [id: Snowflake, type: RelationshipTypes, data: object]; - unhandledPacket: [packet: { op: GatewayOpcodes | number; d?: any; s?: number; t?: string }, shard: WebSocketShard]; - update: [oldVersion: string, newVersion: string]; - captchaRequired: [request: Request, captcha: Captcha]; -} - -export interface ConstantsEvents { - RATE_LIMIT: 'rateLimit'; - INVALID_REQUEST_WARNING: 'invalidRequestWarning'; - API_RESPONSE: 'apiResponse'; - API_REQUEST: 'apiRequest'; - CLIENT_READY: 'ready'; - APPLICATION_COMMAND_AUTOCOMPLETE_RESPONSE: 'applicationCommandAutocompleteResponse'; - /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ - APPLICATION_COMMAND_CREATE: 'applicationCommandCreate'; - /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ - APPLICATION_COMMAND_DELETE: 'applicationCommandDelete'; - /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ - APPLICATION_COMMAND_UPDATE: 'applicationCommandUpdate'; - APPLICATION_COMMAND_PERMISSIONS_UPDATE: 'applicationCommandPermissionsUpdate'; - AUTO_MODERATION_ACTION_EXECUTION: 'autoModerationActionExecution'; - AUTO_MODERATION_RULE_CREATE: 'autoModerationRuleCreate'; - AUTO_MODERATION_RULE_DELETE: 'autoModerationRuleDelete'; - AUTO_MODERATION_RULE_UPDATE: 'autoModerationRuleUpdate'; - CALL_CREATE: 'callCreate'; - CALL_DELETE: 'callDelete'; - CALL_UPDATE: 'callUpdate'; - GUILD_AVAILABLE: 'guildAvailable'; - GUILD_CREATE: 'guildCreate'; - GUILD_DELETE: 'guildDelete'; - GUILD_UPDATE: 'guildUpdate'; - GUILD_UNAVAILABLE: 'guildUnavailable'; - GUILD_MEMBER_ADD: 'guildMemberAdd'; - GUILD_MEMBER_REMOVE: 'guildMemberRemove'; - GUILD_MEMBER_UPDATE: 'guildMemberUpdate'; - GUILD_MEMBER_AVAILABLE: 'guildMemberAvailable'; - GUILD_MEMBERS_CHUNK: 'guildMembersChunk'; - GUILD_MEMBER_LIST_UPDATE: 'guildMemberListUpdate'; - GUILD_INTEGRATIONS_UPDATE: 'guildIntegrationsUpdate'; - GUILD_ROLE_CREATE: 'roleCreate'; - GUILD_ROLE_DELETE: 'roleDelete'; - INVITE_CREATE: 'inviteCreate'; - INVITE_DELETE: 'inviteDelete'; - GUILD_ROLE_UPDATE: 'roleUpdate'; - GUILD_EMOJI_CREATE: 'emojiCreate'; - GUILD_EMOJI_DELETE: 'emojiDelete'; - GUILD_EMOJI_UPDATE: 'emojiUpdate'; - GUILD_BAN_ADD: 'guildBanAdd'; - GUILD_BAN_REMOVE: 'guildBanRemove'; - CHANNEL_CREATE: 'channelCreate'; - CHANNEL_DELETE: 'channelDelete'; - CHANNEL_UPDATE: 'channelUpdate'; - CHANNEL_PINS_UPDATE: 'channelPinsUpdate'; - CHANNEL_RECIPIENT_ADD: 'channelRecipientAdd'; - CHANNEL_RECIPIENT_REMOVE: 'channelRecipientRemove'; - MESSAGE_ACK: 'messageAck'; - MESSAGE_CREATE: 'messageCreate'; - MESSAGE_DELETE: 'messageDelete'; - MESSAGE_UPDATE: 'messageUpdate'; - MESSAGE_BULK_DELETE: 'messageDeleteBulk'; - MESSAGE_REACTION_ADD: 'messageReactionAdd'; - MESSAGE_REACTION_REMOVE: 'messageReactionRemove'; - MESSAGE_REACTION_REMOVE_ALL: 'messageReactionRemoveAll'; - MESSAGE_REACTION_REMOVE_EMOJI: 'messageReactionRemoveEmoji'; - THREAD_CREATE: 'threadCreate'; - THREAD_DELETE: 'threadDelete'; - THREAD_UPDATE: 'threadUpdate'; - THREAD_LIST_SYNC: 'threadListSync'; - THREAD_MEMBER_UPDATE: 'threadMemberUpdate'; - THREAD_MEMBERS_UPDATE: 'threadMembersUpdate'; - USER_UPDATE: 'userUpdate'; - USER_SETTINGS_UPDATE: 'userSettingsUpdate'; - USER_GUILD_SETTINGS_UPDATE: 'userGuildSettingsUpdate'; - PRESENCE_UPDATE: 'presenceUpdate'; - VOICE_SERVER_UPDATE: 'voiceServerUpdate'; - VOICE_STATE_UPDATE: 'voiceStateUpdate'; - TYPING_START: 'typingStart'; - WEBHOOKS_UPDATE: 'webhookUpdate'; - INTERACTION_CREATE: 'interactionCreate'; - INTERACTION_SUCCESS: 'interactionSuccess'; - INTERACTION_MODAL_CREATE: 'interactionModalCreate'; - INTERACTION_FAILURE: 'interactionFailure'; - ERROR: 'error'; - WARN: 'warn'; - DEBUG: 'debug'; - CACHE_SWEEP: 'cacheSweep'; - SHARD_DISCONNECT: 'shardDisconnect'; - SHARD_ERROR: 'shardError'; - SHARD_RECONNECTING: 'shardReconnecting'; - SHARD_READY: 'shardReady'; - SHARD_RESUME: 'shardResume'; - INVALIDATED: 'invalidated'; - RAW: 'raw'; - STAGE_INSTANCE_CREATE: 'stageInstanceCreate'; - STAGE_INSTANCE_UPDATE: 'stageInstanceUpdate'; - STAGE_INSTANCE_DELETE: 'stageInstanceDelete'; - GUILD_STICKER_CREATE: 'stickerCreate'; - GUILD_STICKER_DELETE: 'stickerDelete'; - GUILD_STICKER_UPDATE: 'stickerUpdate'; - GUILD_SCHEDULED_EVENT_CREATE: 'guildScheduledEventCreate'; - GUILD_SCHEDULED_EVENT_UPDATE: 'guildScheduledEventUpdate'; - GUILD_SCHEDULED_EVENT_DELETE: 'guildScheduledEventDelete'; - GUILD_SCHEDULED_EVENT_USER_ADD: 'guildScheduledEventUserAdd'; - GUILD_SCHEDULED_EVENT_USER_REMOVE: 'guildScheduledEventUserRemove'; - GUILD_AUDIT_LOG_ENTRY_CREATE: 'guildAuditLogEntryCreate'; - RELATIONSHIP_ADD: 'relationshipAdd'; - RELATIONSHIP_REMOVE: 'relationshipRemove'; - RELATIONSHIP_UPDATE: 'relationshipUpdate'; - UNHANDLED_PACKET: 'unhandledPacket'; - CAPTCHA_REQUIRED: 'captchaRequired'; -} - -export interface WebEmbedOptions { - shorten?: boolean; - hidden?: boolean; - title?: string; - description?: string; - url?: string; - timestamp?: Date | number; - color?: ColorResolvable; - fields?: EmbedFieldData[]; - author?: Partial & { icon_url?: string; proxy_icon_url?: string }; - thumbnail?: Partial & { proxy_url?: string }; - image?: Partial & { proxy_url?: string }; - video?: Partial & { proxy_url?: string }; - footer?: Partial & { icon_url?: string; proxy_icon_url?: string }; - imageType?: 'thumbnail' | 'image'; - redirect?: string; -} -// export interface MessageOptions -// embeds?: (WebEmbed | MessageEmbed | MessageEmbedOptions | APIEmbed)[]; - -/** - * @extends https://luna.gitlab.io/discord-unofficial-docs/user_settings.html - */ -export interface RawUserSettingsData { - afk_timeout?: number; - allow_accessibility_detection?: boolean; - animate_emoji?: boolean; - animate_stickers?: number; - contact_sync_enabled?: boolean; - convert_emoticons?: boolean; - custom_status?: { text?: string; expires_at?: string | null; emoji_name?: string; emoji_id?: Snowflake | null }; - default_guilds_restricted?: boolean; - detect_platform_accounts?: boolean; - developer_mode?: boolean; - disable_games_tab?: boolean; - enable_tts_command?: boolean; - explicit_content_filter?: DMScanLevel; - friend_discovery_flags?: number; - friend_source_flags?: { all?: boolean; mutual_friends?: boolean; mututal_guilds?: boolean }; - gif_auto_play?: boolean; - guild_folders?: { id?: Snowflake; guild_ids?: Snowflake[]; name?: string }[]; - guild_positions?: number[]; - inline_attachment_media?: boolean; - inline_embed_media?: boolean; - locale?: string; - message_display_compact?: boolean; - native_phone_integration_enabled?: boolean; - render_embeds?: boolean; - render_reactions?: boolean; - restricted_guilds?: any[]; - show_current_game?: boolean; - status?: PresenceStatusData; - stream_notifications_enabled?: boolean; - theme?: 'dark' | 'light'; - timezone_offset?: number; - view_nsfw_guilds?: boolean; -} - -export interface RawGuildSettingsData { - guild_id: Snowflake; - suppress_everyone: boolean; - suppress_roles: boolean; - mute_scheduled_events: boolean; - message_notifications: 2; - flags: 0; - mobile_push: boolean; - muted: boolean; - mute_config?: RawMuteConfigData; - hide_muted_channels: boolean; - channel_overrides: RawGuildChannelSettingsData[]; - notify_highlights: number; - version: number; -} - -export interface RawGuildChannelSettingsData { - channel_id: Snowflake; - message_notifications: number; - muted: boolean; - mute_config?: RawMuteConfigData; - collapsed: boolean; -} - -export interface RawMuteConfigData { - end_time: string; - selected_time_window: number; -} - -export interface MuteConfigData { - endTime: Date; - selectedTimeWindow: number; -} - -export interface ClientOptions { - shards?: number | number[] | 'auto'; - shardCount?: number; - closeTimeout?: number; - makeCache?: CacheFactory; - /** @deprecated Pass the value of this property as `lifetime` to `sweepers.messages` instead. */ - messageCacheLifetime?: number; - /** @deprecated Pass the value of this property as `interval` to `sweepers.messages` instead. */ - messageSweepInterval?: number; - allowedMentions?: MessageMentionOptions; - invalidRequestWarningInterval?: number; - partials?: PartialTypes[]; - restWsBridgeTimeout?: number; - restTimeOffset?: number; - restRequestTimeout?: number; - restGlobalRateLimit?: number; - restSweepInterval?: number; - retryLimit?: number; - failIfNotExists?: boolean; - userAgentSuffix?: string[]; - presence?: PresenceData; - intents?: BitFieldResolvable; - waitGuildTimeout?: number; - messageCreateEventGuildTimeout?: number; - sweepers?: SweeperOptions; - ws?: WebSocketOptions; - http?: HTTPOptions; - rejectOnRateLimit?: string[] | ((data: RateLimitData) => boolean | Promise); - // add - checkUpdate?: boolean; - syncStatus?: boolean; - autoRedeemNitro?: boolean; - patchVoice?: boolean; - password?: string; - DMSync?: boolean; - proxy?: string; - captchaService?: captchaServices; - captchaKey?: string; - captchaSolver?: (data: Captcha, userAgent: string) => Promise; - captchaRetryLimit?: number; - captchaWithProxy?: boolean; - interactionTimeout?: number; - usingNewAttachmentAPI?: boolean; -} - -export type captchaServices = '2captcha' | 'capmonster'; - -// end copy - export interface BaseApplicationCommandData { name: string; nameLocalizations?: LocalizationMap; @@ -5200,12 +4515,12 @@ export interface ApplicationCommandChannelOption extends BaseApplicationCommandO export interface ApplicationCommandAutocompleteOption extends Omit { type: - | 'STRING' - | 'NUMBER' - | 'INTEGER' - | ApplicationCommandOptionTypes.STRING - | ApplicationCommandOptionTypes.NUMBER - | ApplicationCommandOptionTypes.INTEGER; + | 'STRING' + | 'NUMBER' + | 'INTEGER' + | ApplicationCommandOptionTypes.STRING + | ApplicationCommandOptionTypes.NUMBER + | ApplicationCommandOptionTypes.INTEGER; autocomplete: true; } @@ -5417,16 +4732,7 @@ export type ApplicationCommandPermissionType = keyof typeof ApplicationCommandPe export type ApplicationCommandResolvable = ApplicationCommand | Snowflake; export type ApplicationFlagsString = - | 'EMBEDDED_RELEASED' - | 'MANAGED_EMOJI' - | 'EMBEDDED_IAP' - | 'GROUP_DM_CREATE' - | 'RPC_PRIVATE_BETA' | 'APPLICATION_AUTO_MODERATION_RULE_CREATE_BADGE' - | 'ALLOW_ASSETS' - | 'ALLOW_ACTIVITY_ACTION_SPECTATE' - | 'ALLOW_ACTIVITY_ACTION_JOIN_REQUEST' - | 'RPC_HAS_CONNECTED' | 'GATEWAY_PRESENCE' | 'GATEWAY_PRESENCE_LIMITED' | 'GATEWAY_GUILD_MEMBERS' @@ -5435,10 +4741,7 @@ export type ApplicationFlagsString = | 'EMBEDDED' | 'GATEWAY_MESSAGE_CONTENT' | 'GATEWAY_MESSAGE_CONTENT_LIMITED' - | 'EMBEDDED_FIRST_PARTY' - | 'APPLICATION_COMMAND_BADGE' - | 'ACTIVE' - | 'IFRAME_MODAL'; + | 'APPLICATION_COMMAND_BADGE'; export interface ApplicationRoleConnectionMetadataEditOptions { name: string; @@ -5489,9 +4792,9 @@ export interface AutoModerationRuleCreateOptions { reason?: string; } -export interface AutoModerationRuleEditOptions extends Partial> {} +export interface AutoModerationRuleEditOptions extends Partial> { } -export interface AutoModerationTriggerMetadataOptions extends Partial {} +export interface AutoModerationTriggerMetadataOptions extends Partial { } export interface AutoModerationActionOptions { type: AutoModerationActionType | AutoModerationActionTypes; @@ -5598,10 +4901,9 @@ export type CacheFactory = ( export type CacheWithLimitsOptions = { [K in keyof Caches]?: Caches[K][0]['prototype'] extends DataManager - ? LimitedCollectionOptions | number - : never; + ? LimitedCollectionOptions | number + : never; }; - export interface CategoryCreateChannelOptions { permissionOverwrites?: OverwriteResolvable[] | Collection; topic?: string; @@ -5691,10 +4993,162 @@ export interface BaseClientEvents { invalidRequestWarning: [invalidRequestWarningData: InvalidRequestWarningData]; } +export interface ClientEvents extends BaseClientEvents { + /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ + applicationCommandCreate: [command: ApplicationCommand]; + /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ + applicationCommandDelete: [command: ApplicationCommand]; + /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ + applicationCommandUpdate: [oldCommand: ApplicationCommand | null, newCommand: ApplicationCommand]; + applicationCommandPermissionsUpdate: [data: ApplicationCommandPermissionsUpdateData]; + autoModerationActionExecution: [autoModerationActionExecution: AutoModerationActionExecution]; + autoModerationRuleCreate: [autoModerationRule: AutoModerationRule]; + autoModerationRuleDelete: [autoModerationRule: AutoModerationRule]; + autoModerationRuleUpdate: [ + oldAutoModerationRule: AutoModerationRule | null, + newAutoModerationRule: AutoModerationRule, + ]; + cacheSweep: [message: string]; + channelCreate: [channel: NonThreadGuildBasedChannel]; + channelDelete: [channel: DMChannel | NonThreadGuildBasedChannel]; + channelPinsUpdate: [channel: TextBasedChannel, date: Date]; + channelUpdate: [ + oldChannel: DMChannel | NonThreadGuildBasedChannel, + newChannel: DMChannel | NonThreadGuildBasedChannel, + ]; + warn: [message: string]; + emojiCreate: [emoji: GuildEmoji]; + emojiDelete: [emoji: GuildEmoji]; + emojiUpdate: [oldEmoji: GuildEmoji, newEmoji: GuildEmoji]; + error: [error: Error]; + guildAvailable: [guild: Guild]; + guildBanAdd: [ban: GuildBan]; + guildBanRemove: [ban: GuildBan]; + guildCreate: [guild: Guild]; + guildDelete: [guild: Guild]; + guildUnavailable: [guild: Guild]; + guildIntegrationsUpdate: [guild: Guild]; + guildMemberAdd: [member: GuildMember]; + guildMemberAvailable: [member: GuildMember | PartialGuildMember]; + guildMemberRemove: [member: GuildMember | PartialGuildMember]; + guildMembersChunk: [ + members: Collection, + guild: Guild, + data: { count: number; index: number; nonce: string | undefined; notFound: unknown[] }, + ]; + guildMemberUpdate: [oldMember: GuildMember | PartialGuildMember, newMember: GuildMember]; + guildUpdate: [oldGuild: Guild, newGuild: Guild]; + inviteCreate: [invite: Invite]; + inviteDelete: [invite: Invite]; + /** @deprecated Use messageCreate instead */ + message: [message: Message]; + messageCreate: [message: Message]; + messageDelete: [message: Message | PartialMessage]; + messageReactionRemoveAll: [ + message: Message | PartialMessage, + reactions: Collection, + ]; + messageReactionRemoveEmoji: [reaction: MessageReaction | PartialMessageReaction]; + messageDeleteBulk: [messages: Collection]; + messageReactionAdd: [reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser]; + messageReactionRemove: [reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser]; + messageUpdate: [oldMessage: Message | PartialMessage, newMessage: Message | PartialMessage]; + presenceUpdate: [oldPresence: Presence | null, newPresence: Presence]; + ready: [client: Client]; + invalidated: []; + roleCreate: [role: Role]; + roleDelete: [role: Role]; + roleUpdate: [oldRole: Role, newRole: Role]; + threadCreate: [thread: ThreadChannel, newlyCreated: boolean]; + threadDelete: [thread: ThreadChannel]; + threadListSync: [threads: Collection]; + threadMemberUpdate: [oldMember: ThreadMember, newMember: ThreadMember]; + threadMembersUpdate: [ + oldMembers: Collection, + newMembers: Collection, + ]; + threadUpdate: [oldThread: ThreadChannel, newThread: ThreadChannel]; + typingStart: [typing: Typing]; + userUpdate: [oldUser: User | PartialUser, newUser: User]; + voiceStateUpdate: [oldState: VoiceState, newState: VoiceState]; + webhookUpdate: [channel: TextChannel | NewsChannel | VoiceChannel | ForumChannel | StageChannel]; + shardDisconnect: [closeEvent: CloseEvent, shardId: number]; + shardError: [error: Error, shardId: number]; + shardReady: [shardId: number, unavailableGuilds: Set | undefined]; + shardReconnecting: [shardId: number]; + shardResume: [shardId: number, replayedEvents: number]; + stageInstanceCreate: [stageInstance: StageInstance]; + stageInstanceUpdate: [oldStageInstance: StageInstance | null, newStageInstance: StageInstance]; + stageInstanceDelete: [stageInstance: StageInstance]; + stickerCreate: [sticker: Sticker]; + stickerDelete: [sticker: Sticker]; + stickerUpdate: [oldSticker: Sticker, newSticker: Sticker]; + guildScheduledEventCreate: [guildScheduledEvent: GuildScheduledEvent]; + guildScheduledEventUpdate: [oldGuildScheduledEvent: GuildScheduledEvent, newGuildScheduledEvent: GuildScheduledEvent]; + guildScheduledEventDelete: [guildScheduledEvent: GuildScheduledEvent]; + guildScheduledEventUserAdd: [guildScheduledEvent: GuildScheduledEvent, user: User]; + guildScheduledEventUserRemove: [guildScheduledEvent: GuildScheduledEvent, user: User]; + guildAuditLogEntryCreate: [auditLogEntry: GuildAuditLogsEntry, guild: Guild]; + unhandledPacket: [packet: { t?: string; d: any }, shard: number]; + relationshipAdd: [userId: Snowflake, shouldNotify: boolean]; + relationshipRemove: [userId: Snowflake, type: RelationshipTypes, nickname: string | null]; + relationshipUpdate: [ + userId: Snowflake, + oldData: { + nickname: string | null; + since: Date; + type: RelationshipTypes; + }, + newData: { + nickname: string | null; + since: Date; + type: RelationshipTypes; + }, + ]; + channelRecipientAdd: [channel: GroupDMChannel, user: User]; + channelRecipientRemove: [channel: GroupDMChannel, user: User]; + interactionModalCreate: [modal: Modal]; + callCreate: [call: CallState]; + callUpdate: [call: CallState]; + callDelete: [call: CallState]; +} + export interface ClientFetchInviteOptions { guildScheduledEventId?: Snowflake; } +export type CaptchaSolver = (captcha: Captcha, UserAgent: string) => Promise; + +export interface ClientOptions { + messageCreateEventGuildTimeout?: number; + DMChannelVoiceStatusSync?: number; + captchaRetryLimit?: number; + captchaSolver?: CaptchaSolver; + closeTimeout?: number; + makeCache?: CacheFactory; + /** @deprecated Pass the value of this property as `lifetime` to `sweepers.messages` instead. */ + messageCacheLifetime?: number; + /** @deprecated Pass the value of this property as `interval` to `sweepers.messages` instead. */ + messageSweepInterval?: number; + allowedMentions?: MessageMentionOptions; + invalidRequestWarningInterval?: number; + partials?: PartialTypes[]; + restWsBridgeTimeout?: number; + restTimeOffset?: number; + restRequestTimeout?: number; + restGlobalRateLimit?: number; + restSweepInterval?: number; + retryLimit?: number; + failIfNotExists?: boolean; + userAgentSuffix?: string[]; + presence?: PresenceData; + waitGuildTimeout?: number; + sweepers?: SweeperOptions; + ws?: WebSocketOptions; + http?: HTTPOptions; + rejectOnRateLimit?: string[] | ((data: RateLimitData) => boolean | Promise); +} + export type ClientPresenceStatus = 'online' | 'idle' | 'dnd'; export interface ClientPresenceStatusData { @@ -5707,7 +5161,7 @@ export interface ClientUserEditData { username?: string; avatar?: BufferResolvable | Base64Resolvable | null; banner?: BufferResolvable | Base64Resolvable | null; - bio?: string | null; + bio?: string; } export interface CloseEvent { @@ -5799,12 +5253,12 @@ export interface ConstantsClientApplicationAssetTypes { export type AutocompleteFocusedOption = Pick & { focused: true; type: - | 'STRING' - | 'INTEGER' - | 'NUMBER' - | ApplicationCommandOptionTypes.STRING - | ApplicationCommandOptionTypes.INTEGER - | ApplicationCommandOptionTypes.NUMBER; + | 'STRING' + | 'INTEGER' + | 'NUMBER' + | ApplicationCommandOptionTypes.STRING + | ApplicationCommandOptionTypes.INTEGER + | ApplicationCommandOptionTypes.NUMBER; value: string; }; @@ -5841,6 +5295,103 @@ export interface ConstantsColors { NOT_QUITE_BLACK: 0x23272a; } +export interface ConstantsEvents { + RATE_LIMIT: 'rateLimit'; + INVALID_REQUEST_WARNING: 'invalidRequestWarning'; + API_RESPONSE: 'apiResponse'; + API_REQUEST: 'apiRequest'; + CLIENT_READY: 'ready'; + /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ + APPLICATION_COMMAND_CREATE: 'applicationCommandCreate'; + /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ + APPLICATION_COMMAND_DELETE: 'applicationCommandDelete'; + APPLICATION_COMMAND_PERMISSIONS_UPDATE: 'applicationCommandPermissionsUpdate'; + /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ + APPLICATION_COMMAND_UPDATE: 'applicationCommandUpdate'; + AUTO_MODERATION_ACTION_EXECUTION: 'autoModerationActionExecution'; + AUTO_MODERATION_RULE_CREATE: 'autoModerationRuleCreate'; + AUTO_MODERATION_RULE_DELETE: 'autoModerationRuleDelete'; + AUTO_MODERATION_RULE_UPDATE: 'autoModerationRuleUpdate'; + GUILD_AVAILABLE: 'guildAvailable'; + GUILD_CREATE: 'guildCreate'; + GUILD_DELETE: 'guildDelete'; + GUILD_UPDATE: 'guildUpdate'; + GUILD_UNAVAILABLE: 'guildUnavailable'; + GUILD_MEMBER_ADD: 'guildMemberAdd'; + GUILD_MEMBER_REMOVE: 'guildMemberRemove'; + GUILD_MEMBER_UPDATE: 'guildMemberUpdate'; + GUILD_MEMBER_AVAILABLE: 'guildMemberAvailable'; + GUILD_MEMBERS_CHUNK: 'guildMembersChunk'; + GUILD_INTEGRATIONS_UPDATE: 'guildIntegrationsUpdate'; + GUILD_ROLE_CREATE: 'roleCreate'; + GUILD_ROLE_DELETE: 'roleDelete'; + INVITE_CREATE: 'inviteCreate'; + INVITE_DELETE: 'inviteDelete'; + GUILD_ROLE_UPDATE: 'roleUpdate'; + GUILD_EMOJI_CREATE: 'emojiCreate'; + GUILD_EMOJI_DELETE: 'emojiDelete'; + GUILD_EMOJI_UPDATE: 'emojiUpdate'; + GUILD_BAN_ADD: 'guildBanAdd'; + GUILD_BAN_REMOVE: 'guildBanRemove'; + CHANNEL_CREATE: 'channelCreate'; + CHANNEL_DELETE: 'channelDelete'; + CHANNEL_UPDATE: 'channelUpdate'; + CHANNEL_PINS_UPDATE: 'channelPinsUpdate'; + MESSAGE_CREATE: 'messageCreate'; + MESSAGE_DELETE: 'messageDelete'; + MESSAGE_UPDATE: 'messageUpdate'; + MESSAGE_BULK_DELETE: 'messageDeleteBulk'; + MESSAGE_REACTION_ADD: 'messageReactionAdd'; + MESSAGE_REACTION_REMOVE: 'messageReactionRemove'; + MESSAGE_REACTION_REMOVE_ALL: 'messageReactionRemoveAll'; + MESSAGE_REACTION_REMOVE_EMOJI: 'messageReactionRemoveEmoji'; + THREAD_CREATE: 'threadCreate'; + THREAD_DELETE: 'threadDelete'; + THREAD_UPDATE: 'threadUpdate'; + THREAD_LIST_SYNC: 'threadListSync'; + THREAD_MEMBER_UPDATE: 'threadMemberUpdate'; + THREAD_MEMBERS_UPDATE: 'threadMembersUpdate'; + USER_UPDATE: 'userUpdate'; + PRESENCE_UPDATE: 'presenceUpdate'; + VOICE_SERVER_UPDATE: 'voiceServerUpdate'; + VOICE_STATE_UPDATE: 'voiceStateUpdate'; + TYPING_START: 'typingStart'; + WEBHOOKS_UPDATE: 'webhookUpdate'; + ERROR: 'error'; + WARN: 'warn'; + DEBUG: 'debug'; + CACHE_SWEEP: 'cacheSweep'; + SHARD_DISCONNECT: 'shardDisconnect'; + SHARD_ERROR: 'shardError'; + SHARD_RECONNECTING: 'shardReconnecting'; + SHARD_READY: 'shardReady'; + SHARD_RESUME: 'shardResume'; + INVALIDATED: 'invalidated'; + RAW: 'raw'; + STAGE_INSTANCE_CREATE: 'stageInstanceCreate'; + STAGE_INSTANCE_UPDATE: 'stageInstanceUpdate'; + STAGE_INSTANCE_DELETE: 'stageInstanceDelete'; + GUILD_STICKER_CREATE: 'stickerCreate'; + GUILD_STICKER_DELETE: 'stickerDelete'; + GUILD_STICKER_UPDATE: 'stickerUpdate'; + GUILD_SCHEDULED_EVENT_CREATE: 'guildScheduledEventCreate'; + GUILD_SCHEDULED_EVENT_UPDATE: 'guildScheduledEventUpdate'; + GUILD_SCHEDULED_EVENT_DELETE: 'guildScheduledEventDelete'; + GUILD_SCHEDULED_EVENT_USER_ADD: 'guildScheduledEventUserAdd'; + GUILD_SCHEDULED_EVENT_USER_REMOVE: 'guildScheduledEventUserRemove'; + GUILD_AUDIT_LOG_ENTRY_CREATE: 'guildAuditLogEntryCreate'; + UNHANDLED_PACKET: 'unhandledPacket'; + RELATIONSHIP_ADD: 'relationshipAdd'; + RELATIONSHIP_UPDATE: 'relationshipUpdate'; + RELATIONSHIP_REMOVE: 'relationshipRemove'; + CHANNEL_RECIPIENT_ADD: 'channelRecipientAdd'; + CHANNEL_RECIPIENT_REMOVE: 'channelRecipientRemove'; + INTERACTION_MODAL_CREATE: 'interactionModalCreate'; + CALL_CREATE: 'callCreate'; + CALL_UPDATE: 'callUpdate'; + CALL_DELETE: 'callDelete'; +} + export interface ConstantsOpcodes { DISPATCH: 0; HEARTBEAT: 1; @@ -5854,6 +5405,29 @@ export interface ConstantsOpcodes { INVALID_SESSION: 9; HELLO: 10; HEARTBEAT_ACK: 11; + GUILD_SYNC: 12; + DM_UPDATE: 13; + GUILD_SUBSCRIPTIONS: 14; + LOBBY_CONNECT: 15; + LOBBY_DISCONNECT: 16; + LOBBY_VOICE_STATE_UPDATE: 17; + STREAM_CREATE: 18; + STREAM_DELETE: 19; + STREAM_WATCH: 20; + STREAM_PING: 21; + STREAM_SET_PAUSED: 22; + REQUEST_GUILD_APPLICATION_COMMANDS: 24; + EMBEDDED_ACTIVITY_LAUNCH: 25; + EMBEDDED_ACTIVITY_CLOSE: 26; + EMBEDDED_ACTIVITY_UPDATE: 27; + REQUEST_FORUM_UNREADS: 28; + REMOTE_COMMAND: 29; + GET_DELETED_ENTITY_IDS_NOT_MATCHING_HASH: 30; + REQUEST_SOUNDBOARD_SOUNDS: 31; + SPEED_TEST_CREATE: 32; + SPEED_TEST_DELETE: 33; + REQUEST_LAST_MESSAGES: 34; + SEARCH_RECENT_MEMBERS: 35; } export interface ConstantsShardEvents { @@ -5978,7 +5552,7 @@ export type ExplicitContentFilterLevel = keyof typeof ExplicitContentFilterLevel export interface FetchApplicationCommandOptions extends BaseFetchOptions { guildId?: Snowflake; - locale?: localeSetting; + locale?: LocaleString; withLocalizations?: boolean; } @@ -6074,6 +5648,7 @@ export interface FetchReactionUsersOptions { export interface FetchThreadMemberOptions extends BaseFetchOptions { withMember?: boolean; } + export interface FetchThreadMembersWithGuildMemberDataOptions { withMember: true; after?: Snowflake; @@ -6082,7 +5657,7 @@ export interface FetchThreadMembersWithGuildMemberDataOptions { } export interface FetchThreadMembersWithoutGuildMemberDataOptions { - withMember: false; + withMember?: false; cache?: boolean; } @@ -6236,20 +5811,20 @@ export interface GuildAuditLogsEntryExtraField { MESSAGE_UNPIN: { channel: GuildTextBasedChannel | { id: Snowflake }; messageId: Snowflake }; MEMBER_DISCONNECT: { count: number }; CHANNEL_OVERWRITE_CREATE: - | Role - | GuildMember - | { id: Snowflake; name: string; type: OverwriteTypes.role } - | { id: Snowflake; type: OverwriteTypes.member }; + | Role + | GuildMember + | { id: Snowflake; name: string; type: OverwriteTypes.role } + | { id: Snowflake; type: OverwriteTypes.member }; CHANNEL_OVERWRITE_UPDATE: - | Role - | GuildMember - | { id: Snowflake; name: string; type: OverwriteTypes.role } - | { id: Snowflake; type: OverwriteTypes.member }; + | Role + | GuildMember + | { id: Snowflake; name: string; type: OverwriteTypes.role } + | { id: Snowflake; type: OverwriteTypes.member }; CHANNEL_OVERWRITE_DELETE: - | Role - | GuildMember - | { id: Snowflake; name: string; type: OverwriteTypes.role } - | { id: Snowflake; type: OverwriteTypes.member }; + | Role + | GuildMember + | { id: Snowflake; name: string; type: OverwriteTypes.role } + | { id: Snowflake; type: OverwriteTypes.member }; STAGE_INSTANCE_CREATE: StageChannel | { id: Snowflake }; STAGE_INSTANCE_DELETE: StageChannel | { id: Snowflake }; STAGE_INSTANCE_UPDATE: StageChannel | { id: Snowflake }; @@ -6280,8 +5855,8 @@ export interface GuildAuditLogsEntryTargetField | RoleResolvable[]; reason?: string; @@ -6397,7 +5978,6 @@ export type GuildFeatures = | 'ANIMATED_ICON' | 'AUTO_MODERATION' | 'BANNER' - | 'CLYDE_ENABLED' | 'COMMERCE' | 'COMMUNITY' | 'CREATOR_MONETIZABLE_PROVISIONAL' @@ -6419,8 +5999,8 @@ export type GuildFeatures = | 'THREE_DAY_THREAD_ARCHIVE' | 'SEVEN_DAY_THREAD_ARCHIVE' | 'PRIVATE_THREADS' - | 'RAID_ALERTS_DISABLED' | 'ROLE_ICONS' + | 'RAID_ALERTS_DISABLED' | 'ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE' | 'ROLE_SUBSCRIPTIONS_ENABLED'; @@ -6514,8 +6094,8 @@ export type GuildScheduledEventManagerFetchResult< export type GuildScheduledEventManagerFetchSubscribersResult = T extends { withMember: true } - ? Collection> - : Collection>; + ? Collection> + : Collection>; export type GuildScheduledEventPrivacyLevel = keyof typeof GuildScheduledEventPrivacyLevels; @@ -6550,6 +6130,7 @@ export interface HTTPAttachmentData { export interface HTTPErrorData { json: unknown; files: HTTPAttachmentData[]; + headers?: Record; } export interface HTTPOptions { @@ -6625,13 +6206,6 @@ export type IntentsString = | 'AUTO_MODERATION_CONFIGURATION' | 'AUTO_MODERATION_EXECUTION'; -export interface InviteGenerationOptions { - permissions?: PermissionResolvable; - guild?: GuildResolvable; - disableGuildSelect?: boolean; - scopes: InviteScope[]; -} - export type GuildInvitableChannelResolvable = | TextChannel | VoiceChannel @@ -6667,7 +6241,8 @@ export type InviteScope = | 'guilds' | 'guilds.join' | 'gdm.join' - | 'webhook.incoming'; + | 'webhook.incoming' + | 'role_connections.write'; export interface LifetimeFilterOptions { excludeFromSweep?: (value: V, key: K, collection: LimitedCollection) => boolean; @@ -6707,8 +6282,8 @@ export type ModalActionRowComponentResolvable = export interface MessageActionRowOptions< T extends - | MessageActionRowComponentResolvable - | ModalActionRowComponentResolvable = MessageActionRowComponentResolvable, + | MessageActionRowComponentResolvable + | ModalActionRowComponentResolvable = MessageActionRowComponentResolvable, > extends BaseMessageComponentOptions { components: T[]; } @@ -6766,35 +6341,16 @@ export type MessageComponentType = keyof typeof MessageComponentTypes; export type MessageComponentTypeResolvable = MessageComponentType | MessageComponentTypes; +export type GuildForumThreadMessageCreateOptions = MessageOptions & Pick; + export interface MessageEditOptions { attachments?: MessageAttachment[]; content?: string | null; - id?: Snowflake | number; - parentId?: Snowflake | number; - type?: ExcludeEnum< - typeof ChannelTypes, - | 'DM' - | 'GROUP_DM' - | 'GUILD_NEWS' - | 'GUILD_STORE' - | 'UNKNOWN' - | 'GUILD_NEWS_THREAD' - | 'GUILD_PUBLIC_THREAD' - | 'GUILD_PRIVATE_THREAD' - | 'GUILD_STAGE_VOICE' - >; - name: string; - topic?: string; - nsfw?: boolean; - bitrate?: number; - userLimit?: number; - rtcRegion?: string | null; - videoQualityMode?: VideoQualityMode; - permissionOverwrites?: PartialOverwriteData[]; - rateLimitPerUser?: number; - availableTags?: GuildForumTagData[]; - defaultReactionEmoji?: DefaultReactionEmoji; - defaultThreadRateLimitPerUser?: number; + embeds?: (MessageEmbed | MessageEmbedOptions | APIEmbed)[] | null; + files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[]; + flags?: BitFieldResolvable; + allowedMentions?: MessageMentionOptions; + components?: (MessageActionRow | (Required & MessageActionRowOptions))[]; } export interface MessageEmbedAuthor { @@ -6897,15 +6453,14 @@ export interface MessageOptions { tts?: boolean; nonce?: string | number; content?: string | null; - embeds?: (WebEmbed | MessageEmbed | MessageEmbedOptions | APIEmbed)[]; + embeds?: (MessageEmbed | MessageEmbedOptions | APIEmbed)[]; components?: (MessageActionRow | (Required & MessageActionRowOptions))[]; allowedMentions?: MessageMentionOptions; files?: (FileOptions | BufferResolvable | Stream | MessageAttachment)[]; reply?: ReplyOptions; stickers?: StickerResolvable[]; attachments?: MessageAttachment[]; - flags?: BitFieldResolvable<'SUPPRESS_EMBEDS' | 'SUPPRESS_NOTIFICATIONS', number>; - usingNewAttachmentAPI?: boolean; + flags?: BitFieldResolvable<'SUPPRESS_EMBEDS' | 'SUPPRESS_NOTIFICATIONS' | 'IS_VOICE_MESSAGE', number>; } export type MessageReactionResolvable = MessageReaction | Snowflake | string; @@ -6926,12 +6481,11 @@ export interface BaseMessageSelectMenuOptions { minValues?: number; placeholder?: string; } + export interface StringMessageSelectMenuOptions extends BaseMessageSelectMenuOptions { type?: - | 'STRING_SELECT' - | 'SELECT_MENU' - | SelectMenuComponentTypes.STRING_SELECT - | SelectMenuComponentTypes.SELECT_MENU; + | 'STRING_SELECT' + | SelectMenuComponentTypes.STRING_SELECT options?: MessageSelectOptionData[]; } @@ -6982,8 +6536,8 @@ export type MFALevel = keyof typeof MFALevels; export interface ModalOptions { components: - | MessageActionRow[] - | MessageActionRowOptions[]; + | MessageActionRow[] + | MessageActionRowOptions[]; customId: string; title: string; } @@ -7099,7 +6653,7 @@ export type PremiumTier = keyof typeof PremiumTiers; export interface PresenceData { status?: PresenceStatusData; afk?: boolean; - activities?: ActivitiesOptions[]; + activities?: (ActivitiesOptions | RichPresence | SpotifyRPC | CustomStatus)[]; shardId?: number | number[]; } @@ -7129,6 +6683,9 @@ export interface PartialChannelData { videoQualityMode?: VideoQualityMode; permissionOverwrites?: PartialOverwriteData[]; rateLimitPerUser?: number; + availableTags?: GuildForumTagData[]; + defaultReactionEmoji?: DefaultReactionEmoji; + defaultThreadRateLimitPerUser?: number; } export type Partialize< @@ -7141,19 +6698,19 @@ export type Partialize< id: Snowflake; partial: true; } & { - [K in keyof Omit]: K extends N ? null : K extends M ? T[K] | null : T[K]; -}; + [K in keyof Omit]: K extends N ? null : K extends M ? T[K] | null : T[K]; + }; export interface PartialDMChannel extends Partialize { lastMessageId: undefined; } -export interface PartialGuildMember extends Partialize {} +export interface PartialGuildMember extends Partialize { } export interface PartialMessage - extends Partialize {} + extends Partialize { } -export interface PartialMessageReaction extends Partialize {} +export interface PartialMessageReaction extends Partialize { } export interface PartialOverwriteData { id: Snowflake | number; @@ -7168,7 +6725,7 @@ export interface PartialRoleData extends RoleData { export type PartialTypes = 'USER' | 'CHANNEL' | 'GUILD_MEMBER' | 'MESSAGE' | 'REACTION' | 'GUILD_SCHEDULED_EVENT'; -export interface PartialUser extends Partialize {} +export interface PartialUser extends Partialize { } export type PresenceStatusData = ClientPresenceStatus | 'invisible'; @@ -7289,11 +6846,6 @@ export interface StartThreadOptions { rateLimitPerUser?: number; } -export interface MessageButtonLocation { - row: number; - col: number; -} - export type Status = number; export type StickerFormatType = keyof typeof StickerFormatTypes; @@ -7312,6 +6864,10 @@ export type SystemChannelFlagsString = export type SystemChannelFlagsResolvable = BitFieldResolvable; +export type ChannelFlagsResolvable = BitFieldResolvable; + +export type SelectMenuComponentType = keyof typeof SelectMenuComponentTypes; + export type SystemMessageType = Exclude< MessageType, 'DEFAULT' | 'REPLY' | 'APPLICATION_COMMAND' | 'CONTEXT_MENU_COMMAND' @@ -7363,8 +6919,8 @@ export interface SweeperDefinitions { export type SweeperOptions = { [K in keyof SweeperDefinitions]?: SweeperDefinitions[K][2] extends true - ? SweepOptions | LifetimeSweepOptions - : SweepOptions; + ? SweepOptions | LifetimeSweepOptions + : SweepOptions; }; export interface LimitedCollectionOptions { @@ -7386,13 +6942,9 @@ export type AnyChannel = | TextChannel | ThreadChannel | VoiceChannel - | ForumChannel - | PartialGroupDMChannel; + | ForumChannel; -export type TextBasedChannel = Exclude< - Extract, - ForumChannel ->; +export type TextBasedChannel = Exclude, ForumChannel>; export type TextBasedChannelTypes = TextBasedChannel['type']; @@ -7440,6 +6992,11 @@ export interface GuildTextThreadCreateOptions extends StartTh rateLimitPerUser?: number; } +export interface GuildForumThreadCreateOptions extends StartThreadOptions { + message: GuildForumThreadMessageCreateOptions | MessagePayload; + appliedTags?: Snowflake[]; +} + export interface ThreadEditData { name?: string; archived?: boolean; @@ -7460,38 +7017,17 @@ export type UserFlagsString = | 'PARTNERED_SERVER_OWNER' | 'HYPESQUAD_EVENTS' | 'BUGHUNTER_LEVEL_1' - | 'MFA_SMS' - | 'PREMIUM_PROMO_DISMISSED' | 'HOUSE_BRAVERY' | 'HOUSE_BRILLIANCE' | 'HOUSE_BALANCE' | 'EARLY_SUPPORTER' | 'TEAM_USER' - | 'INTERNAL_APPLICATION' - | 'SYSTEM' - | 'HAS_UNREAD_URGENT_MESSAGES' | 'BUGHUNTER_LEVEL_2' - | 'UNDERAGE_DELETED' | 'VERIFIED_BOT' | 'EARLY_VERIFIED_BOT_DEVELOPER' | 'DISCORD_CERTIFIED_MODERATOR' | 'BOT_HTTP_INTERACTIONS' - | 'SPAMMER' - | 'DISABLE_PREMIUM' - | 'ACTIVE_DEVELOPER' - | 'HIGH_GLOBAL_RATE_LIMIT' - | 'DELETED' - | 'DISABLED_SUSPICIOUS_ACTIVITY' - | 'SELF_DELETED' - | 'PREMIUM_DISCRIMINATOR' - | 'USED_DESKTOP_CLIENT' - | 'USED_WEB_CLIENT' - | 'USED_MOBILE_CLIENT' - | 'DISABLED' - | 'VERIFIED_EMAIL' - | 'QUARANTINED' - | 'COLLABORATOR' - | 'RESTRICTED_COLLABORATOR'; + | 'ACTIVE_DEVELOPER'; export type UserMention = `<@${Snowflake}>`; @@ -7521,6 +7057,15 @@ export interface WebhookClientDataURL { url: string; } +export type FriendRequestOptions = + | { + user: UserResolvable; + } + | { + username: string; + discriminator: number | null; + }; + export type WebhookClientOptions = Pick< ClientOptions, 'allowedMentions' | 'restTimeOffset' | 'restRequestTimeout' | 'retryLimit' | 'http' @@ -7536,7 +7081,6 @@ export type WebhookEditMessageOptions = Pick< WebhookMessageOptions, 'content' | 'embeds' | 'files' | 'allowedMentions' | 'components' | 'attachments' | 'threadId' >; - export interface InteractionEditReplyOptions extends WebhookEditMessageOptions { message?: MessageResolvable | '@original'; } @@ -7556,6 +7100,7 @@ export interface WebhookMessageOptions extends Omit; compress?: boolean; properties?: WebSocketProperties; } @@ -7597,11 +7142,10 @@ export interface WelcomeScreenEditData { export type WSEventType = | 'READY' | 'RESUMED' - | 'APPLICATION_COMMAND_AUTOCOMPLETE_RESPONSE' | 'APPLICATION_COMMAND_CREATE' | 'APPLICATION_COMMAND_DELETE' - | 'APPLICATION_COMMAND_UPDATE' | 'APPLICATION_COMMAND_PERMISSIONS_UPDATE' + | 'APPLICATION_COMMAND_UPDATE' | 'AUTO_MODERATION_ACTION_EXECUTION' | 'AUTO_MODERATION_RULE_CREATE' | 'AUTO_MODERATION_RULE_DELETE' @@ -7692,85 +7236,4 @@ export type InternalDiscordGatewayAdapterCreator = ( methods: InternalDiscordGatewayAdapterLibraryMethods, ) => InternalDiscordGatewayAdapterImplementerMethods; -// GuildForum -export type ChannelFlagsString = 'PINNED' | 'REQUIRE_TAG'; -export class ChannelFlags extends BitField { - public static FLAGS: Record; - public static resolve(bit?: BitFieldResolvable): number; -} - -export interface GuildForumTagEmoji { - id: Snowflake | null; - name: string | null; -} - -export interface GuildForumTag { - id: Snowflake; - name: string; - moderated: boolean; - emoji: GuildForumTagEmoji | null; -} - -export type GuildForumTagData = Partial & { name: string }; - -export interface DefaultReactionEmoji { - id: Snowflake | null; - name: string | null; -} - -export class ForumChannel extends TextBasedChannelMixin(GuildChannel, [ - 'send', - 'lastMessage', - 'lastPinAt', - 'bulkDelete', - 'sendTyping', - 'createMessageCollector', - 'awaitMessages', - 'createMessageComponentCollector', - 'awaitMessageComponent', -]) { - public type: 'GUILD_FORUM'; - public threads: GuildForumThreadManager; - public availableTags: GuildForumTag[]; - public defaultReactionEmoji: DefaultReactionEmoji | null; - public defaultThreadRateLimitPerUser: number | null; - public rateLimitPerUser: number | null; - public defaultAutoArchiveDuration: ThreadAutoArchiveDuration | null; - public nsfw: boolean; - public topic: string | null; - public defaultSortOrder: SortOrderType | null; - public defaultForumLayout: ForumLayoutType; - public setAvailableTags(tags: GuildForumTagData[], reason?: string): Promise; - public setDefaultReactionEmoji(emojiId: DefaultReactionEmoji | null, reason?: string): Promise; - public setDefaultThreadRateLimitPerUser(rateLimit: number, reason?: string): Promise; - public createInvite(options?: CreateInviteOptions): Promise; - public fetchInvites(cache?: boolean): Promise>; - public setDefaultAutoArchiveDuration( - defaultAutoArchiveDuration: ThreadAutoArchiveDuration, - reason?: string, - ): Promise; - public setTopic(topic: string | null, reason?: string): Promise; - public setDefaultSortOrder(defaultSortOrder: SortOrderType | null, reason?: string): Promise; - public setDefaultForumLayout(defaultForumLayout: ForumLayoutType, reason?: string): Promise; -} - -export class GuildTextThreadManager extends ThreadManager { - public create(options: GuildTextThreadCreateOptions): Promise; -} - -export class GuildForumThreadManager extends ThreadManager { - public create(options: GuildForumThreadCreateOptions): Promise; -} - -export type GuildForumThreadMessageCreateOptions = MessageOptions & Pick; - -export type ChannelFlagsResolvable = BitFieldResolvable; - -export type SelectMenuComponentType = keyof typeof SelectMenuComponentTypes; - -export interface GuildForumThreadCreateOptions extends StartThreadOptions { - message: GuildForumThreadMessageCreateOptions | MessagePayload; - appliedTags?: Snowflake[]; -} - //#endregion