diff --git a/src/client/Client.js b/src/client/Client.js index 04224c2..86525f6 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -4,11 +4,14 @@ const process = require('node:process'); const { setInterval, setTimeout } = require('node:timers'); const { Collection } = require('@discordjs/collection'); const { getVoiceConnection } = require('@discordjs/voice'); +const axios = require('axios'); +const chalk = require('chalk'); 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 ChannelManager = require('../managers/ChannelManager'); const ClientSettingManager = require('../managers/ClientSettingManager'); @@ -237,10 +240,6 @@ class Client extends BaseClient { */ this.usedCodes = []; - /** - * Session ID - * @type {?string} - */ this.session_id = null; if (this.options.messageSweepInterval > 0) { @@ -260,6 +259,15 @@ class Client extends BaseClient { }, 3_600_000); } + /** + * Session ID + * @type {?string} + * @readonly + */ + get sessionId() { + return this.session_id; + } + /** * All custom emojis that the client has access to, mapped by their ids * @type {BaseGuildEmojiManager} @@ -539,6 +547,29 @@ class Client extends BaseClient { }); } + /** + * 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 axios + .get(`https://registry.npmjs.com/${encodeURIComponent('discord.js-selfbot-v13')}`) + .catch(() => {}); + if (!res_) { + return this.emit('debug', `${chalk.redBright('[Fail]')} Check Update error`); + } + const latest_tag = res_.data['dist-tags'].latest; + this.emit('update', Discord.version, latest_tag); + return this; + } + /** * Returns whether the client has logged in, indicative of being able to access * properties such as `user` and `application`. diff --git a/src/client/websocket/handlers/READY.js b/src/client/websocket/handlers/READY.js index a67a61a..3d576e0 100644 --- a/src/client/websocket/handlers/READY.js +++ b/src/client/websocket/handlers/READY.js @@ -2,33 +2,51 @@ let ClientUser; const { VoiceConnection } = require('@discordjs/voice'); -const axios = require('axios'); const chalk = require('chalk'); -const Discord = require('../../../index'); const { Events, Opcodes } = require('../../../util/Constants'); const { VoiceConnection: VoiceConnection_patch } = require('../../../util/Voice'); -let running = false; -/** - * Emitted whenever clientOptions.checkUpdate = false - * @event Client#update - * @param {string} oldVersion Current version - * @param {string} newVersion Latest version - */ +let firstReady = false; -async function checkUpdate(client) { - const res_ = await axios - .get(`https://registry.npmjs.com/${encodeURIComponent('discord.js-selfbot-v13')}`) - .catch(() => {}); - if (!res_) { - return client.emit(Events.DEBUG, `${chalk.redBright('[Fail]')} Check Update error`); +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.13.0')}]`, + ); + /* eslint-enable */ + } catch (e) { + client.emit( + 'debug', + `${chalk.redBright('[Fail]')} Patched ${chalk.cyanBright( + 'VoiceConnection.prototype.configureNetworking', + )} [${chalk.bgMagentaBright('@discordjs/voice')} - ${chalk.redBright('v0.13.0')}]\n${e.stack}`, + ); + client.emit( + Events.ERROR, + `${chalk.redBright('[Fail]')} Patched ${chalk.cyanBright( + 'VoiceConnection.prototype.configureNetworking', + )} [${chalk.bgMagentaBright('@discordjs/voice')} - ${chalk.redBright('v0.13.0')}]`, + ); + client.emit( + Events.ERROR, + `${chalk.redBright('[Error]')} Please install ${chalk.bgMagentaBright( + '@discordjs/voice', + )} version ${chalk.redBright('v0.13.0')}`, + ); } - const latest_tag = res_.data['dist-tags'].latest; - if (client.options.checkUpdate) { - if (latest_tag !== Discord.version && Discord.version.includes('-') == false) { - if (!running) { +} + +module.exports = async (client, { d: data }, shard) => { + if (!firstReady) { + client.once('update', (currentVersion, newVersion) => { + if (!newVersion) { console.log(` - ${chalk.yellowBright('[WARNING]')} New Discord.js-selfbot-v13 version. - Current: ${chalk.redBright(Discord.version)} => Latest: ${chalk.greenBright(latest_tag)} + ${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 @@ -38,11 +56,23 @@ async function checkUpdate(client) { and using event update https://discordjs-self-v13.netlify.app/#/docs/docs/main/class/Client?scrollTo=e-update\n`); - } - } else if (!running) { - console.log( - ` - ${chalk.greenBright('[OK]')} Discord.js-selfbot-v13 is up to date. Current: ${chalk.blueBright(Discord.version)} + } 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 @@ -52,49 +82,22 @@ async function checkUpdate(client) { and using event update https://discordjs-self-v13.netlify.app/#/docs/docs/main/class/Client?scrollTo=e-update\n`, - ); - } - } else { - client.emit('update', Discord.version, latest_tag); - } - running = true; - return undefined; -} + ); + } + }); -module.exports = async (client, { d: data }, shard) => { - checkUpdate(client); - - if (client.options.patchVoice && !running) { - try { - /* eslint-disable */ - VoiceConnection.prototype.configureNetworking = VoiceConnection_patch.prototype.configureNetworking; - client.emit( - Events.DEBUG, - `${chalk.greenBright('[OK]')} Patched ${chalk.cyanBright( - 'VoiceConnection.prototype.configureNetworking', - )} [${chalk.bgMagentaBright('@discordjs/voice')} - ${chalk.redBright('v0.13.0')}]`, - ); - /* eslint-enable */ - } catch (e) { - client.emit( - Events.DEBUG, - `${chalk.redBright('[Fail]')} Patched ${chalk.cyanBright( - 'VoiceConnection.prototype.configureNetworking', - )} [${chalk.bgMagentaBright('@discordjs/voice')} - ${chalk.redBright('v0.13.0')}]\n${e.stack}`, - ); - client.emit( - Events.ERROR, - `${chalk.redBright('[Fail]')} Patched ${chalk.cyanBright( - 'VoiceConnection.prototype.configureNetworking', - )} [${chalk.bgMagentaBright('@discordjs/voice')} - ${chalk.redBright('v0.13.0')}]`, - ); - client.emit( - Events.ERROR, - `${chalk.redBright('[Error]')} Please install ${chalk.bgMagentaBright( - '@discordjs/voice', - )} version ${chalk.redBright('v0.13.0')}`, - ); + if (client.options.checkUpdate) { + client.checkUpdate(); } + + if (client.options.patchVoice) { + patchVoice(client); + } + + if (client.options.readyStatus) { + client.customStatusAuto(client); + } + firstReady = true; } client.session_id = data.session_id; @@ -135,10 +138,6 @@ module.exports = async (client, { d: data }, shard) => { ); } - if (client.options.readyStatus && !running) { - client.customStatusAuto(client); - } - for (const guild of data.guilds) { guild.shardId = shard.id; client.guilds._add(guild); diff --git a/src/structures/ClientPresence.js b/src/structures/ClientPresence.js index 819acfc..c721d78 100644 --- a/src/structures/ClientPresence.js +++ b/src/structures/ClientPresence.js @@ -1,6 +1,7 @@ 'use strict'; const { Presence } = require('./Presence'); +const { CustomStatus } = require('./RichPresence'); const { TypeError } = require('../errors'); const { Opcodes, ActivityTypes } = require('../util/Constants'); @@ -48,7 +49,9 @@ class ClientPresence extends Presence { }; if (activities?.length) { for (const [i, activity] of activities.entries()) { - if (typeof activity.name !== 'string') throw new TypeError('INVALID_TYPE', `activities[${i}].name`, 'string'); + if (!(activity instanceof CustomStatus) && typeof activity.name !== 'string') { + throw new TypeError('INVALID_TYPE', `activities[${i}].name`, 'string'); + } activity.type ??= 0; data.activities.push( Object.assign(activity, { diff --git a/src/structures/ClientUser.js b/src/structures/ClientUser.js index 23f1799..c383ea9 100644 --- a/src/structures/ClientUser.js +++ b/src/structures/ClientUser.js @@ -364,7 +364,7 @@ class ClientUser extends User { * @typedef {Object} PresenceData * @property {PresenceStatusData} [status] Status of the user * @property {boolean} [afk] Whether the user is AFK - * @property {ActivitiesOptions[]} [activities] Activity the user is playing + * @property {Array[ActivitiesOptions|CustomStatus|RichPresence|SpotifyRPC]} [activities] Activity the user is playing * @property {number|number[]} [shardId] Shard id(s) to have the activity set on */ diff --git a/src/structures/RichPresence.js b/src/structures/RichPresence.js index badd918..3c16777 100644 --- a/src/structures/RichPresence.js +++ b/src/structures/RichPresence.js @@ -77,6 +77,18 @@ class CustomStatus { state: this.state, }; } + + /** + * When concatenated with a string, this automatically returns the activities' name instead of the Activity object. + * @returns {string} + */ + toString() { + return this.name; + } + + _clone() { + return Object.assign(Object.create(this), this); + } } class RichPresence { @@ -520,6 +532,18 @@ https://github.com/aiko-chan-ai/discord.js-selfbot-v13/blob/main/Documents/RichP }); return res; } + + /** + * When concatenated with a string, this automatically returns the activities' name instead of the Activity object. + * @returns {string} + */ + toString() { + return this.name; + } + + _clone() { + return Object.assign(Object.create(this), this); + } } /** diff --git a/typings/index.d.ts b/typings/index.d.ts index eb54ff8..930fb73 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -257,6 +257,7 @@ export abstract class RichPresence { ): Promise; public static getUUID(): string; public toJSON(): object; + public toString(): string; } export interface ExternalAssets { @@ -305,6 +306,7 @@ export abstract class CustomStatus { public setEmoji(emoji?: EmojiIdentifierResolvable): this; public setState(state: string): this; public toJSON(): object; + public toString(): string; } export class Activity { @@ -852,6 +854,7 @@ export class Client extends BaseClient { public voice: ClientVoiceManager; public ws: WebSocketManager; public password: string | null; + public readonly sessionId: string | null; public destroy(): void; public logout(): Promise; public fetchGuildPreview(guild: GuildResolvable): Promise; @@ -869,6 +872,7 @@ export class Client extends BaseClient { public QRLogin(debug?: boolean): DiscordAuthWebsocket; public remoteAuth(url: string, forceAccept?: boolean): Promise; public createToken(): Promise; + public checkUpdate(): Promise; public isReady(): this is Client; /** @deprecated Use {@link Sweepers#sweepMessages} instead */ public sweepMessages(lifetime?: number): number; @@ -4205,7 +4209,7 @@ export type ActivityFlagsString = | 'PARTY_PRIVACY_VOICE_CHANNEL' | 'EMBEDDED'; -export type ActivitiesOptions = Omit; +export type ActivitiesOptions = Omit; export interface ActivityOptions { name?: string;