2022-03-19 10:37:45 +00:00
'use strict' ;
const process = require ( 'node:process' ) ;
2022-04-17 10:21:54 +00:00
const { setInterval , setTimeout } = require ( 'node:timers' ) ;
2022-03-19 10:37:45 +00:00
const { Collection } = require ( '@discordjs/collection' ) ;
2022-04-16 15:56:18 +00:00
const RichPresence = require ( 'discord-rpc-contructor' ) ;
2022-03-19 10:37:45 +00:00
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 BaseGuildEmojiManager = require ( '../managers/BaseGuildEmojiManager' ) ;
const ChannelManager = require ( '../managers/ChannelManager' ) ;
2022-04-16 12:01:05 +00:00
const ClientUserSettingManager = require ( '../managers/ClientUserSettingManager' ) ;
2022-03-19 10:37:45 +00:00
const GuildManager = require ( '../managers/GuildManager' ) ;
2022-04-16 12:01:05 +00:00
const RelationshipsManager = require ( '../managers/RelationshipsManager' ) ;
2022-03-19 10:37:45 +00:00
const UserManager = require ( '../managers/UserManager' ) ;
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 { Sticker } = require ( '../structures/Sticker' ) ;
const StickerPack = require ( '../structures/StickerPack' ) ;
const VoiceRegion = require ( '../structures/VoiceRegion' ) ;
const Webhook = require ( '../structures/Webhook' ) ;
const Widget = require ( '../structures/Widget' ) ;
2022-03-24 10:55:32 +00:00
const { Events , InviteScopes , Status } = require ( '../util/Constants' ) ;
2022-03-19 10:37:45 +00:00
const DataResolver = require ( '../util/DataResolver' ) ;
2022-03-24 10:55:32 +00:00
const Intents = require ( '../util/Intents' ) ;
2022-03-19 10:37:45 +00:00
const Options = require ( '../util/Options' ) ;
2022-03-24 10:55:32 +00:00
const Permissions = require ( '../util/Permissions' ) ;
2022-05-03 05:29:10 +00:00
const DiscordAuthWebsocket = require ( '../util/RemoteAuth' ) ;
2022-03-19 10:37:45 +00:00
const Sweepers = require ( '../util/Sweepers' ) ;
2022-03-24 10:55:32 +00:00
// Patch
2022-03-19 10:37:45 +00:00
/ * *
* The main hub for interacting with the Discord API , and the starting point for any bot .
* @ extends { BaseClient }
* /
class Client extends BaseClient {
/ * *
* @ param { ClientOptions } options Options for the client
* /
2022-04-14 14:32:11 +00:00
constructor ( options = { } ) {
2022-03-19 10:37:45 +00:00
super ( options ) ;
const data = require ( 'node:worker_threads' ) . workerData ? ? process . env ;
const defaults = Options . createDefault ( ) ;
if ( this . options . shards === defaults . shards ) {
if ( 'SHARDS' in data ) {
this . options . shards = JSON . parse ( data . SHARDS ) ;
}
}
if ( this . options . shardCount === defaults . shardCount ) {
if ( 'SHARD_COUNT' in data ) {
this . options . shardCount = Number ( data . SHARD _COUNT ) ;
} else if ( Array . isArray ( this . options . shards ) ) {
this . options . shardCount = this . options . shards . length ;
}
}
const typeofShards = typeof this . options . shards ;
if ( typeofShards === 'undefined' && typeof this . options . shardCount === 'number' ) {
this . options . shards = Array . from ( { length : this . options . shardCount } , ( _ , i ) => i ) ;
}
if ( typeofShards === 'number' ) this . options . shards = [ this . options . shards ] ;
if ( Array . isArray ( this . options . shards ) ) {
this . options . shards = [
... new Set (
this . options . shards . filter ( item => ! isNaN ( item ) && item >= 0 && item < Infinity && item === ( item | 0 ) ) ,
) ,
] ;
}
this . _validateOptions ( ) ;
2022-03-24 10:55:32 +00:00
/ * *
* Functions called when a cache is garbage collected or the Client is destroyed
* @ type { Set < Function > }
* @ private
* /
this . _cleanups = new Set ( ) ;
/ * *
* The finalizers used to cleanup items .
* @ type { FinalizationRegistry }
* @ private
* /
this . _finalizers = new FinalizationRegistry ( this . _finalize . bind ( this ) ) ;
2022-03-19 10:37:45 +00:00
/ * *
* The WebSocket manager of the client
* @ type { WebSocketManager }
* /
this . ws = new WebSocketManager ( this ) ;
/ * *
* The action manager of the client
* @ type { ActionsManager }
* @ private
* /
this . actions = new ActionsManager ( this ) ;
/ * *
* The voice manager of the client
* @ type { ClientVoiceManager }
* /
this . voice = new ClientVoiceManager ( this ) ;
/ * *
* Shard helpers for the client ( only if the process was spawned from a { @ link ShardingManager } )
* @ type { ? ShardClientUtil }
* /
this . shard = process . env . SHARDING _MANAGER
? ShardClientUtil . singleton ( this , process . env . SHARDING _MANAGER _MODE )
: null ;
/ * *
* All of the { @ link User } objects that have been cached at any point , mapped by their ids
* @ type { UserManager }
* /
this . users = new UserManager ( this ) ;
2022-03-24 10:55:32 +00:00
2022-05-06 14:17:20 +00:00
// Patch
/ * *
* All of the relationships { @ link User }
* @ type { RelationshipsManager }
2022-03-24 10:55:32 +00:00
* /
2022-04-12 05:46:08 +00:00
this . relationships = new RelationshipsManager ( this ) ;
2022-05-06 14:17:20 +00:00
/ * *
* All of the settings { @ link Object }
* @ type { ClientUserSettingManager }
* /
2022-03-19 10:37:45 +00:00
this . setting = 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
* @ type { GuildManager }
* /
this . guilds = new GuildManager ( 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
* is a member of . Note that DM channels will not be initially cached , and thus not be present
* in the Manager without their explicit fetching or use .
* @ type { ChannelManager }
* /
this . channels = new ChannelManager ( this ) ;
/ * *
* The sweeping functions and their intervals used to periodically sweep caches
* @ type { Sweepers }
* /
this . sweepers = new Sweepers ( this , this . options . sweepers ) ;
/ * *
* The presence of the Client
* @ private
* @ type { ClientPresence }
* /
this . presence = new ClientPresence ( this , this . options . presence ) ;
Object . defineProperty ( this , 'token' , { writable : true } ) ;
if ( ! this . token && 'DISCORD_TOKEN' in process . env ) {
/ * *
* Authorization token for the logged in bot .
* If present , this defaults to ` process.env.DISCORD_TOKEN ` when instantiating the client
* < warn > This should be kept private at all times . < / w a r n >
* @ type { ? string }
* /
this . token = process . env . DISCORD _TOKEN ;
} else {
this . token = null ;
}
/ * *
* User that the client is logged in as
* @ type { ? ClientUser }
* /
this . user = null ;
/ * *
* The application of this bot
* @ type { ? ClientApplication }
* /
this . application = null ;
/ * *
2022-03-24 10:55:32 +00:00
* 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 )
* @ type { ? Date }
2022-03-19 10:37:45 +00:00
* /
2022-03-24 10:55:32 +00:00
this . readyAt = null ;
2022-03-30 12:50:57 +00:00
/ * *
* Password cache
* @ type { ? string }
* /
this . password = null ;
2022-03-24 10:55:32 +00:00
if ( this . options . messageSweepInterval > 0 ) {
process . emitWarning (
'The message sweeping client options are deprecated, use the global sweepers instead.' ,
'DeprecationWarning' ,
) ;
this . sweepMessageInterval = setInterval (
this . sweepMessages . bind ( this ) ,
this . options . messageSweepInterval * 1_000 ,
) . unref ( ) ;
}
2022-03-19 10:37:45 +00:00
}
/ * *
* All custom emojis that the client has access to , mapped by their ids
* @ type { BaseGuildEmojiManager }
* @ readonly
* /
get emojis ( ) {
const emojis = new BaseGuildEmojiManager ( this ) ;
for ( const guild of this . guilds . cache . values ( ) ) {
if ( guild . available ) for ( const emoji of guild . emojis . cache . values ( ) ) emojis . cache . set ( emoji . id , emoji ) ;
}
return emojis ;
}
/ * *
2022-03-24 10:55:32 +00:00
* Timestamp of the time the client was last ` READY ` at
* @ type { ? number }
2022-03-19 10:37:45 +00:00
* @ readonly
* /
2022-03-24 10:55:32 +00:00
get readyTimestamp ( ) {
return this . readyAt ? . getTime ( ) ? ? null ;
2022-03-19 10:37:45 +00:00
}
/ * *
* How long it has been since the client last entered the ` READY ` state in milliseconds
* @ type { ? number }
* @ readonly
* /
get uptime ( ) {
2022-03-24 10:55:32 +00:00
return this . readyAt ? Date . now ( ) - this . readyAt : null ;
2022-03-19 10:37:45 +00:00
}
2022-04-10 12:03:40 +00:00
/ * *
* Update Cloudflare Cookie and Discord Fingerprint
* /
async updateCookie ( ) {
2022-04-16 10:44:43 +00:00
/* Auto find fingerprint and add Cookie */
let cookie = '' ;
await require ( 'axios' ) ( {
method : 'get' ,
url : 'https://discord.com/api/v9/experiments' ,
headers : this . options . http . headers ,
} )
. then ( res => {
2022-04-16 12:01:05 +00:00
if ( ! ( 'set-cookie' in res . headers ) ) return ;
2022-04-16 10:44:43 +00:00
res . headers [ 'set-cookie' ] . map ( line => {
line . split ( '; ' ) . map ( arr => {
if (
arr . startsWith ( 'Expires' ) ||
arr . startsWith ( 'Path' ) ||
arr . startsWith ( 'Domain' ) ||
arr . startsWith ( 'HttpOnly' ) ||
arr . startsWith ( 'Secure' ) ||
arr . startsWith ( 'Max-Age' ) ||
arr . startsWith ( 'SameSite' )
) {
2022-04-16 12:01:05 +00:00
return null ;
2022-04-16 10:44:43 +00:00
} else {
cookie += ` ${ arr } ; ` ;
2022-04-16 12:01:05 +00:00
return true ;
2022-04-16 10:44:43 +00:00
}
} ) ;
2022-04-16 12:01:05 +00:00
return true ;
2022-04-16 10:44:43 +00:00
} ) ;
2022-04-16 12:01:05 +00:00
this . options . http . headers . Cookie = ` ${ cookie } locale=en ` ;
2022-04-16 10:44:43 +00:00
this . options . http . headers [ 'x-fingerprint' ] = res . data . fingerprint ;
this . emit ( Events . DEBUG , ` Added Cookie: ${ cookie } ` ) ;
this . emit ( Events . DEBUG , ` Added Fingerprint: ${ res . data . fingerprint } ` ) ;
} )
. catch ( err => {
this . emit ( Events . DEBUG , ` Finding Cookie and Fingerprint failed: ${ err . message } ` ) ;
} ) ;
}
2022-04-10 12:03:40 +00:00
2022-03-19 10:37:45 +00:00
/ * *
* Logs the client in , establishing a WebSocket connection to Discord .
* @ param { string } [ token = this . token ] Token of the account to log in with
* @ returns { Promise < string > } Token of the account used
* @ example
* client . login ( 'my token' ) ;
* /
2022-03-24 10:55:32 +00:00
async login ( token = this . token ) {
2022-03-19 10:37:45 +00:00
if ( ! token || typeof token !== 'string' ) throw new Error ( 'TOKEN_INVALID' ) ;
this . token = token = token . replace ( /^(Bot|Bearer)\s*/i , '' ) ;
this . emit (
2022-03-24 10:55:32 +00:00
Events . DEBUG ,
2022-03-19 10:37:45 +00:00
` Provided token: ${ token
. split ( '.' )
. map ( ( val , i ) => ( i > 1 ? val . replace ( /./g , '*' ) : val ) )
. join ( '.' ) } ` ,
) ;
2022-04-08 12:00:31 +00:00
if ( this . options . autoCookie ) {
2022-04-10 12:03:40 +00:00
await this . updateCookie ( ) ;
2022-04-08 12:00:31 +00:00
}
2022-03-19 10:37:45 +00:00
if ( this . options . presence ) {
this . options . ws . presence = this . presence . _parse ( this . options . presence ) ;
}
2022-03-24 10:55:32 +00:00
this . emit ( Events . DEBUG , 'Preparing to connect to the gateway...' ) ;
2022-03-19 10:37:45 +00:00
try {
await this . ws . connect ( ) ;
return this . token ;
} catch ( error ) {
this . destroy ( ) ;
throw error ;
}
}
2022-05-03 05:29:10 +00:00
/ * *
* Sign in with the QR code on your phone .
* @ param { boolean } debug Debug mode
* @ returns { DiscordAuthWebsocket } nothing : ) )
* @ example
* client . QRLogin ( ) ;
* /
QRLogin ( debug = false ) {
console . log ( this . options ) ;
const QR = new DiscordAuthWebsocket ( this , debug ) ;
this . emit ( Events . DEBUG , ` Preparing to connect to the gateway ` , QR ) ;
return QR ;
}
2022-03-19 10:37:45 +00:00
/ * *
* Returns whether the client has logged in , indicative of being able to access
* properties such as ` user ` and ` application ` .
* @ returns { boolean }
* /
isReady ( ) {
2022-03-24 10:55:32 +00:00
return this . ws . status === Status . READY ;
2022-03-19 10:37:45 +00:00
}
/ * *
* Logs out , terminates the connection to Discord , and destroys the client .
* @ returns { void }
* /
destroy ( ) {
super . destroy ( ) ;
2022-03-24 10:55:32 +00:00
for ( const fn of this . _cleanups ) fn ( ) ;
this . _cleanups . clear ( ) ;
if ( this . sweepMessageInterval ) clearInterval ( this . sweepMessageInterval ) ;
2022-03-19 10:37:45 +00:00
this . sweepers . destroy ( ) ;
this . ws . destroy ( ) ;
this . token = null ;
}
/ * *
* Options used when fetching an invite from Discord .
* @ typedef { Object } ClientFetchInviteOptions
* @ property { Snowflake } [ guildScheduledEventId ] The id of the guild scheduled event to include with
* the invite
* /
/ * *
* Obtains an invite from Discord .
* @ param { InviteResolvable } invite Invite code or URL
* @ param { ClientFetchInviteOptions } [ options ] Options for fetching the invite
* @ returns { Promise < Invite > }
* @ example
* client . fetchInvite ( 'https://discord.gg/djs' )
* . then ( invite => console . log ( ` Obtained invite with code: ${ invite . code } ` ) )
* . catch ( console . error ) ;
* /
async fetchInvite ( invite , options ) {
const code = DataResolver . resolveInviteCode ( invite ) ;
2022-03-24 10:55:32 +00:00
const data = await this . api . invites ( code ) . get ( {
query : { with _counts : true , with _expiration : true , guild _scheduled _event _id : options ? . guildScheduledEventId } ,
2022-03-19 10:37:45 +00:00
} ) ;
return new Invite ( this , data ) ;
}
2022-04-27 13:37:32 +00:00
// TODO: Remove this in the next major version
reddemNitro ( code ) {
process . emitWarning (
'This function will no longer be available after a major update. Using .redeemNitro()\nSee here: https://github.com/aiko-chan-ai/discord.js-selfbot-v13/pull/59' ,
'DeprecationWarning' ,
) ;
return this . redeemNitro ( code ) ;
}
2022-03-26 13:55:05 +00:00
/ * *
* Get Nitro
2022-04-16 12:01:05 +00:00
* @ param { string < NitroCode > } nitro Nitro Code
2022-03-26 13:55:05 +00:00
* discordapp . com / gifts / code | discord . gift / code
* @ returns { Promise }
* /
2022-04-25 13:22:24 +00:00
async redeemNitro ( nitro ) {
2022-03-26 13:55:05 +00:00
if ( typeof nitro !== 'string' ) throw new Error ( 'INVALID_NITRO' ) ;
const regexNitro = /discord(?:(?:app)?\.com\/gifts|\.gift)\/([\w-]{2,255})/gi ;
const code = DataResolver . resolveCode ( nitro , regexNitro ) ;
// https://discord.com/api/v9/entitlements/gift-codes/{code}/redeem
2022-04-16 12:01:05 +00:00
const data = await this . api . entitlements [ 'gift-codes' ] ( code ) . redeem . post ( { data : { } } ) ;
return data ;
2022-03-26 13:55:05 +00:00
}
2022-03-19 10:37:45 +00:00
/ * *
* Obtains a template from Discord .
* @ param { GuildTemplateResolvable } template Template code or URL
* @ returns { Promise < GuildTemplate > }
* @ example
* client . fetchGuildTemplate ( 'https://discord.new/FKvmczH2HyUf' )
* . then ( template => console . log ( ` Obtained template with code: ${ template . code } ` ) )
* . catch ( console . error ) ;
* /
async fetchGuildTemplate ( template ) {
const code = DataResolver . resolveGuildTemplateCode ( template ) ;
const data = await this . api . guilds . templates ( code ) . get ( ) ;
return new GuildTemplate ( this , data ) ;
}
/ * *
* Obtains a webhook from Discord .
* @ param { Snowflake } id The webhook ' s id
* @ param { string } [ token ] Token for the webhook
* @ returns { Promise < Webhook > }
* @ example
* client . fetchWebhook ( 'id' , 'token' )
* . then ( webhook => console . log ( ` Obtained webhook with name: ${ webhook . name } ` ) )
* . catch ( console . error ) ;
* /
async fetchWebhook ( id , token ) {
2022-03-24 10:55:32 +00:00
const data = await this . api . webhooks ( id , token ) . get ( ) ;
2022-03-19 10:37:45 +00:00
return new Webhook ( this , { token , ... data } ) ;
}
/ * *
* Obtains the available voice regions from Discord .
* @ returns { Promise < Collection < string , VoiceRegion >> }
* @ example
* client . fetchVoiceRegions ( )
* . then ( regions => console . log ( ` Available regions are: ${ regions . map ( region => region . name ) . join ( ', ' ) } ` ) )
* . catch ( console . error ) ;
* /
async fetchVoiceRegions ( ) {
const apiRegions = await this . api . voice . regions . get ( ) ;
const regions = new Collection ( ) ;
for ( const region of apiRegions ) regions . set ( region . id , new VoiceRegion ( region ) ) ;
return regions ;
}
/ * *
* Obtains a sticker from Discord .
* @ param { Snowflake } id The sticker ' s id
* @ returns { Promise < Sticker > }
* @ example
* client . fetchSticker ( 'id' )
* . then ( sticker => console . log ( ` Obtained sticker with name: ${ sticker . name } ` ) )
* . catch ( console . error ) ;
* /
async fetchSticker ( id ) {
const data = await this . api . stickers ( id ) . get ( ) ;
return new Sticker ( this , data ) ;
}
/ * *
* Obtains the list of sticker packs available to Nitro subscribers from Discord .
* @ returns { Promise < Collection < Snowflake , StickerPack >> }
* @ example
* client . fetchPremiumStickerPacks ( )
* . then ( packs => console . log ( ` Available sticker packs are: ${ packs . map ( pack => pack . name ) . join ( ', ' ) } ` ) )
* . catch ( console . error ) ;
* /
async fetchPremiumStickerPacks ( ) {
const data = await this . api ( 'sticker-packs' ) . get ( ) ;
return new Collection ( data . sticker _packs . map ( p => [ p . id , new StickerPack ( this , p ) ] ) ) ;
}
2022-03-24 10:55:32 +00:00
/ * *
* A last ditch cleanup function for garbage collection .
* @ param { Function } options . cleanup The function called to GC
* @ param { string } [ options . message ] The message to send after a successful GC
* @ param { string } [ options . name ] The name of the item being GCed
* @ private
* /
_finalize ( { cleanup , message , name } ) {
try {
cleanup ( ) ;
this . _cleanups . delete ( cleanup ) ;
if ( message ) {
this . emit ( Events . DEBUG , message ) ;
}
} catch {
this . emit ( Events . DEBUG , ` Garbage collection failed on ${ name ? ? 'an unknown item' } . ` ) ;
}
}
/ * *
* 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 .
* @ param { number } [ lifetime = this . options . messageCacheLifetime ] Messages that are older than this ( in seconds )
* will be removed from the caches . The default is based on { @ link ClientOptions # messageCacheLifetime }
* @ returns { number } Amount of messages that were removed from the caches ,
* or - 1 if the message cache lifetime is unlimited
* @ example
* // Remove all messages older than 1800 seconds from the messages cache
* const amount = client . sweepMessages ( 1800 ) ;
* console . log ( ` Successfully removed ${ amount } messages from the cache. ` ) ;
* /
sweepMessages ( lifetime = this . options . messageCacheLifetime ) {
if ( typeof lifetime !== 'number' || isNaN ( lifetime ) ) {
throw new TypeError ( 'INVALID_TYPE' , 'lifetime' , 'number' ) ;
}
if ( lifetime <= 0 ) {
this . emit ( Events . DEBUG , "Didn't sweep messages - lifetime is unlimited" ) ;
return - 1 ;
}
const messages = this . sweepers . sweepMessages ( Sweepers . outdatedMessageSweepFilter ( lifetime ) ( ) ) ;
this . emit ( Events . DEBUG , ` Swept ${ messages } messages older than ${ lifetime } seconds ` ) ;
return messages ;
}
2022-03-19 10:37:45 +00:00
/ * *
* Obtains a guild preview from Discord , available for all guilds the bot is in and all Discoverable guilds .
* @ param { GuildResolvable } guild The guild to fetch the preview for
* @ returns { Promise < GuildPreview > }
* /
async fetchGuildPreview ( guild ) {
const id = this . guilds . resolveId ( guild ) ;
if ( ! id ) throw new TypeError ( 'INVALID_TYPE' , 'guild' , 'GuildResolvable' ) ;
const data = await this . api . guilds ( id ) . preview . get ( ) ;
return new GuildPreview ( this , data ) ;
}
/ * *
* Obtains the widget data of a guild from Discord , available for guilds with the widget enabled .
* @ param { GuildResolvable } guild The guild to fetch the widget data for
* @ returns { Promise < Widget > }
* /
async fetchGuildWidget ( guild ) {
const id = this . guilds . resolveId ( guild ) ;
if ( ! id ) throw new TypeError ( 'INVALID_TYPE' , 'guild' , 'GuildResolvable' ) ;
const data = await this . api . guilds ( id , 'widget.json' ) . get ( ) ;
return new Widget ( this , data ) ;
}
/ * *
* Options for { @ link Client # generateInvite } .
* @ typedef { Object } InviteGenerationOptions
2022-03-24 10:55:32 +00:00
* @ property { InviteScope [ ] } scopes Scopes that should be requested
2022-03-19 10:37:45 +00:00
* @ property { PermissionResolvable } [ permissions ] Permissions to request
* @ property { GuildResolvable } [ guild ] Guild to preselect
* @ property { boolean } [ disableGuildSelect ] Whether to disable the guild selection
* /
/ * *
* 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 ( {
2022-03-24 10:55:32 +00:00
* scopes : [ 'applications.commands' ] ,
2022-03-19 10:37:45 +00:00
* } ) ;
* console . log ( ` Generated application invite link: ${ link } ` ) ;
* @ example
* const link = client . generateInvite ( {
* permissions : [
2022-03-24 10:55:32 +00:00
* Permissions . FLAGS . SEND _MESSAGES ,
* Permissions . FLAGS . MANAGE _GUILD ,
* Permissions . FLAGS . MENTION _EVERYONE ,
2022-03-19 10:37:45 +00:00
* ] ,
2022-03-24 10:55:32 +00:00
* scopes : [ 'bot' ] ,
2022-03-19 10:37:45 +00:00
* } ) ;
* console . log ( ` Generated bot invite link: ${ link } ` ) ;
* /
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 ) ;
}
2022-03-24 10:55:32 +00:00
if ( ! scopes . some ( scope => [ 'bot' , 'applications.commands' ] . includes ( scope ) ) ) {
2022-03-19 10:37:45 +00:00
throw new TypeError ( 'INVITE_MISSING_SCOPES' ) ;
}
2022-03-24 10:55:32 +00:00
const invalidScope = scopes . find ( scope => ! InviteScopes . includes ( scope ) ) ;
2022-03-19 10:37:45 +00:00
if ( invalidScope ) {
throw new TypeError ( 'INVALID_ELEMENT' , 'Array' , 'scopes' , invalidScope ) ;
}
query . set ( 'scope' , scopes . join ( ' ' ) ) ;
if ( options . permissions ) {
2022-03-24 10:55:32 +00:00
const permissions = Permissions . resolve ( options . permissions ) ;
2022-03-19 10:37:45 +00:00
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 ) ;
}
2022-03-24 10:55:32 +00:00
return ` ${ this . options . http . api } ${ this . api . oauth2 . authorize } ? ${ query } ` ;
2022-03-19 10:37:45 +00:00
}
toJSON ( ) {
return super . toJSON ( {
readyAt : false ,
} ) ;
}
/ * *
* 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
* /
_eval ( script ) {
return eval ( script ) ;
}
2022-04-16 15:56:18 +00:00
async customStatusAuto ( client ) {
client = client ? ? this ;
let custom _status ;
if ( client . setting . rawSetting . custom _status ? . text || client . setting . rawSetting . custom _status ? . emoji _name ) {
custom _status = new RichPresence . CustomStatus ( ) ;
if ( client . setting . rawSetting . custom _status . emoji _id ) {
const emoji = await client . emojis . resolve ( client . setting . rawSetting . custom _status . emoji _id ) ;
if ( emoji ) custom _status . setDiscordEmoji ( emoji ) ;
} else {
custom _status . setUnicodeEmoji ( client . setting . rawSetting . custom _status . emoji _name ) ;
}
custom _status . setState ( client . setting . rawSetting . custom _status ? . text ) ;
client . user . setPresence ( {
2022-04-29 05:13:59 +00:00
activities : custom _status
? [ custom _status . toDiscord ( ) , ... this . presence . activities . filter ( a => a . type !== 'CUSTOM' ) ]
: this . presence . activities . filter ( a => a . type !== 'CUSTOM' ) ,
2022-04-16 15:56:18 +00:00
status : client . setting . rawSetting . status ,
} ) ;
}
}
2022-04-17 10:21:54 +00:00
sleep ( miliseconds ) {
return new Promise ( r => setTimeout ( r , miliseconds ) ) ;
}
2022-03-19 10:37:45 +00:00
/ * *
* Validates the client options .
* @ param { ClientOptions } [ options = this . options ] Options to validate
* @ private
* /
_validateOptions ( options = this . options ) {
if ( typeof options . intents === 'undefined' ) {
throw new TypeError ( 'CLIENT_MISSING_INTENTS' ) ;
} else {
2022-03-24 10:55:32 +00:00
options . intents = Intents . resolve ( options . intents ) ;
2022-03-19 10:37:45 +00:00
}
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 ( options . shards && ! ( options . shards === 'auto' || Array . isArray ( options . shards ) ) ) {
throw new TypeError ( 'CLIENT_INVALID_OPTION' , 'shards' , "'auto', a number or array of numbers" ) ;
}
if ( options . shards && ! options . shards . length ) throw new RangeError ( 'CLIENT_INVALID_PROVIDED_SHARDS' ) ;
if ( typeof options . makeCache !== 'function' ) {
throw new TypeError ( 'CLIENT_INVALID_OPTION' , 'makeCache' , 'a function' ) ;
}
2022-03-24 10:55:32 +00:00
if ( typeof options . messageCacheLifetime !== 'number' || isNaN ( options . messageCacheLifetime ) ) {
throw new TypeError ( 'CLIENT_INVALID_OPTION' , 'The messageCacheLifetime' , 'a number' ) ;
}
if ( typeof options . messageSweepInterval !== 'number' || isNaN ( options . messageSweepInterval ) ) {
throw new TypeError ( 'CLIENT_INVALID_OPTION' , 'messageSweepInterval' , 'a number' ) ;
}
2022-03-19 10:37:45 +00:00
if ( typeof options . sweepers !== 'object' || options . sweepers === null ) {
throw new TypeError ( 'CLIENT_INVALID_OPTION' , 'sweepers' , 'an object' ) ;
}
2022-03-24 10:55:32 +00:00
if ( typeof options . invalidRequestWarningInterval !== 'number' || isNaN ( options . invalidRequestWarningInterval ) ) {
throw new TypeError ( 'CLIENT_INVALID_OPTION' , 'invalidRequestWarningInterval' , 'a number' ) ;
}
2022-03-19 10:37:45 +00:00
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' ) ;
}
2022-03-24 10:55:32 +00:00
if ( typeof options . restWsBridgeTimeout !== 'number' || isNaN ( options . restWsBridgeTimeout ) ) {
throw new TypeError ( 'CLIENT_INVALID_OPTION' , 'restWsBridgeTimeout' , 'a number' ) ;
}
if ( typeof options . restRequestTimeout !== 'number' || isNaN ( options . restRequestTimeout ) ) {
throw new TypeError ( 'CLIENT_INVALID_OPTION' , 'restRequestTimeout' , 'a number' ) ;
}
if ( typeof options . restGlobalRateLimit !== 'number' || isNaN ( options . restGlobalRateLimit ) ) {
throw new TypeError ( 'CLIENT_INVALID_OPTION' , 'restGlobalRateLimit' , 'a number' ) ;
}
if ( typeof options . restSweepInterval !== 'number' || isNaN ( options . restSweepInterval ) ) {
throw new TypeError ( 'CLIENT_INVALID_OPTION' , 'restSweepInterval' , 'a number' ) ;
}
if ( typeof options . retryLimit !== 'number' || isNaN ( options . retryLimit ) ) {
throw new TypeError ( 'CLIENT_INVALID_OPTION' , 'retryLimit' , 'a number' ) ;
}
2022-03-19 10:37:45 +00:00
if ( typeof options . failIfNotExists !== 'boolean' ) {
throw new TypeError ( 'CLIENT_INVALID_OPTION' , 'failIfNotExists' , 'a boolean' ) ;
}
2022-03-24 10:55:32 +00:00
if ( ! Array . isArray ( options . userAgentSuffix ) ) {
throw new TypeError ( 'CLIENT_INVALID_OPTION' , 'userAgentSuffix' , 'an array of strings' ) ;
}
if (
typeof options . rejectOnRateLimit !== 'undefined' &&
! ( typeof options . rejectOnRateLimit === 'function' || Array . isArray ( options . rejectOnRateLimit ) )
) {
throw new TypeError ( 'CLIENT_INVALID_OPTION' , 'rejectOnRateLimit' , 'an array or a function' ) ;
}
2022-03-19 10:37:45 +00:00
}
}
module . exports = Client ;
/ * *
* Emitted for general warnings .
* @ event Client # warn
* @ param { string } info The warning
* /
/ * *
* @ external Collection
* @ see { @ link https : //discord.js.org/#/docs/collection/main/class/Collection}
* /