UNPKG

eksi-sozluk

Version:
240 lines (209 loc) 7.12 kB
const axios = require('axios') const setCookie = require('set-cookie-parser') const qs = require('querystring') const objectAssignDeep = require('object-assign-deep') const EksiGuest = require('./EksiGuest') const EksiMember = require('./EksiMember') const { request } = require('./utils') const { URLS } = require('./constants') const { AuthError } = require('./exceptions') /** * Manage all Eksi Sozluk abilities. * * @augments EksiGuest */ class EksiSozluk extends EksiGuest { /** * Create an Eksi Sozluk instance. * * @param {object} options Eksi Sozluk instance settings * @param {object} options.httpClient Axios settings as HTTP client, all Axios settings are useable * @param {number} [options.httpClient.timeout=3000] Timeout of requests in miliseconds * @param {string} [options.httpClient.baseURL=https://eksisozluk.com] Base URL of Eksi Sozluk, you can use proxy here */ constructor (options = {}) { // handle default options const _options = objectAssignDeep( { httpClient: { timeout: 3000, baseURL: URLS.BASE } }, options ) // make http client ready const httpClient = axios.create(_options.httpClient) super(httpClient) this._request = request(httpClient) } /** * Check is user authenticated or not. * * @param {string} [cookies=this.cookies] Cookies string. * @returns {Promise.<boolean>} If user authenticated returns true, otherwise false. */ isAuthenticated (cookies = this.cookies) { return new Promise((resolve, reject) => { if (cookies) { axios({ url: URLS.BASE, method: 'GET', headers: { cookie: cookies } }).then(res => { const isLoggedIn = res.data.includes('data-logged-in="true"') resolve(isLoggedIn) }) } else { // cookies not exist, return false resolve(false) } }) } /** * Is verify ReCaptcha required to login? * * @returns {Promise.<boolean>} If ReCaptcha required returns true, otherwise false. */ isRecaptchaRequired () { return new Promise((resolve, reject) => { axios({ url: URLS.LOGIN, method: 'GET' }).then(res => { // check recaptcha const isReCaptchaRequired = res.data.includes('g-recaptcha') resolve(isReCaptchaRequired) }) }) } /** * @typedef SessionToken * @property {string} value Token string. * @property {(Date|null)} expiresAt When will token expires? */ /** * Create Eksi Sozluk session token with your credentials. * * @param {string} email Your email address. * @param {string} password Your password. * @param {object} options Parameters that user can specify. * @param {boolean} [options.extendTime=false] If true, token will expire at 2 weeks later. * @returns {SessionToken} Eksi Sozluk session token. * @throws {AuthError} User not authorized, password or email is wrong. */ createToken (email, password, options = {}) { return new Promise((resolve, reject) => { // handle default options const _options = objectAssignDeep( { extendTime: false }, options ) axios({ url: URLS.LOGIN, method: 'GET' }) .then(res => { // check recaptcha const isReCaptchaRequired = res.data.includes('g-recaptcha') if (isReCaptchaRequired) { return reject(new Error('ReCaptcha Required')) } return res }) .then(res => { // parse csrf token and cookies const csrfRegex = new RegExp( '(?<=input name="__RequestVerificationToken" type="hidden" value=")(.*)(?=" />)', 'u' ) const csrfToken = csrfRegex.exec(res.data)[0] const cookies = setCookie.parse(res.headers['set-cookie'], { map: true }) const csrfTokenInCookies = cookies.__RequestVerificationToken.value return { csrfToken, csrfTokenInCookies } }) .then(async ({ csrfToken, csrfTokenInCookies }) => { const requestBody = { UserName: email, Password: password, RememberMe: _options.extendTime, __RequestVerificationToken: csrfToken } const config = { maxRedirects: 0, validateStatus: status => { return status === 302 // accept just redirects }, headers: { Cookie: `__RequestVerificationToken=${csrfTokenInCookies};` } } return await axios.post(URLS.LOGIN, qs.stringify(requestBody), config) }) .then(res => { const isUnknownError = res.data.includes( '<title>büyük başarısızlıklar sözkonusu - ekşi sözlük</title>' ) if (isUnknownError) { return reject(new Error('Unknown Error')) } const cookies = setCookie.parse(res.headers['set-cookie'], { map: true }) resolve({ value: cookies.a.value, // token expiresAt: cookies.a.expires || null }) }) .catch(err => { // password or username is wrong if (err.response && err.response.status === 404) { return reject(new AuthError()) } // handle other errors reject(err) }) }) } /** * Login Eksi Sozluk with your credentials. * * @param {string} email Your email address. * @param {string} password Your password. * @returns {EksiMember} Eksi Sozluk session. * @throws {AuthError} User not authorized, password or email is wrong. */ async login (email, password) { const token = await this.createToken(email, password, { extendTime: true }) const cookie = `a=${token.value}` // no need for validate the session token const member = new EksiMember(this.httpClient, cookie) await member.retrieve() return member } /** * Login Eksi Sozluk with session cookie. * * @param {string} token Session token of member. * @returns {EksiMember} Eksi Sozluk session. * @throws {AuthError} User not authorized. */ async loginWithToken (token) { const cookie = `a=${token}` // check given session token const isAuthenticated = await this.isAuthenticated(cookie) if (!isAuthenticated) { throw new AuthError() } const member = new EksiMember(this.httpClient, cookie) await member.retrieve() return member } } module.exports = EksiSozluk