From 6ad4cbed0ae8f43522ecc5eba3e2de87d588eb55 Mon Sep 17 00:00:00 2001 From: Roei Elisha Date: Thu, 24 May 2018 02:22:06 +0300 Subject: [PATCH] make login work --- package-lock.json | 59 +++++++++-- package.json | 4 + src/my_request.ts | 247 +++++++++++++++++++++------------------------- tsconfig.json | 5 +- tslint.json | 4 +- 5 files changed, 177 insertions(+), 142 deletions(-) diff --git a/package-lock.json b/package-lock.json index a98ff95..8cd9e70 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,11 +4,15 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/bluebird": { + "version": "3.5.20", + "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.20.tgz", + "integrity": "sha512-Wk41MVdF+cHBfVXj/ufUHJeO3BlIQr1McbHZANErMykaCWeDSZbH5erGjNBw2/3UlRdSxZbLfSuQTzFmPOYFsA==" + }, "@types/caseless": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.1.tgz", - "integrity": "sha512-FhlMa34NHp9K5MY1Uz8yb+ZvuX0pnvn3jScRSNAb75KHGB8d3rEU6hqMs3Z2vjuytcMfRg6c5CHMc3wtYyD2/A==", - "dev": true + "integrity": "sha512-FhlMa34NHp9K5MY1Uz8yb+ZvuX0pnvn3jScRSNAb75KHGB8d3rEU6hqMs3Z2vjuytcMfRg6c5CHMc3wtYyD2/A==" }, "@types/cheerio": { "version": "0.22.7", @@ -20,7 +24,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz", "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", - "dev": true, "requires": { "@types/node": "8.5.2" } @@ -37,14 +40,12 @@ "@types/node": { "version": "8.5.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-8.5.2.tgz", - "integrity": "sha512-KA4GKOpgXnrqEH2eCVhiv2CsxgXGQJgV1X0vsGlh+WCnxbeAE1GT44ZsTU1IN5dEeV/gDupKa7gWo08V5IxWVQ==", - "dev": true + "integrity": "sha512-KA4GKOpgXnrqEH2eCVhiv2CsxgXGQJgV1X0vsGlh+WCnxbeAE1GT44ZsTU1IN5dEeV/gDupKa7gWo08V5IxWVQ==" }, "@types/request": { "version": "2.47.0", "resolved": "https://registry.npmjs.org/@types/request/-/request-2.47.0.tgz", "integrity": "sha512-/KXM5oev+nNCLIgBjkwbk8VqxmzI56woD4VUxn95O+YeQ8hJzcSmIZ1IN3WexiqBb6srzDo2bdMbsXxgXNkz5Q==", - "dev": true, "requires": { "@types/caseless": "0.12.1", "@types/form-data": "2.2.1", @@ -52,11 +53,19 @@ "@types/tough-cookie": "2.3.2" } }, + "@types/request-promise": { + "version": "4.1.41", + "resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.41.tgz", + "integrity": "sha512-qlx6COxSTdSFHY9oX9v2zL1I05hgz5lwqYiXa2SFL2nDxAiG5KK8rnllLBH7k6OqzS3Ck0bWbxlGVdrZhS6oNw==", + "requires": { + "@types/bluebird": "3.5.20", + "@types/request": "2.47.0" + } + }, "@types/tough-cookie": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.2.tgz", - "integrity": "sha512-vOVmaruQG5EatOU/jM6yU2uCp3Lz6mK1P5Ztu4iJjfM4SVHU9XYktPUQtKlIXuahqXHdEyUarMrBEwg5Cwu+bA==", - "dev": true + "integrity": "sha512-vOVmaruQG5EatOU/jM6yU2uCp3Lz6mK1P5Ztu4iJjfM4SVHU9XYktPUQtKlIXuahqXHdEyUarMrBEwg5Cwu+bA==" }, "@types/xml2js": { "version": "0.4.2", @@ -178,6 +187,11 @@ "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.27.tgz", "integrity": "sha512-NzUKMYW4SWme+H5K+mfEmBxEF/V04PhlzoxxXwSnDig78y2t7HLBVotfDBMUhRPRA3WWID3GmJB/OJSWPhVXtg==" }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -709,6 +723,11 @@ "verror": "1.10.0" } }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, "lodash.assignin": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", @@ -919,6 +938,25 @@ "uuid": "3.2.1" } }, + "request-promise": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz", + "integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=", + "requires": { + "bluebird": "3.5.1", + "request-promise-core": "1.1.1", + "stealthy-require": "1.1.1", + "tough-cookie": "2.3.4" + } + }, + "request-promise-core": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", + "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", + "requires": { + "lodash": "4.17.10" + } + }, "resolve": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", @@ -979,6 +1017,11 @@ "integrity": "sha1-kQ9dKu17Ugxud3SZwfMuE5/eyxA=", "dev": true }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, "string_decoder": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", diff --git a/package.json b/package.json index 8d5d531..44186b0 100644 --- a/package.json +++ b/package.json @@ -22,15 +22,19 @@ }, "dependencies": { "big-integer": "^1.6.27", + "bluebird": "^3.5.1", "cheerio": "^0.22.0", "cloudscraper": "^1.5.0", "commander": "^2.15.1", "fs-extra": "^5.0.0", "mkdirp": "^0.5.0", "request": "^2.85.0", + "request-promise": "^4.2.2", "xml2js": "^0.4.5" }, "devDependencies": { + "@types/bluebird": "^3.5.20", + "@types/request-promise": "^4.1.41", "@types/cheerio": "^0.22.7", "@types/mkdirp": "^0.5.2", "@types/request": "^2.47.0", diff --git a/src/my_request.ts b/src/my_request.ts index c93842d..86fff6c 100644 --- a/src/my_request.ts +++ b/src/my_request.ts @@ -1,39 +1,84 @@ 'use strict'; -import request = require('request'); import cheerio = require('cheerio'); +import request = require('request'); +import rp = require('request-promise'); +import Promise = require('bluebird'); import log = require('./log'); +import { RequestPromise } from 'request-promise'; +import { Response } from 'request'; + +// tslint:disable-next-line:no-var-requires const cloudscraper = require('cloudscraper'); let isAuthenticated = false; let isPremium = false; -const defaultHeaders: request.Headers = -{ +const defaultHeaders: request.Headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64; x64; rv:58.0) Gecko/20100101 Firefox/58.0', - // Mozilla/5.0 (Windows NT 6.2; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0', 'Connection': 'keep-alive', - 'Referer': 'https://www.crunchyroll.com/login' + 'Referer': 'https://www.crunchyroll.com/login', }; +function generateDeviceId(): string { + let id = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + for (let i = 0; i < 32; i++) { + id += possible.charAt(Math.floor(Math.random() * possible.length)); + } + + return id; +} + +function startSession(): Promise { + return rp({ + method: 'GET', + url: 'CR_SESSION_URL', + qs: { + device_id: generateDeviceId(), + device_type: 'CR_DEVICE_TYPE', + access_token: 'CR_SESSION_KEY', + version: 'CR_API_VERSION', + locale: 'CR_LOCALE', + }, + json: true, + }) + .then((response: any) => { + return response.data.session_id; + }); +} + +function login(sessionId: string, user: string, pass: string): Promise { + return rp({ + method: 'POST', + url: 'CR_LOGIN_URL', + form: { + account: user, + password: pass, + session_id: sessionId, + version: 'CR_API_VERSION', + }, + json: true, + }) + .then((response) => { + if (response.error) throw new Error('Login failed: ' + response.message); + return response.data; + }); +} + +// TODO: logout + /** * Performs a GET request for the resource. */ -export function get(config: IConfig, options: string|request.Options, done: (err: Error, result?: string) => void) -{ - authenticate(config, err => - { - if (err) - { - return done(err); +export function get(config: IConfig, options: string|request.Options, done: (err: Error, result?: string) => void) { + authenticate(config, (err) => { + if (err) { + return done(err); } - cloudscraper.request(modify(options, 'GET'), (err: Error, response: any, body: any) => - { - if (err) - { - return done(err); - } - + cloudscraper.request(modify(options, 'GET'), (error: Error, response: any, body: any) => { + if (error) return done(error); done(null, typeof body === 'string' ? body : String(body)); }); }); @@ -42,22 +87,14 @@ export function get(config: IConfig, options: string|request.Options, done: (err /** * Performs a POST request for the resource. */ -export function post(config: IConfig, options: request.Options, done: (err: Error, result?: string) => void) -{ - authenticate(config, err => - { - if (err) - { +export function post(config: IConfig, options: request.Options, done: (err: Error, result?: string) => void) { + authenticate(config, (err) => { + if (err) { return done(err); } - cloudscraper.request(modify(options, 'POST'), (err: Error, response: any, body: any) => - { - if (err) - { - return done(err); - } - + cloudscraper.request(modify(options, 'POST'), (error: Error, response: any, body: any) => { + if (error) return done(error); done(null, typeof body === 'string' ? body : String(body)); }); }); @@ -66,114 +103,61 @@ export function post(config: IConfig, options: request.Options, done: (err: Erro /** * Authenticates using the configured pass and user. */ -function authenticate(config: IConfig, done: (err: Error) => void) -{ - if (isAuthenticated || !config.pass || !config.user) - { +function authenticate(config: IConfig, done: (err: Error) => void) { + if (isAuthenticated || !config.pass || !config.user) { return done(null); } - /* Bypass the login page and send a login request directly */ - let options = - { - headers: defaultHeaders, - jar: true, - gzip: false, - method: 'GET', - url: 'https://www.crunchyroll.com/login' - }; - - cloudscraper.request(options, (err: Error, rep: string, body: string) => - { - if (err) return done(err); - - const $ = cheerio.load(body); - - /* Get the token from the login page */ - const token = $('input[name="login_form[_token]"]').attr('value'); - if (token === '') - { - return done(new Error('Can`t find token!')); - } - - let options = - { + startSession() + .then((sessionId: string) => { + defaultHeaders.Cookie = `sess_id=${sessionId}; c_locale=enUS`; + return login(sessionId, config.user, config.pass); + }) + .then((userData) => { + /** + * The page return with a meta based redirection, as we wan't to check that everything is fine, reload + * the main page. A bit convoluted, but more sure. + */ + const options = { headers: defaultHeaders, - form: - { - 'login_form[name]': config.user, - 'login_form[password]': config.pass, - 'login_form[redirect_url]': '/', - 'login_form[_token]': token - }, jar: true, - gzip: false, - method: 'POST', - url: 'https://www.crunchyroll.com/login' + url: 'http://www.crunchyroll.com/', + method: 'GET', }; - cloudscraper.request(options, (err: Error, rep: string, body: string) => - { - if (err) - { - return done(err); + cloudscraper.request(options, (err: Error, rep: string, body: string) => { + if (err) return done(err); + + const $ = cheerio.load(body); + + /* Check if auth worked */ + const regexps = /ga\('set', 'dimension[5-8]', '([^']*)'\);/g; + const dims = regexps.exec($('script').text()); + + for (let i = 1; i < 5; i++) { + if ((dims[i] !== undefined) && (dims[i] !== '') && (dims[i] !== 'not-registered')) { + isAuthenticated = true; + } + + if ((dims[i] === 'premium') || (dims[i] === 'premiumplus')) { + isPremium = true; + } } - /* The page return with a meta based redirection, as we wan't to check that everything is fine, reload - * the main page. A bit convoluted, but more sure. - */ - let options = - { - headers: defaultHeaders, - jar: true, - url: 'http://www.crunchyroll.com/', - method: 'GET' - }; + if (isAuthenticated === false) { + const error = $('ul.message, li.error').text(); + return done(new Error('Authentication failed: ' + error)); + } - cloudscraper.request(options, (err: Error, rep: string, body: string) => - { - if (err) - { - return done(err); - } - - let $ = cheerio.load(body); - - /* Check if auth worked */ - const regexps = /ga\('set', 'dimension[5-8]', '([^']*)'\);/g; - const dims = regexps.exec($('script').text()); - - for (let i = 1; i < 5; i++) - { - if ((dims[i] !== undefined) && (dims[i] !== '') && (dims[i] !== 'not-registered')) - { - isAuthenticated = true; - } - - if ((dims[i] === 'premium') || (dims[i] === 'premiumplus')) - { - isPremium = true; - } - } - - if (isAuthenticated === false) - { - const error = $('ul.message, li.error').text(); - return done(new Error('Authentication failed: ' + error)); - } - - if (isPremium === false) - { - log.warn('Do not use this app without a premium account.'); - } - else - { - log.info('You have a premium account! Good!'); - } - done(null); - }); + if (isPremium === false) { + log.warn('Do not use this app without a premium account.'); + } else { + log.info('You have a premium account! Good!'); + } + done(null); }); - }); + }) + .catch(done); } /** @@ -181,12 +165,11 @@ function authenticate(config: IConfig, done: (err: Error) => void) */ function modify(options: string|request.Options, reqMethod: string): request.Options { - if (typeof options !== 'string') - { + if (typeof options !== 'string') { options.jar = true; options.headers = defaultHeaders; options.method = reqMethod; return options; } return { jar: true, headers: defaultHeaders, url: options.toString(), method: reqMethod }; -} +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index d308e0a..cf8734c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,9 @@ "removeComments": false, "module": "commonjs", "outDir": "dist", - "sourceMap": true + "sourceMap": true, + "lib": [ + "es2015" + ] } } diff --git a/tslint.json b/tslint.json index f479faf..bb0d2b6 100644 --- a/tslint.json +++ b/tslint.json @@ -64,6 +64,8 @@ "check-operator", "check-separator", "check-type" - ] + ], + "object-literal-sort-keys": false, + "ordered-imports": false } } \ No newline at end of file