Compare commits

..

No commits in common. "1cafa029f0aad622754d6a79feec1cdb4b1cda53" and "78e2fb2945cc3e9976b2962baf34330d1dd51dc6" have entirely different histories.

12 changed files with 94 additions and 185 deletions

File diff suppressed because one or more lines are too long

View File

@ -89,31 +89,3 @@ await message.channel.sendSlash('718642000898818048', 'sauce', a)
} }
``` ```
### Receive messages after bot has replied `{botname} is thinking...`
> [aiko-chan-ai/discord.js-selfbot-v13#1055 (comment)](https://github.com/aiko-chan-ai/discord.js-selfbot-v13/issues/1055#issuecomment-1949653100)
![image](https://cdn.discordapp.com/attachments/820557032016969751/1208363574477590538/image.png?ex=65e30346&is=65d08e46&hm=72771d6aa0d23f817f5daf8d2f33906ff74200aace7787c3cd02d2e30e58f8d5&)
```js
const channel = client.channels.cache.get('id');
channel
.sendSlash('289066747443675143', 'osu', 'Accolibed')
.then(async (message) => {
if (message.flags.has('LOADING')) { // owo is thinking...
return new Promise((r, rej) => {
let t = setTimeout(() => rej('timeout'), 15 * 60 * 1000); // 15m (DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE)
message.client.on('messageUpdate', (_, m) => {
if (_.id == message.id) {
clearTimeout(t);
r(m);
}
});
});
} else {
return Promise.resolve(message);
}
})
.then(console.log);
```

View File

@ -277,39 +277,6 @@ class Client extends BaseClient {
return ws.connect(this); return ws.connect(this);
} }
/**
* Logs the client in, establishing a WebSocket connection to Discord.
* @param {string} email The email associated with the account
* @param {string} password The password assicated with the account
* @param {string | number} [code = null] The mfa code if you have it enabled
* @returns {string | null} Token of the account used
*
* @example
* client.passLogin("test@gmail.com", "SuperSecretPa$$word", 1234)
*/
async passLogin(email, password, code = null) {
const initial = await this.api.auth.login.post({
auth: false,
versioned: true,
data: { gift_code_sku_id: null, login_source: null, undelete: false, login: email, password },
});
if ('token' in initial) {
return this.login(initial.token);
} else if ('ticket' in initial) {
const totp = await this.api.auth.mfa.totp.post({
auth: false,
versioned: true,
data: { gift_code_sku_id: null, login_source: null, code, ticket: initial.ticket },
});
if ('token' in totp) {
return this.login(totp.token);
}
}
return null;
}
/** /**
* Returns whether the client has logged in, indicative of being able to access * Returns whether the client has logged in, indicative of being able to access
* properties such as `user` and `application`. * properties such as `user` and `application`.

View File

@ -1,11 +1,11 @@
'use strict'; 'use strict';
const EventEmitter = require('node:events'); const EventEmitter = require('node:events');
const http = require('node:http');
const { setTimeout, setInterval, clearTimeout } = require('node:timers'); const { setTimeout, setInterval, clearTimeout } = require('node:timers');
const WebSocket = require('../../WebSocket'); const WebSocket = require('../../WebSocket');
const { Status, Events, ShardEvents, Opcodes, WSEvents, WSCodes } = require('../../util/Constants'); const { Status, Events, ShardEvents, Opcodes, WSEvents, WSCodes } = require('../../util/Constants');
const Intents = require('../../util/Intents'); const Intents = require('../../util/Intents');
const Util = require('../../util/Util');
const STATUS_KEYS = Object.keys(Status); const STATUS_KEYS = Object.keys(Status);
const CONNECTION_STATE = Object.keys(WebSocket.WebSocket); const CONNECTION_STATE = Object.keys(WebSocket.WebSocket);
@ -272,7 +272,7 @@ class WebSocketShard extends EventEmitter {
Version : ${client.options.ws.version} Version : ${client.options.ws.version}
Encoding : ${WebSocket.encoding} Encoding : ${WebSocket.encoding}
Compression: ${zlib ? 'zlib-stream' : 'none'} Compression: ${zlib ? 'zlib-stream' : 'none'}
Agent : ${Util.verifyProxyAgent(client.options.ws.agent)}`, Agent : ${client.options.ws.agent instanceof http.Agent}`,
); );
this.status = this.status === Status.DISCONNECTED ? Status.RECONNECTING : Status.CONNECTING; this.status = this.status === Status.DISCONNECTED ? Status.RECONNECTING : Status.CONNECTING;
@ -283,7 +283,7 @@ class WebSocketShard extends EventEmitter {
// Adding a handshake timeout to just make sure no zombie connection appears. // Adding a handshake timeout to just make sure no zombie connection appears.
const ws = (this.connection = WebSocket.create(gateway, wsQuery, { const ws = (this.connection = WebSocket.create(gateway, wsQuery, {
handshakeTimeout: 30_000, handshakeTimeout: 30_000,
agent: Util.verifyProxyAgent(client.options.ws.agent) ? client.options.ws.agent : undefined, agent: client.options.ws.agent instanceof http.Agent ? client.options.ws.agent : undefined,
})); }));
ws.onopen = this.onOpen.bind(this); ws.onopen = this.onOpen.bind(this);
ws.onmessage = this.onMessage.bind(this); ws.onmessage = this.onMessage.bind(this);

View File

@ -85,23 +85,23 @@ class GuildForumThreadManager extends ThreadManager {
}); });
const attachmentsData = await Promise.all(requestPromises); const attachmentsData = await Promise.all(requestPromises);
attachmentsData.sort((a, b) => parseInt(a.id) - parseInt(b.id)); attachmentsData.sort((a, b) => parseInt(a.id) - parseInt(b.id));
data.attachments = attachmentsData;
if (autoArchiveDuration === 'MAX') autoArchiveDuration = resolveAutoArchiveMaxLimit(this.channel.guild); if (autoArchiveDuration === 'MAX') autoArchiveDuration = resolveAutoArchiveMaxLimit(this.channel.guild);
const post_data = await this.client.api.channels(this.channel.id).threads.post({ const data = await this.client.api.channels(this.channel.id).threads.post({
data: { data: {
name, name,
auto_archive_duration: autoArchiveDuration, auto_archive_duration: autoArchiveDuration,
rate_limit_per_user: rateLimitPerUser, rate_limit_per_user: rateLimitPerUser,
applied_tags: appliedTags, applied_tags: appliedTags,
message: body, message: body,
attachments: attachmentsData,
}, },
files: [], files: [],
reason, reason,
}); });
return this.client.actions.ThreadCreate.handle(post_data).thread; return this.client.actions.ThreadCreate.handle(data).thread;
} }
} }

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
const Buffer = require('node:buffer').Buffer; const Buffer = require('node:buffer').Buffer;
const http = require('node:http');
const https = require('node:https'); const https = require('node:https');
const { setTimeout } = require('node:timers'); const { setTimeout } = require('node:timers');
const makeFetchCookie = require('fetch-cookie'); const makeFetchCookie = require('fetch-cookie');
@ -8,7 +9,6 @@ const FormData = require('form-data');
const fetchOriginal = require('node-fetch'); const fetchOriginal = require('node-fetch');
const { CookieJar } = require('tough-cookie'); const { CookieJar } = require('tough-cookie');
const { ciphers } = require('../util/Constants'); const { ciphers } = require('../util/Constants');
const Util = require('../util/Util');
const cookieJar = new CookieJar(); const cookieJar = new CookieJar();
const fetch = makeFetchCookie(fetchOriginal, cookieJar); const fetch = makeFetchCookie(fetchOriginal, cookieJar);
@ -40,17 +40,11 @@ class APIRequest {
make(captchaKey, captchaRqToken) { make(captchaKey, captchaRqToken) {
if (!agent) { if (!agent) {
if (Util.verifyProxyAgent(this.client.options.http.agent)) { if (this.client.options.http.agent instanceof http.Agent) {
// Bad code this.client.options.http.agent.options.keepAlive = true;
for (const [k, v] of Object.entries({ this.client.options.http.agent.options.honorCipherOrder = true;
keepAlive: true, this.client.options.http.agent.options.minVersion = 'TLSv1.2';
honorCipherOrder: true, this.client.options.http.agent.options.ciphers = ciphers.join(':');
minVersion: 'TLSv1.2',
ciphers: ciphers.join(':'),
})) {
this.client.options.http.agent.options[k] = v;
this.client.options.http.agent.httpsAgent.options.options[k] = v;
}
agent = this.client.options.http.agent; agent = this.client.options.http.agent;
} else { } else {
agent = new https.Agent({ agent = new https.Agent({

View File

@ -45,7 +45,7 @@ class ClientPresence extends Presence {
const data = { const data = {
activities: [], activities: [],
afk: typeof afk === 'boolean' ? afk : false, afk: typeof afk === 'boolean' ? afk : false,
since: typeof since === 'number' && !Number.isNaN(since) ? since : 0, since: typeof since === 'number' && !Number.isNaN(since) ? since : null,
status: status ?? this.status, status: status ?? this.status,
}; };
if (activities?.length) { if (activities?.length) {

View File

@ -1076,10 +1076,10 @@ class Message extends Base {
* @returns {Promise<Message|Modal>} * @returns {Promise<Message|Modal>}
*/ */
selectMenu(menu, values = []) { selectMenu(menu, values = []) {
let selectMenu = menu; let selectMenu;
if (/[0-4]/.test(menu)) { if (/[0-4]/.test(menu)) {
selectMenu = this.components[menu]?.components[0]; selectMenu = this.components[menu]?.components[0];
} else if (typeof menu == 'string') { } else {
selectMenu = this.components selectMenu = this.components
.flatMap(row => row.components) .flatMap(row => row.components)
.find( .find(
@ -1092,7 +1092,7 @@ class Message extends Base {
if (values.length < selectMenu.minValues) { if (values.length < selectMenu.minValues) {
throw new RangeError(`[SELECT_MENU_MIN_VALUES] The minimum number of values is ${selectMenu.minValues}`); throw new RangeError(`[SELECT_MENU_MIN_VALUES] The minimum number of values is ${selectMenu.minValues}`);
} }
if (values.length > selectMenu?.maxValues) { if (values.length > selectMenu.maxValues) {
throw new RangeError(`[SELECT_MENU_MAX_VALUES] The maximum number of values is ${selectMenu.maxValues}`); throw new RangeError(`[SELECT_MENU_MAX_VALUES] The maximum number of values is ${selectMenu.maxValues}`);
} }
values = values.map(value => { values = values.map(value => {

View File

@ -404,10 +404,7 @@ class Activity {
} }
toJSON(...props) { toJSON(...props) {
return { return Util.flatten(this, ...props);
...Util.flatten(this, ...props),
type: typeof this.type === 'number' ? this.type : ActivityTypes[this.type],
};
} }
} }
@ -695,6 +692,7 @@ class RichPresence extends Activity {
* Set the large image of this activity * Set the large image of this activity
* @param {?RichPresenceImage} image The large image asset's id * @param {?RichPresenceImage} image The large image asset's id
* @returns {RichPresence} * @returns {RichPresence}
* @deprecated
*/ */
setAssetsLargeImage(image) { setAssetsLargeImage(image) {
this.assets.setLargeImage(image); this.assets.setLargeImage(image);
@ -705,6 +703,7 @@ class RichPresence extends Activity {
* Set the small image of this activity * Set the small image of this activity
* @param {?RichPresenceImage} image The small image asset's id * @param {?RichPresenceImage} image The small image asset's id
* @returns {RichPresence} * @returns {RichPresence}
* @deprecated
*/ */
setAssetsSmallImage(image) { setAssetsSmallImage(image) {
this.assets.setSmallImage(image); this.assets.setSmallImage(image);
@ -715,6 +714,7 @@ class RichPresence extends Activity {
* Hover text for the large image * Hover text for the large image
* @param {string} text Assets text * @param {string} text Assets text
* @returns {RichPresence} * @returns {RichPresence}
* @deprecated
*/ */
setAssetsLargeText(text) { setAssetsLargeText(text) {
this.assets.setLargeText(text); this.assets.setLargeText(text);
@ -725,6 +725,7 @@ class RichPresence extends Activity {
* Hover text for the small image * Hover text for the small image
* @param {string} text Assets text * @param {string} text Assets text
* @returns {RichPresence} * @returns {RichPresence}
* @deprecated
*/ */
setAssetsSmallText(text) { setAssetsSmallText(text) {
this.assets.setSmallText(text); this.assets.setSmallText(text);
@ -878,16 +879,6 @@ class RichPresence extends Activity {
return this; return this;
} }
/**
* Secrets for rich presence joining and spectating (send-only)
* @param {?string} join Secrets for rich presence joining
* @returns {RichPresence}
*/
setJoinSecret(join) {
this.secrets.join = join;
return this;
}
/** /**
* Add a button to the rich presence * Add a button to the rich presence
* @param {string} name The name of the button * @param {string} name The name of the button
@ -1066,13 +1057,6 @@ class SpotifyRPC extends RichPresence {
this.metadata.context_uri = `spotify:album:${id}`; this.metadata.context_uri = `spotify:album:${id}`;
return this; return this;
} }
toJSON() {
return {
...super.toJSON({ id: false, emoji: false, platform: false, buttons: false }),
session_id: this.presence.client.sessionId,
};
}
} }
exports.Presence = Presence; exports.Presence = Presence;

View File

@ -188,7 +188,7 @@ class Options extends null {
referrer_current: '', referrer_current: '',
referring_domain_current: '', referring_domain_current: '',
release_channel: 'stable', release_channel: 'stable',
client_build_number: 267220, client_build_number: 261141,
client_event_source: null, client_event_source: null,
}, },
compress: false, compress: false,

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
const { Agent } = require('node:http');
const { parse } = require('node:path'); const { parse } = require('node:path');
const process = require('node:process'); const process = require('node:process');
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
@ -605,7 +604,7 @@ class Util extends null {
return user ? Util._removeMentions(`@${user.username}`) : input; return user ? Util._removeMentions(`@${user.username}`) : input;
} }
const member = channel.guild?.members.cache.get(id); const member = channel.guild.members.cache.get(id);
if (member) { if (member) {
return Util._removeMentions(`@${member.displayName}`); return Util._removeMentions(`@${member.displayName}`);
} else { } else {
@ -809,15 +808,6 @@ class Util extends null {
let defaultValue; let defaultValue;
return () => (defaultValue ??= cb()); return () => (defaultValue ??= cb());
} }
/**
* Hacking check object instanceof Proxy-agent
* @param {Object} object any
* @returns {boolean}
*/
static verifyProxyAgent(object) {
return typeof object == 'object' && object.httpAgent instanceof Agent && object.httpsAgent instanceof Agent;
}
} }
module.exports = Util; module.exports = Util;

26
typings/index.d.ts vendored
View File

@ -176,9 +176,13 @@ export interface RichButton {
export class RichPresence extends Activity { export class RichPresence extends Activity {
public constructor(client: Client, data?: object); public constructor(client: Client, data?: object);
public metadata: RichPresenceMetadata; public metadata: RichPresenceMetadata;
/** @deprecated */
public setAssetsLargeImage(image?: string): this; public setAssetsLargeImage(image?: string): this;
/** @deprecated */
public setAssetsLargeText(text?: string): this; public setAssetsLargeText(text?: string): this;
/** @deprecated */
public setAssetsSmallImage(image?: string): this; public setAssetsSmallImage(image?: string): this;
/** @deprecated */
public setAssetsSmallText(text?: string): this; public setAssetsSmallText(text?: string): this;
public setName(name?: string): this; public setName(name?: string): this;
public setURL(url?: string): this; public setURL(url?: string): this;
@ -191,7 +195,6 @@ export class RichPresence extends Activity {
public setEndTimestamp(timestamp: Date | number | null): this; public setEndTimestamp(timestamp: Date | number | null): this;
public setButtons(...button: RichButton[]): this; public setButtons(...button: RichButton[]): this;
public addButton(name: string, url: string): this; public addButton(name: string, url: string): this;
public setJoinSecret(join?: string): this;
public static getExternal( public static getExternal(
client: Client, client: Client,
applicationId: Snowflake, applicationId: Snowflake,
@ -770,7 +773,6 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
public fetchGuildWidget(guild: GuildResolvable): Promise<Widget>; public fetchGuildWidget(guild: GuildResolvable): Promise<Widget>;
public sleep(timeout: number): Promise<void>; public sleep(timeout: number): Promise<void>;
public login(token?: string): Promise<string>; public login(token?: string): Promise<string>;
public passLogin(email: string, password: string, code?: string | number): Promise<string | null>;
public QRLogin(): Promise<void>; public QRLogin(): Promise<void>;
public logout(): Promise<void>; public logout(): Promise<void>;
public isReady(): this is Client<true>; public isReady(): this is Client<true>;
@ -1549,7 +1551,7 @@ export class HTTPError extends Error {
} }
// tslint:disable-next-line:no-empty-interface - Merge RateLimitData into RateLimitError to not have to type it again // tslint:disable-next-line:no-empty-interface - Merge RateLimitData into RateLimitError to not have to type it again
export interface RateLimitError extends RateLimitData {} export interface RateLimitError extends RateLimitData { }
export class RateLimitError extends Error { export class RateLimitError extends Error {
private constructor(data: RateLimitData); private constructor(data: RateLimitData);
public name: 'RateLimitError'; public name: 'RateLimitError';
@ -4904,9 +4906,9 @@ export interface AutoModerationRuleCreateOptions {
reason?: string; reason?: string;
} }
export interface AutoModerationRuleEditOptions extends Partial<Omit<AutoModerationRuleCreateOptions, 'triggerType'>> {} export interface AutoModerationRuleEditOptions extends Partial<Omit<AutoModerationRuleCreateOptions, 'triggerType'>> { }
export interface AutoModerationTriggerMetadataOptions extends Partial<AutoModerationTriggerMetadata> {} export interface AutoModerationTriggerMetadataOptions extends Partial<AutoModerationTriggerMetadata> { }
export interface AutoModerationActionOptions { export interface AutoModerationActionOptions {
type: AutoModerationActionType | AutoModerationActionTypes; type: AutoModerationActionType | AutoModerationActionTypes;
@ -5975,8 +5977,8 @@ export interface GuildAuditLogsEntryTargetField<TActionType extends GuildAuditLo
INVITE: Invite; INVITE: Invite;
MESSAGE: TActionType extends 'MESSAGE_BULK_DELETE' ? Guild | { id: Snowflake } : User; MESSAGE: TActionType extends 'MESSAGE_BULK_DELETE' ? Guild | { id: Snowflake } : User;
INTEGRATION: Integration; INTEGRATION: Integration;
CHANNEL: NonThreadGuildBasedChannel | { id: Snowflake; [x: string]: unknown }; CHANNEL: NonThreadGuildBasedChannel | { id: Snowflake;[x: string]: unknown };
THREAD: ThreadChannel | { id: Snowflake; [x: string]: unknown }; THREAD: ThreadChannel | { id: Snowflake;[x: string]: unknown };
STAGE_INSTANCE: StageInstance; STAGE_INSTANCE: StageInstance;
STICKER: Sticker; STICKER: Sticker;
GUILD_SCHEDULED_EVENT: GuildScheduledEvent; GUILD_SCHEDULED_EVENT: GuildScheduledEvent;
@ -6819,18 +6821,18 @@ export type Partialize<
partial: true; partial: true;
} & { } & {
[K in keyof Omit<T, 'client' | 'id' | 'partial' | E>]: K extends N ? null : K extends M ? T[K] | null : T[K]; [K in keyof Omit<T, 'client' | 'id' | 'partial' | E>]: K extends N ? null : K extends M ? T[K] | null : T[K];
}; };
export interface PartialDMChannel extends Partialize<DMChannel, null, null, 'lastMessageId'> { export interface PartialDMChannel extends Partialize<DMChannel, null, null, 'lastMessageId'> {
lastMessageId: undefined; lastMessageId: undefined;
} }
export interface PartialGuildMember extends Partialize<GuildMember, 'joinedAt' | 'joinedTimestamp'> {} export interface PartialGuildMember extends Partialize<GuildMember, 'joinedAt' | 'joinedTimestamp'> { }
export interface PartialMessage export interface PartialMessage
extends Partialize<Message, 'type' | 'system' | 'pinned' | 'tts', 'content' | 'cleanContent' | 'author'> {} extends Partialize<Message, 'type' | 'system' | 'pinned' | 'tts', 'content' | 'cleanContent' | 'author'> { }
export interface PartialMessageReaction extends Partialize<MessageReaction, 'count'> {} export interface PartialMessageReaction extends Partialize<MessageReaction, 'count'> { }
export interface PartialOverwriteData { export interface PartialOverwriteData {
id: Snowflake | number; id: Snowflake | number;
@ -6845,7 +6847,7 @@ export interface PartialRoleData extends RoleData {
export type PartialTypes = 'USER' | 'CHANNEL' | 'GUILD_MEMBER' | 'MESSAGE' | 'REACTION' | 'GUILD_SCHEDULED_EVENT'; export type PartialTypes = 'USER' | 'CHANNEL' | 'GUILD_MEMBER' | 'MESSAGE' | 'REACTION' | 'GUILD_SCHEDULED_EVENT';
export interface PartialUser extends Partialize<User, 'username' | 'tag' | 'discriminator'> {} export interface PartialUser extends Partialize<User, 'username' | 'tag' | 'discriminator'> { }
export type PresenceStatusData = ClientPresenceStatus | 'invisible'; export type PresenceStatusData = ClientPresenceStatus | 'invisible';