feat: implement ProxyAgent support and update HTTP options for improved proxy handling
This commit is contained in:
@@ -14,10 +14,17 @@ const client = new Discord.Client({
|
|||||||
agent: proxy, // WebSocket Proxy
|
agent: proxy, // WebSocket Proxy
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
agent: proxy, // REST Proxy
|
// API Proxy
|
||||||
|
// Read more: https://github.com/nodejs/undici/blob/main/docs/docs/api/ProxyAgent.md
|
||||||
|
// agent: ProxyAgentOptions
|
||||||
|
agent: 'my.proxy.server',
|
||||||
|
// or new URL('my.proxy.server')
|
||||||
|
// or { uri: 'my.proxy.server' }
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// So if you only need to use the API Proxy (for the purpose of saving data), you don't need to install `proxy-agent`.
|
||||||
|
|
||||||
client.on('ready', async () => {
|
client.on('ready', async () => {
|
||||||
console.log('Ready!', client.user.tag);
|
console.log('Ready!', client.user.tag);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -588,7 +588,7 @@ class Client extends BaseClient {
|
|||||||
* await client.acceptInvite('https://discord.gg/genshinimpact', { bypassOnboarding: true, bypassVerify: true })
|
* await client.acceptInvite('https://discord.gg/genshinimpact', { bypassOnboarding: true, bypassVerify: true })
|
||||||
*/
|
*/
|
||||||
async acceptInvite(invite, options = { bypassOnboarding: true, bypassVerify: true }) {
|
async acceptInvite(invite, options = { bypassOnboarding: true, bypassVerify: true }) {
|
||||||
throw new Error('METHOD_WARNING');
|
// ! throw new Error('METHOD_WARNING');
|
||||||
const code = DataResolver.resolveInviteCode(invite);
|
const code = DataResolver.resolveInviteCode(invite);
|
||||||
if (!code) throw new Error('INVITE_RESOLVE_CODE');
|
if (!code) throw new Error('INVITE_RESOLVE_CODE');
|
||||||
const i = await this.fetchInvite(code);
|
const i = await this.fetchInvite(code);
|
||||||
@@ -711,7 +711,7 @@ class Client extends BaseClient {
|
|||||||
})
|
})
|
||||||
*/
|
*/
|
||||||
authorizeURL(url, options = { authorize: true }) {
|
authorizeURL(url, options = { authorize: true }) {
|
||||||
throw new Error('METHOD_WARNING');
|
// ! throw new Error('METHOD_WARNING');
|
||||||
const pathnameAPI = /\/api\/(v\d{1,2}\/)?oauth2\/authorize/;
|
const pathnameAPI = /\/api\/(v\d{1,2}\/)?oauth2\/authorize/;
|
||||||
const pathnameURL = /\/oauth2\/authorize/;
|
const pathnameURL = /\/oauth2\/authorize/;
|
||||||
const url_ = new URL(url);
|
const url_ = new URL(url);
|
||||||
@@ -803,9 +803,6 @@ class Client extends BaseClient {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_validateOptions(options = this.options) {
|
_validateOptions(options = this.options) {
|
||||||
options.captchaSolver = () => {
|
|
||||||
throw new Error('METHOD_WARNING');
|
|
||||||
};
|
|
||||||
if (typeof options.makeCache !== 'function') {
|
if (typeof options.makeCache !== 'function') {
|
||||||
throw new TypeError('CLIENT_INVALID_OPTION', 'makeCache', 'a function');
|
throw new TypeError('CLIENT_INVALID_OPTION', 'makeCache', 'a function');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const Buffer = require('node:buffer').Buffer;
|
const Buffer = require('node:buffer').Buffer;
|
||||||
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');
|
||||||
const { CookieJar } = require('tough-cookie');
|
const { CookieJar } = require('tough-cookie');
|
||||||
const { fetch: fetchOriginal, FormData } = require('undici');
|
const { fetch: fetchOriginal, FormData, buildConnector, Client, ProxyAgent } = require('undici');
|
||||||
const { ciphers } = require('../util/Constants');
|
const { ciphers } = require('../util/Constants');
|
||||||
const Util = require('../util/Util');
|
const Util = require('../util/Util');
|
||||||
|
|
||||||
@@ -39,23 +38,14 @@ class APIRequest {
|
|||||||
|
|
||||||
make(captchaKey, captchaRqToken) {
|
make(captchaKey, captchaRqToken) {
|
||||||
if (!agent) {
|
if (!agent) {
|
||||||
if (Util.verifyProxyAgent(this.client.options.http.agent)) {
|
const r_ = Util.checkUndiciProxyAgent(this.client.options.http.agent);
|
||||||
// Bad code
|
if (!r_) {
|
||||||
for (const [k, v] of Object.entries({
|
agent = new Client('https://discord.com', {
|
||||||
keepAlive: true,
|
connect: buildConnector({ ciphers: ciphers.join(':') }),
|
||||||
honorCipherOrder: true,
|
});
|
||||||
secureProtocol: 'TLSv1_2_method',
|
|
||||||
ciphers: ciphers.join(':'),
|
|
||||||
})) {
|
|
||||||
this.client.options.http.agent.httpsAgent[k] = v;
|
|
||||||
}
|
|
||||||
agent = this.client.options.http.agent;
|
|
||||||
} else {
|
} else {
|
||||||
agent = new https.Agent({
|
agent = new ProxyAgent({
|
||||||
...this.client.options.http.agent,
|
...r_,
|
||||||
keepAlive: true,
|
|
||||||
honorCipherOrder: true,
|
|
||||||
secureProtocol: 'TLSv1_2_method',
|
|
||||||
ciphers: ciphers.join(':'),
|
ciphers: ciphers.join(':'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -70,7 +60,7 @@ class APIRequest {
|
|||||||
let headers = {
|
let headers = {
|
||||||
accept: '*/*',
|
accept: '*/*',
|
||||||
'accept-language': 'en-US',
|
'accept-language': 'en-US',
|
||||||
'sec-ch-ua': '"Not?A_Brand";v="8", "Chromium";v="108"',
|
'sec-ch-ua': '"Chromium";v="131", "Not_A Brand";v="24"',
|
||||||
'sec-ch-ua-mobile': '?0',
|
'sec-ch-ua-mobile': '?0',
|
||||||
'sec-ch-ua-platform': '"Windows"',
|
'sec-ch-ua-platform': '"Windows"',
|
||||||
'sec-fetch-dest': 'empty',
|
'sec-fetch-dest': 'empty',
|
||||||
@@ -86,6 +76,7 @@ class APIRequest {
|
|||||||
origin: 'https://discord.com',
|
origin: 'https://discord.com',
|
||||||
...this.client.options.http.headers,
|
...this.client.options.http.headers,
|
||||||
'User-Agent': this.fullUserAgent,
|
'User-Agent': this.fullUserAgent,
|
||||||
|
priority: 'u=1, i',
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.options.auth !== false) headers.Authorization = this.rest.getAuth();
|
if (this.options.auth !== false) headers.Authorization = this.rest.getAuth();
|
||||||
@@ -146,10 +137,11 @@ class APIRequest {
|
|||||||
return fetch(url, {
|
return fetch(url, {
|
||||||
method: this.method.toUpperCase(), // Undici doesn't normalize "patch" into "PATCH" (which surprisingly follows the spec).
|
method: this.method.toUpperCase(), // Undici doesn't normalize "patch" into "PATCH" (which surprisingly follows the spec).
|
||||||
headers,
|
headers,
|
||||||
agent,
|
|
||||||
body,
|
body,
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
redirect: 'follow',
|
redirect: 'follow',
|
||||||
|
dispatcher: agent,
|
||||||
|
credentials: 'include',
|
||||||
}).finally(() => clearTimeout(timeout));
|
}).finally(() => clearTimeout(timeout));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ const { Error, RangeError, TypeError } = require('../errors');
|
|||||||
exports.MaxBulkDeletableMessageAge = 1_209_600_000;
|
exports.MaxBulkDeletableMessageAge = 1_209_600_000;
|
||||||
|
|
||||||
exports.UserAgent =
|
exports.UserAgent =
|
||||||
'Mozilla/5.0 (Windows NT 10.0; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.215 Safari/537.36';
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Electron/33.0.0 Safari/537.36';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Google Chrome v108 TLS ciphers
|
* Google Chrome v131 TLS ciphers
|
||||||
* @see {@link https://tls.browserleaks.com/tls}
|
* @see {@link https://tls.browserleaks.com/tls}
|
||||||
* @see {@link https://github.com/yifeikong/curl-impersonate}
|
* @see {@link https://github.com/yifeikong/curl-impersonate}
|
||||||
* @typedef {Array<string>} Ciphers
|
* @typedef {Array<string>} Ciphers
|
||||||
|
|||||||
@@ -128,11 +128,17 @@ const Intents = require('./Intents');
|
|||||||
* @see {@link https://nodejs.org/api/http.html#http_new_agent_options}
|
* @see {@link https://nodejs.org/api/http.html#http_new_agent_options}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProxyAgent options.
|
||||||
|
* @typedef {Object} ProxyAgentOptions
|
||||||
|
* @see {@link https://github.com/nodejs/undici/blob/main/docs/docs/api/ProxyAgent.md}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP options
|
* HTTP options
|
||||||
* @typedef {Object} HTTPOptions
|
* @typedef {Object} HTTPOptions
|
||||||
* @property {number} [version=9] API version to use
|
* @property {number} [version=9] API version to use
|
||||||
* @property {AgentOptions} [agent={}] HTTPS Agent options
|
* @property {ProxyAgentOptions} [agent={}] ProxyAgent options
|
||||||
* @property {string} [api='https://discord.com/api'] Base URL of the API
|
* @property {string} [api='https://discord.com/api'] Base URL of the API
|
||||||
* @property {string} [cdn='https://cdn.discordapp.com'] Base URL of the CDN
|
* @property {string} [cdn='https://cdn.discordapp.com'] Base URL of the CDN
|
||||||
* @property {string} [invite='https://discord.gg'] Base URL of invites
|
* @property {string} [invite='https://discord.gg'] Base URL of invites
|
||||||
@@ -181,14 +187,14 @@ class Options extends null {
|
|||||||
device: '',
|
device: '',
|
||||||
system_locale: 'vi-VN',
|
system_locale: 'vi-VN',
|
||||||
browser_user_agent: UserAgent,
|
browser_user_agent: UserAgent,
|
||||||
browser_version: '108.0.5359.215',
|
browser_version: '131.0.0.0',
|
||||||
os_version: '10',
|
os_version: '10',
|
||||||
referrer: '',
|
referrer: '',
|
||||||
referring_domain: '',
|
referring_domain: '',
|
||||||
referrer_current: '',
|
referrer_current: '',
|
||||||
referring_domain_current: '',
|
referring_domain_current: '',
|
||||||
release_channel: 'stable',
|
release_channel: 'stable',
|
||||||
client_build_number: 338907,
|
client_build_number: 353304,
|
||||||
client_event_source: null,
|
client_event_source: null,
|
||||||
},
|
},
|
||||||
compress: false,
|
compress: false,
|
||||||
|
|||||||
@@ -874,6 +874,21 @@ class Util extends null {
|
|||||||
return typeof object == 'object' && object.httpAgent instanceof Agent && object.httpsAgent instanceof Agent;
|
return typeof object == 'object' && object.httpAgent instanceof Agent && object.httpsAgent instanceof Agent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static checkUndiciProxyAgent(data) {
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
return {
|
||||||
|
uri: data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (data instanceof URL) {
|
||||||
|
return {
|
||||||
|
uri: data.toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (typeof data === 'object' && typeof data.uri === 'string') return data;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static createPromiseInteraction(client, nonce, timeoutMs = 5_000, isHandlerDeferUpdate = false, parent) {
|
static createPromiseInteraction(client, nonce, timeoutMs = 5_000, isHandlerDeferUpdate = false, parent) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Waiting for MsgCreate / ModalCreate
|
// Waiting for MsgCreate / ModalCreate
|
||||||
|
|||||||
4
typings/index.d.ts
vendored
4
typings/index.d.ts
vendored
@@ -61,7 +61,7 @@ import {
|
|||||||
import { ChildProcess, ChildProcessWithoutNullStreams } from 'node:child_process';
|
import { ChildProcess, ChildProcessWithoutNullStreams } from 'node:child_process';
|
||||||
import { EventEmitter } from 'node:events';
|
import { EventEmitter } from 'node:events';
|
||||||
import { AgentOptions } from 'node:https';
|
import { AgentOptions } from 'node:https';
|
||||||
import { Response } from 'undici';
|
import { Response, ProxyAgent } from 'undici';
|
||||||
import { Readable, Writable, Stream } from 'node:stream';
|
import { Readable, Writable, Stream } from 'node:stream';
|
||||||
import { MessagePort, Worker } from 'node:worker_threads';
|
import { MessagePort, Worker } from 'node:worker_threads';
|
||||||
import * as WebSocket from 'ws';
|
import * as WebSocket from 'ws';
|
||||||
@@ -6726,7 +6726,7 @@ export interface HTTPErrorData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface HTTPOptions {
|
export interface HTTPOptions {
|
||||||
agent?: Omit<AgentOptions, 'keepAlive'>;
|
agent?: Omit<ProxyAgent.Options, 'keepAlive'>;
|
||||||
api?: string;
|
api?: string;
|
||||||
version?: number;
|
version?: number;
|
||||||
host?: string;
|
host?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user