RenaiApp/src/main/modules/nhentai/nhentai-api.ts

125 lines
3.5 KiB
TypeScript

import { inject, injectable } from 'inversify';
import { JSDOM } from 'jsdom';
import { RequestInit, Response } from 'node-fetch';
import { Errors, RenaiError } from '../../core/error';
import { IWebCrawler } from '../web-crawler/i-web-crawler';
import { INhentaiApi } from './i-nhentai-api';
const domain = 'nhentai.net';
const url = `https://${domain}/`;
const paths = {
books: 'g/',
login: 'login/',
favorites: 'favorites/',
};
const usernameInput = 'username_or_email';
const passwordInput = 'password';
interface ILoginMeta {
[key: string]: string;
}
interface ILoginAuth {
[usernameInput]: string;
[passwordInput]: string;
}
interface ILoginParams extends ILoginMeta, ILoginAuth {}
@injectable()
export class NhentaiApi implements INhentaiApi {
private webCrawler: IWebCrawler;
public constructor(@inject(Symbol.for('web-crawler')) webCrawler: IWebCrawler) {
this.webCrawler = webCrawler;
}
public isLoggedIn(): Promise<boolean> {
return this.webCrawler
.fetch(`${url}${paths.favorites}`, { redirect: 'manual' })
.then((res: Response) => res.status === HttpCode.OK);
}
public login(name: string, password: string): Promise<void> {
return this.getLoginMeta()
.then((meta: ILoginMeta) => {
const loginParams: ILoginParams = {
...meta,
...{
[usernameInput]: name,
[passwordInput]: password,
},
};
return this.postNHentai(paths.login, {
body: encodeURI(
Object.keys(loginParams)
.map((key: keyof ILoginParams) => `${key}=${loginParams[key]}`)
.join('&')
),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'manual',
});
})
.then(() => {})
.catch(() => Promise.reject(new RenaiError(Errors.ELOGINFAIL)));
}
private getNHentai(path: string): Promise<Document> {
return this.webCrawler
.fetch(`${url}${path}`)
.then((res: Response) => res.text())
.then((text: string) => {
const { document } = new JSDOM(text).window;
return document;
});
}
private postNHentai(path: string, requestInit: RequestInit = {}): Promise<Response> {
const postUrl = `${url}${path}`;
return this.webCrawler.fetch(postUrl, {
...requestInit,
...{
headers: {
...requestInit.headers,
...{
Host: domain,
Referer: postUrl,
},
},
},
method: 'post',
});
}
private getLoginMeta(): Promise<ILoginMeta> {
return this.getNHentai(paths.login).then((document: Document) => {
// eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let i = 0; i < document.forms.length; i++) {
const form: HTMLFormElement = document.forms[i];
const valueStore: ILoginMeta = {};
let isLoginForm = false;
// eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let j = 0; j < form.elements.length; j++) {
const input = form.elements[j];
const name = input.getAttribute('name');
if (name === usernameInput || name === passwordInput) {
isLoginForm = true;
} else if (name) {
valueStore[name] = input.getAttribute('value');
}
}
if (isLoginForm) {
return valueStore;
}
}
return Promise.reject(new RenaiError(Errors.ENOLOGIN));
});
}
}