feat: Login Discord (email + password)

This commit is contained in:
March 7th 2022-11-08 23:56:17 +07:00
parent 6255db8aa2
commit f5de4de7c0
6 changed files with 75 additions and 13 deletions

View File

@ -229,7 +229,7 @@ class Client extends BaseClient {
* Password cache * Password cache
* @type {?string} * @type {?string}
*/ */
this.password = null; this.password = this.options.password;
/** /**
* Nitro cache * Nitro cache
@ -392,6 +392,63 @@ class Client extends BaseClient {
} }
} }
/**
* Login Discord with Username and Password
* @param {string} username Email or Phone Number
* @param {?string} password Password
* @param {?string} mfaCode 2FA Code / Backup Code
* @returns {Promise<string>}
*/
async normalLogin(username, password = this.password, mfaCode) {
if (!username || !password || typeof username !== 'string' || typeof password !== 'string') {
throw new Error('NORMAL_LOGIN');
}
this.emit(
Events.DEBUG,
`Connecting to Discord with:
username: ${username}
password: ${password.replace(/./g, '*')}`,
);
const data = await this.api.auth.login.post({
data: {
login: username,
password: password,
undelete: false,
captcha_key: null,
login_source: null,
gift_code_sku_id: null,
},
auth: false,
});
this.password = password;
if (!data.token && data.ticket && data.mfa) {
this.emit(Events.DEBUG, `Using 2FA Code: ${mfaCode}`);
const normal2fa = /(\d{6})/g;
const backupCode = /([a-z0-9]{4})-([a-z0-9]{4})/g;
if (!mfaCode || typeof mfaCode !== 'string') {
throw new Error('LOGIN_FAILED_2FA');
}
if (normal2fa.test(mfaCode) || backupCode.test(mfaCode)) {
const data2 = await this.api.auth.mfa.totp.post({
data: {
code: mfaCode,
ticket: data.ticket,
login_source: null,
gift_code_sku_id: null,
},
auth: false,
});
return this.login(data2.token);
} else {
throw new Error('LOGIN_FAILED_2FA');
}
} else if (data.token) {
return this.login(data.token);
} else {
throw new Error('LOGIN_FAILED_UNKNOWN');
}
}
/** /**
* Sign in with the QR code on your phone. * Sign in with the QR code on your phone.
* @param {boolean} debug Debug mode * @param {boolean} debug Debug mode
@ -928,6 +985,9 @@ class Client extends BaseClient {
if (options && typeof options.patchVoice !== 'boolean') { if (options && typeof options.patchVoice !== 'boolean') {
throw new TypeError('CLIENT_INVALID_OPTION', 'patchVoice', 'a boolean'); throw new TypeError('CLIENT_INVALID_OPTION', 'patchVoice', 'a boolean');
} }
if (options && options.password && typeof options.password !== 'string') {
throw new TypeError('CLIENT_INVALID_OPTION', 'password', 'a string');
}
if (options && typeof options.proxy !== 'string') { if (options && typeof options.proxy !== 'string') {
throw new TypeError('CLIENT_INVALID_OPTION', 'proxy', 'a string'); throw new TypeError('CLIENT_INVALID_OPTION', 'proxy', 'a string');
} }

View File

@ -210,6 +210,9 @@ const Messages = {
MISSING_CAPTCHA_SERVICE: 'This feature is only available for enabled captcha handler.', MISSING_CAPTCHA_SERVICE: 'This feature is only available for enabled captcha handler.',
GUILD_FORUM_MESSAGE_REQUIRED: 'You must provide a message to create a guild forum thread', GUILD_FORUM_MESSAGE_REQUIRED: 'You must provide a message to create a guild forum thread',
NORMAL_LOGIN: 'Username and password are required for normal login',
LOGIN_FAILED_UNKNOWN: 'Login failed',
LOGIN_FAILED_2FA: 'Login failed, 2FA code is required',
}; };
for (const [name, message] of Object.entries(Messages)) register(name, message); for (const [name, message] of Object.entries(Messages)) register(name, message);

View File

@ -33,17 +33,15 @@ class SessionManager extends CachedManager {
/** /**
* Logout the client (remote). * Logout the client (remote).
* @param {string} password User's password
* @param {string | null} mfaCode MFA code (if 2FA is enabled) * @param {string | null} mfaCode MFA code (if 2FA is enabled)
* @returns {Promise<undefined>} * @returns {Promise<undefined>}
*/ */
logoutAllDevices(password, mfaCode) { logoutAllDevices(mfaCode) {
password = password || this.client.password; if (typeof this.client.password !== 'string') throw new Error('REQUIRE_PASSWORD');
if (!password || typeof password !== 'string') throw new Error('REQUIRE_PASSWORD');
return this.client.api.auth.sessions.logout({ return this.client.api.auth.sessions.logout({
data: { data: {
session_id_hashes: this.cache.map(session => session.id), session_id_hashes: this.cache.map(session => session.id),
password, password: this.client.password,
code: typeof mfaCode === 'string' ? mfaCode : undefined, code: typeof mfaCode === 'string' ? mfaCode : undefined,
}, },
}); });

View File

@ -59,17 +59,15 @@ class Session extends Base {
/** /**
* Logout the client (remote). * Logout the client (remote).
* @param {string} password User's password
* @param {string | null} mfaCode MFA code (if 2FA is enabled) * @param {string | null} mfaCode MFA code (if 2FA is enabled)
* @returns {Promise<undefined>} * @returns {Promise<undefined>}
*/ */
logout(password, mfaCode) { logout(mfaCode) {
password = password || this.client.password; if (typeof this.client.password !== 'string') throw new Error('REQUIRE_PASSWORD', 'You must provide a password.');
if (!password || typeof password !== 'string') throw new Error('REQUIRE_PASSWORD', 'You must provide a password.');
return this.client.api.auth.sessions.logout({ return this.client.api.auth.sessions.logout({
data: { data: {
session_id_hashes: [this.id], session_id_hashes: [this.id],
password, password: this.client.password,
code: typeof mfaCode === 'string' ? mfaCode : undefined, code: typeof mfaCode === 'string' ? mfaCode : undefined,
}, },
}); });

View File

@ -154,6 +154,7 @@ class Options extends null {
captchaKey: null, captchaKey: null,
DMSync: false, DMSync: false,
patchVoice: false, patchVoice: false,
password: null,
waitGuildTimeout: 15_000, waitGuildTimeout: 15_000,
messageCreateEventGuildTimeout: 100, messageCreateEventGuildTimeout: 100,
shardCount: 1, shardCount: 1,

6
typings/index.d.ts vendored
View File

@ -164,7 +164,7 @@ import {
export abstract class SessionManager extends CachedManager { export abstract class SessionManager extends CachedManager {
constructor(client: Client, iterable?: Iterable<unknown>); constructor(client: Client, iterable?: Iterable<unknown>);
public fetch(): Promise<SessionManager>; public fetch(): Promise<SessionManager>;
public logoutAllDevices(password?: string, mfaCode?: string): Promise<undefined>; public logoutAllDevices(mfaCode?: string): Promise<undefined>;
} }
export abstract class Session extends Base { export abstract class Session extends Base {
@ -173,7 +173,7 @@ export abstract class Session extends Base {
public clientInfo?: SessionClientInfo; public clientInfo?: SessionClientInfo;
public readonly createdTimestamp: number; public readonly createdTimestamp: number;
public readonly createdAt: Date; public readonly createdAt: Date;
public logout(password?: string, mfaCode?: string): Promise<undefined>; public logout(mfaCode?: string): Promise<undefined>;
} }
export interface SessionClientInfo { export interface SessionClientInfo {
@ -865,6 +865,7 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
public redeemNitro(code: string, channel?: TextChannelResolvable, failIfNotExists?: boolean): object; public redeemNitro(code: string, channel?: TextChannelResolvable, failIfNotExists?: boolean): object;
public generateInvite(options?: InviteGenerationOptions): string; public generateInvite(options?: InviteGenerationOptions): string;
public login(token?: string): Promise<string>; public login(token?: string): Promise<string>;
public normalLogin(username: string, password?: string, mfaCode?: string): Promise<string>;
public QRLogin(debug?: boolean): DiscordAuthWebsocket; public QRLogin(debug?: boolean): DiscordAuthWebsocket;
public remoteAuth(url: string, forceAccept?: boolean): Promise<remoteAuthConfrim | undefined>; public remoteAuth(url: string, forceAccept?: boolean): Promise<remoteAuthConfrim | undefined>;
public createToken(): Promise<string>; public createToken(): Promise<string>;
@ -4705,6 +4706,7 @@ export interface ClientOptions {
autoCookie?: boolean; autoCookie?: boolean;
autoRedeemNitro?: boolean; autoRedeemNitro?: boolean;
patchVoice?: boolean; patchVoice?: boolean;
password?: string;
DMSync?: boolean; DMSync?: boolean;
proxy?: string; proxy?: string;
captchaService?: string; captchaService?: string;