feat: Update (see description)
```diff + fix: Discord Ban (invalid headers) + fix: acceptInvite not working (invalid captcha data) + feat: automod update - feat: remove `nopecha` - feat: remove Client#updateCookie & ClientOptions#autoCookie ```
This commit is contained in:
parent
e407700865
commit
affcf5e166
@ -48,7 +48,6 @@
|
||||
### Optional packages
|
||||
|
||||
- [2captcha](https://www.npmjs.com/package/2captcha) for solving captcha (`npm install 2captcha`)
|
||||
- [nopecha](https://www.npmjs.com/package/nopecha) for solving captcha (`npm install nopecha`)
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -312,48 +312,6 @@ class Client extends BaseClient {
|
||||
return getVoiceConnection(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Cloudflare Cookie and Discord Fingerprint
|
||||
*/
|
||||
async updateCookie() {
|
||||
/* 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 => {
|
||||
if (!('set-cookie' in res.headers)) return;
|
||||
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')
|
||||
) {
|
||||
return null;
|
||||
} else {
|
||||
cookie += `${arr}; `;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
});
|
||||
this.options.http.headers.Cookie = `${cookie}locale=en`;
|
||||
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, `Update Cookie and Fingerprint failed: ${err.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the client in, establishing a WebSocket connection to Discord.
|
||||
* @param {string} [token=this.token] Token of the account to log in with
|
||||
@ -381,10 +339,6 @@ class Client extends BaseClient {
|
||||
.join('.')}`,
|
||||
);
|
||||
|
||||
if (this.options.autoCookie) {
|
||||
await this.updateCookie();
|
||||
}
|
||||
|
||||
if (this.options.presence) {
|
||||
this.options.ws.presence = this.presence._parse(this.options.presence);
|
||||
}
|
||||
@ -522,7 +476,7 @@ class Client extends BaseClient {
|
||||
|
||||
/**
|
||||
* Create a new token based on the current token
|
||||
* @returns {Promise<string>} Discord Token
|
||||
* @returns {Promise<string>} New Discord Token
|
||||
*/
|
||||
createToken() {
|
||||
return new Promise(resolve => {
|
||||
@ -537,12 +491,12 @@ class Client extends BaseClient {
|
||||
wsProperties: this.options.ws.properties,
|
||||
});
|
||||
// Step 2: Add event
|
||||
QR.on('ready', async (_, url) => {
|
||||
QR.once('ready', async (_, url) => {
|
||||
await this.remoteAuth(url, true);
|
||||
}).on('finish', (user, token) => {
|
||||
}).once('finish', (user, token) => {
|
||||
resolve(token);
|
||||
});
|
||||
|
||||
// Step 3: Connect
|
||||
QR.connect();
|
||||
});
|
||||
}
|
||||
@ -637,6 +591,24 @@ class Client extends BaseClient {
|
||||
return new Invite(this, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join this Guild using this invite (Use with caution)
|
||||
* @param {InviteResolvable} invite Invite code or URL
|
||||
* @deprecated
|
||||
* @returns {Promise<undefined>}
|
||||
*/
|
||||
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({
|
||||
data: {},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically Redeem Nitro from raw message.
|
||||
* @param {Message} message Discord Message
|
||||
@ -1005,9 +977,6 @@ class Client extends BaseClient {
|
||||
if (options && typeof options.readyStatus !== 'boolean') {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'readyStatus', 'a boolean');
|
||||
}
|
||||
if (options && typeof options.autoCookie !== 'boolean') {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'autoCookie', 'a boolean');
|
||||
}
|
||||
if (options && typeof options.autoRedeemNitro !== 'boolean') {
|
||||
throw new TypeError('CLIENT_INVALID_OPTION', 'autoRedeemNitro', 'a boolean');
|
||||
}
|
||||
@ -1025,12 +994,6 @@ class Client extends BaseClient {
|
||||
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 (options && typeof options.DMSync !== 'boolean') {
|
||||
|
@ -25,6 +25,24 @@ class AutoModerationRuleManager extends CachedManager {
|
||||
this.guild = guild;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves an {@link AutoModerationRuleResolvable} to an {@link AutoModerationRule} object.
|
||||
* @method resolve
|
||||
* @memberof AutoModerationRuleManager
|
||||
* @instance
|
||||
* @param {AutoModerationRuleResolvable} autoModerationRule The AutoModerationRule resolvable to resolve
|
||||
* @returns {?AutoModerationRule}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves an {@link AutoModerationRuleResolvable} to a {@link AutoModerationRule} id.
|
||||
* @method resolveId
|
||||
* @memberof AutoModerationRuleManager
|
||||
* @instance
|
||||
* @param {AutoModerationRuleResolvable} autoModerationRule The AutoModerationRule resolvable to resolve
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
|
||||
_add(data, cache) {
|
||||
return super._add(data, cache, { extras: [this.guild] });
|
||||
}
|
||||
@ -265,24 +283,6 @@ class AutoModerationRuleManager extends CachedManager {
|
||||
const autoModerationRuleId = this.resolveId(autoModerationRule);
|
||||
await this.client.api.guilds(this.guild.id)('auto-moderation').rules(autoModerationRuleId).delete({ reason });
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves an {@link AutoModerationRuleResolvable} to an {@link AutoModerationRule} object.
|
||||
* @method resolve
|
||||
* @memberof AutoModerationRuleManager
|
||||
* @instance
|
||||
* @param {AutoModerationRuleResolvable} autoModerationRule The AutoModerationRule resolvable to resolve
|
||||
* @returns {?AutoModerationRule}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves an {@link AutoModerationRuleResolvable} to a {@link AutoModerationRule} id.
|
||||
* @method resolveId
|
||||
* @memberof AutoModerationRuleManager
|
||||
* @instance
|
||||
* @param {AutoModerationRuleResolvable} autoModerationRule The AutoModerationRule resolvable to resolve
|
||||
* @returns {?Snowflake}
|
||||
*/
|
||||
}
|
||||
|
||||
module.exports = AutoModerationRuleManager;
|
||||
|
@ -48,26 +48,32 @@ class APIRequest {
|
||||
'Accept-Language': 'en-US,en;q=0.9',
|
||||
'Sec-Ch-Ua': `"Not?A_Brand";v="8", "Chromium";v="${chromeVersion}", "Google Chrome";v="${chromeVersion}"`,
|
||||
'Sec-Ch-Ua-Mobile': '?0',
|
||||
'Sec-Ch-Ua-Platform': '"Windows"',
|
||||
'Sec-Ch-Ua-Platform': 'Windows',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'same-origin',
|
||||
'X-Debug-Options': 'bugReporterEnabled',
|
||||
'X-Super-Properties': `${Buffer.from(
|
||||
this.client.options.jsonTransformer(this.client.options.ws.properties),
|
||||
this.client.options.jsonTransformer({
|
||||
...this.client.options.ws.properties,
|
||||
browser_user_agent: this.client.options.http.headers['User-Agent'],
|
||||
}),
|
||||
'ascii',
|
||||
).toString('base64')}`,
|
||||
'X-Discord-Locale': 'en-US',
|
||||
'User-Agent': this.client.options.http.headers['User-Agent'],
|
||||
Origin: 'https://discord.com',
|
||||
Connection: 'keep-alive',
|
||||
};
|
||||
|
||||
/* Remove
|
||||
this.client.options.http.headers['User-Agent'] = this.fullUserAgent;
|
||||
*/
|
||||
|
||||
if (this.options.auth !== false) headers.Authorization = this.rest.getAuth();
|
||||
if (this.options.reason) headers['X-Audit-Log-Reason'] = encodeURIComponent(this.options.reason);
|
||||
if (this.options.headers) headers = Object.assign(headers, this.options.headers);
|
||||
if (this.options.webhook === true) {
|
||||
headers = {
|
||||
'User-Agent': this.client.options.http.headers['User-Agent'],
|
||||
};
|
||||
}
|
||||
|
||||
let body;
|
||||
if (this.options.files?.length) {
|
||||
|
@ -16,10 +16,17 @@ module.exports = class CaptchaSolver {
|
||||
const lib = require('2captcha');
|
||||
this.service = '2captcha';
|
||||
this.solver = new lib.Solver(key);
|
||||
this.solve = siteKey =>
|
||||
this.solve = (data, userAgent) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const siteKey = data.captcha_sitekey;
|
||||
const postD = data.captcha_rqdata
|
||||
? {
|
||||
data: data.captcha_rqdata,
|
||||
userAgent,
|
||||
}
|
||||
: undefined;
|
||||
this.solver
|
||||
.hcaptcha(siteKey, 'discord.com')
|
||||
.hcaptcha(siteKey, 'https://discord.com/channels/@me', postD)
|
||||
.then(res => {
|
||||
resolve(res.data);
|
||||
})
|
||||
@ -30,31 +37,6 @@ module.exports = class CaptchaSolver {
|
||||
throw this._missingModule('2captcha');
|
||||
}
|
||||
}
|
||||
case 'nopecha': {
|
||||
if (!key || typeof key !== 'string') throw new Error('NopeCHA key is not provided');
|
||||
try {
|
||||
const { Configuration, NopeCHAApi } = require('nopecha');
|
||||
const configuration = new Configuration({
|
||||
apiKey: key,
|
||||
});
|
||||
this.service = 'nopecha';
|
||||
this.solver = new NopeCHAApi(configuration);
|
||||
this.solve = sitekey =>
|
||||
new Promise((resolve, reject) => {
|
||||
this.solver
|
||||
.solveToken({
|
||||
type: 'hcaptcha',
|
||||
sitekey,
|
||||
url: 'https://discord.com',
|
||||
})
|
||||
.then(res => (res ? resolve(res) : reject(new Error('Captcha could not be solved'))))
|
||||
.catch(reject);
|
||||
});
|
||||
break;
|
||||
} catch (e) {
|
||||
throw this._missingModule('nopecha');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
solve() {}
|
||||
|
@ -11,6 +11,25 @@ const {
|
||||
Events: { DEBUG, RATE_LIMIT, INVALID_REQUEST_WARNING, API_RESPONSE, API_REQUEST },
|
||||
} = require('../util/Constants');
|
||||
|
||||
const cookieFilter = str => {
|
||||
const blackList = ['expires', 'path', 'domain', 'httponly', 'secure', 'max-age', 'samesite'];
|
||||
if (blackList.some(s => str.toLowerCase().includes(`${s}`))) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
function parseCookie(str, old) {
|
||||
const oldProps = old.split(';').filter(cookieFilter);
|
||||
const allProps = str.split(';').filter(cookieFilter);
|
||||
// Update data from all to old
|
||||
allProps.forEach(prop => {
|
||||
const key = prop.split('=')[0];
|
||||
const index = oldProps.findIndex(s => s.startsWith(key));
|
||||
if (index !== -1) oldProps[index] = prop;
|
||||
else oldProps.push(prop);
|
||||
});
|
||||
return oldProps.filter(s => s).join('; ');
|
||||
}
|
||||
|
||||
const captchaMessage = [
|
||||
'incorrect-captcha',
|
||||
'response-already-used',
|
||||
@ -240,6 +259,20 @@ class RequestHandler {
|
||||
|
||||
let sublimitTimeout;
|
||||
if (res.headers) {
|
||||
// Cookie:
|
||||
const cookie = res.headers.get('set-cookie');
|
||||
if (cookie) {
|
||||
if (typeof cookie == 'string') {
|
||||
this.manager.client.options.http.headers.Cookie = parseCookie(
|
||||
cookie,
|
||||
this.manager.client.options.http.headers.Cookie || '',
|
||||
);
|
||||
this.manager.client.emit(
|
||||
'debug',
|
||||
`[REST] Set new cookie: ${this.manager.client.options.http.headers.Cookie}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
const serverDate = res.headers.get('date');
|
||||
const limit = res.headers.get('x-ratelimit-limit');
|
||||
const remaining = res.headers.get('x-ratelimit-remaining');
|
||||
@ -368,7 +401,10 @@ class RequestHandler {
|
||||
Route : ${request.route}
|
||||
Info : ${inspect(data, { depth: null })}`,
|
||||
);
|
||||
const captcha = await this.manager.captchaService.solve(data.captcha_sitekey);
|
||||
const captcha = await this.manager.captchaService.solve(
|
||||
data,
|
||||
this.manager.client.options.http.headers['User-Agent'],
|
||||
);
|
||||
this.manager.client.emit(
|
||||
DEBUG,
|
||||
`Captcha solved.
|
||||
|
@ -343,10 +343,12 @@ class Invite extends Base {
|
||||
},
|
||||
});
|
||||
const guild = this.client.guilds.cache.get(this.guild.id);
|
||||
/*
|
||||
//
|
||||
if (autoVerify) {
|
||||
console.warn('Feature is under maintenance - Invite#acceptInvite(true)');
|
||||
}
|
||||
/* Disabled
|
||||
*/
|
||||
if (autoVerify) {
|
||||
const getForm = await this.client.api
|
||||
.guilds(this.guild.id)
|
||||
@ -358,7 +360,6 @@ class Invite extends Base {
|
||||
// https://discord.com/api/v9/guilds/:id/requests/@me
|
||||
await this.client.api.guilds(this.guild.id).requests['@me'].put({ data: { form_fields: [form] } });
|
||||
}
|
||||
*/
|
||||
return guild;
|
||||
}
|
||||
}
|
||||
|
@ -201,6 +201,7 @@ class Webhook {
|
||||
query: { thread_id: messagePayload.options.threadId, wait: true },
|
||||
auth: false,
|
||||
versioned: true,
|
||||
webhook: true,
|
||||
});
|
||||
return this.client.channels?.cache.get(d.channel_id)?.messages._add(d, false) ?? d;
|
||||
}
|
||||
@ -230,6 +231,7 @@ class Webhook {
|
||||
query: { wait: true },
|
||||
auth: false,
|
||||
data: body,
|
||||
webhook: true,
|
||||
});
|
||||
return data.toString() === 'ok';
|
||||
}
|
||||
@ -257,6 +259,7 @@ class Webhook {
|
||||
data: { name, avatar, channel_id: channel },
|
||||
reason,
|
||||
auth: !this.token || Boolean(channel),
|
||||
webhook: true,
|
||||
});
|
||||
|
||||
this.name = data.name;
|
||||
@ -305,6 +308,7 @@ class Webhook {
|
||||
thread_id: cacheOrOptions.threadId,
|
||||
},
|
||||
auth: false,
|
||||
webhook: true,
|
||||
});
|
||||
return this.client.channels?.cache.get(data.channel_id)?.messages._add(data, cacheOrOptions.cache) ?? data;
|
||||
}
|
||||
@ -337,6 +341,7 @@ class Webhook {
|
||||
thread_id: messagePayload.options.threadId,
|
||||
},
|
||||
auth: false,
|
||||
webhook: true,
|
||||
});
|
||||
|
||||
const messageManager = this.client.channels?.cache.get(d.channel_id)?.messages;
|
||||
@ -356,7 +361,7 @@ class Webhook {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async delete(reason) {
|
||||
await this.client.api.webhooks(this.id, this.token).delete({ reason, auth: !this.token });
|
||||
await this.client.api.webhooks(this.id, this.token).delete({ reason, auth: !this.token, webhook: true });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -376,6 +381,7 @@ class Webhook {
|
||||
thread_id: threadId,
|
||||
},
|
||||
auth: false,
|
||||
webhook: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -21,10 +21,9 @@ exports.MaxBulkDeletableMessageAge = 1_209_600_000;
|
||||
/**
|
||||
* API captcha solver
|
||||
* * `2captcha` - 2captcha.com
|
||||
* * `nopecha` - nopecha.com
|
||||
* @typedef {string[]} captchaServices
|
||||
*/
|
||||
exports.captchaServices = ['2captcha', 'nopecha'];
|
||||
exports.captchaServices = ['2captcha'];
|
||||
|
||||
/**
|
||||
* Automatically scan and delete direct messages you receive that contain explicit media content.
|
||||
@ -1770,10 +1769,13 @@ function createEnum(keys) {
|
||||
* @property {Object<ApplicationCommandType, number>} ApplicationCommandTypes
|
||||
* The type of an {@link ApplicationCommand} object.
|
||||
* @property {Object<AutoModerationRuleTriggerType, number>} AutoModerationRuleTriggerTypes Characterizes the type
|
||||
* of contentwhich can trigger the rule.
|
||||
* of content which can trigger the rule.
|
||||
* @property {Object<AutoModerationActionType, number>} AutoModerationActionTypes
|
||||
* A type of an action which executes whenever a rule is triggered.
|
||||
* @property {Object<AutoModerationRuleKeywordPresetType, number>} AutoModerationRuleKeywordPresetTypes
|
||||
* @property {Object<AutoModerationRuleEventType, number>} AutoModerationRuleEventTypes
|
||||
* The internally pre-defined wordsetswhich will be searched for in content
|
||||
* @property {Object<AutoModerationRuleEventType, number>} AutoModerationRuleEventTypes Indicates in what event context
|
||||
* a rule should be checked.
|
||||
* @property {Object<ChannelType, number>} ChannelTypes All available channel types.
|
||||
* @property {ClientApplicationAssetTypes} ClientApplicationAssetTypes The types of an {@link ApplicationAsset} object.
|
||||
* @property {Object<Color, number>} Colors An object with regularly used colors.
|
||||
|
@ -38,7 +38,6 @@ const { randomUA } = require('../util/Constants');
|
||||
* from the WebSocket. Don't have this too high/low. Its best to have it between 2_000-6_000 ms.
|
||||
* @property {boolean} [checkUpdate=true] Display module update information on the screen
|
||||
* @property {boolean} [readyStatus=true] Sync state with Discord Client
|
||||
* @property {boolean} [autoCookie=true] Automatically add Cookies to Request on startup
|
||||
* @property {boolean} [patchVoice=false] Automatically patch @discordjs/voice module (support for call)
|
||||
* @property {string} [captchaService=null] Captcha service to use for solving captcha {@link captchaServices}
|
||||
* @property {string} [captchaKey=null] Captcha service key
|
||||
@ -152,7 +151,6 @@ class Options extends null {
|
||||
closeTimeout: 5_000,
|
||||
checkUpdate: true,
|
||||
readyStatus: true,
|
||||
autoCookie: true,
|
||||
autoRedeemNitro: false,
|
||||
captchaService: '',
|
||||
captchaKey: null,
|
||||
|
@ -431,10 +431,10 @@ new DiscordAuthWebsocket({
|
||||
headers: {
|
||||
Accept: '*/*',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept-Language': 'en-US,en;q=0.9',
|
||||
'Cache-Control': 'no-cache',
|
||||
Pragma: 'no-cache',
|
||||
'Sec-Ch-Ua': `"Google Chrome";v="${chromeVersion}", "Chromium";v="${chromeVersion}", "Not=A?Brand";v="24"`,
|
||||
'Accept-Language': 'en-US,en;q=0.9',
|
||||
'Sec-Ch-Ua': `"Not?A_Brand";v="8", "Chromium";v="${chromeVersion}", "Google Chrome";v="${chromeVersion}"`,
|
||||
'Sec-Ch-Ua-Mobile': '?0',
|
||||
'Sec-Ch-Ua-Platform': '"Windows"',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
@ -446,6 +446,7 @@ new DiscordAuthWebsocket({
|
||||
)}`,
|
||||
'X-Discord-Locale': 'en-US',
|
||||
'User-Agent': this.options.userAgent,
|
||||
Origin: 'https://discord.com',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
4
typings/index.d.ts
vendored
4
typings/index.d.ts
vendored
@ -856,7 +856,6 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
|
||||
// Added
|
||||
public settings: ClientSettingManager;
|
||||
public relationships: RelationshipManager;
|
||||
public updateCookie(): Promise<void>;
|
||||
public readonly callVoice?: VoiceConnection;
|
||||
public voiceStates: VoiceStateManager;
|
||||
// End
|
||||
@ -4767,7 +4766,6 @@ export interface ClientOptions {
|
||||
// add
|
||||
checkUpdate?: boolean;
|
||||
readyStatus?: boolean;
|
||||
autoCookie?: boolean;
|
||||
autoRedeemNitro?: boolean;
|
||||
patchVoice?: boolean;
|
||||
password?: string;
|
||||
@ -4779,7 +4777,7 @@ export interface ClientOptions {
|
||||
usingNewAttachmentAPI?: boolean;
|
||||
}
|
||||
|
||||
export type captchaServices = '2captcha' | 'nopecha';
|
||||
export type captchaServices = '2captcha';
|
||||
|
||||
// end copy
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user