Implement class
parent
bd967d5a8c
commit
e88c808986
|
@ -0,0 +1,156 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Public modules from npm
|
||||||
|
import cheerio from "cheerio";
|
||||||
|
|
||||||
|
// Modules from files
|
||||||
|
import Post from "./post.js";
|
||||||
|
import PlatformUser from "./platform-user.js";
|
||||||
|
import { urls } from "../../constants/url.js";
|
||||||
|
import { WATCHED_THREAD } from "../../constants/css-selector.js";
|
||||||
|
import { fetchHTML } from "../../network-helper.js";
|
||||||
|
import { GenericAxiosError, UnexpectedResponseContentType } from "../errors.js";
|
||||||
|
import { Result } from "../result.js";
|
||||||
|
|
||||||
|
// Interfaces
|
||||||
|
interface IWatchedThread {
|
||||||
|
/**
|
||||||
|
* URL of the thread
|
||||||
|
*/
|
||||||
|
url: string;
|
||||||
|
/**
|
||||||
|
* Indicates whether the thread has any unread posts.
|
||||||
|
*/
|
||||||
|
unread: boolean,
|
||||||
|
/**
|
||||||
|
* Specifies the forum to which the thread belongs.
|
||||||
|
*/
|
||||||
|
forum: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Types
|
||||||
|
type TFetchResult = Result<GenericAxiosError | UnexpectedResponseContentType, string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class containing the data of the user currently connected to the F95Zone platform.
|
||||||
|
*/
|
||||||
|
export default class UserProfile extends PlatformUser {
|
||||||
|
|
||||||
|
//#region Fields
|
||||||
|
|
||||||
|
private _watched: IWatchedThread[] = [];
|
||||||
|
private _bookmarks: Post[] = [];
|
||||||
|
private _alerts: string[] = [];
|
||||||
|
private _conversations: string[];
|
||||||
|
|
||||||
|
//#endregion Fields
|
||||||
|
|
||||||
|
//#region Getters
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of followed thread data.
|
||||||
|
*/
|
||||||
|
public get watched() { return this._watched; }
|
||||||
|
/**
|
||||||
|
* List of bookmarked posts.
|
||||||
|
* @todo
|
||||||
|
*/
|
||||||
|
public get bookmarks() { return this._bookmarks; }
|
||||||
|
/**
|
||||||
|
* List of alerts.
|
||||||
|
* @todo
|
||||||
|
*/
|
||||||
|
public get alerts() { return this._alerts; }
|
||||||
|
/**
|
||||||
|
* List of conversations.
|
||||||
|
* @todo
|
||||||
|
*/
|
||||||
|
public get conversation() { return this._conversations; }
|
||||||
|
|
||||||
|
//#endregion Getters
|
||||||
|
|
||||||
|
//#region Public methods
|
||||||
|
|
||||||
|
public async fetch() {
|
||||||
|
// First fetch the basic data
|
||||||
|
await super.fetch();
|
||||||
|
|
||||||
|
// Now fetch the watched threads
|
||||||
|
this._watched = await this.fetchWatchedThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion Public methods
|
||||||
|
|
||||||
|
//#region Private methods
|
||||||
|
|
||||||
|
private async fetchWatchedThread(): Promise<IWatchedThread[]> {
|
||||||
|
// Prepare and fetch URL
|
||||||
|
const url = new URL(urls.F95_WATCHED_THREADS);
|
||||||
|
url.searchParams.set("unread", "0");
|
||||||
|
|
||||||
|
const htmlResponse = await fetchHTML(url.toString());
|
||||||
|
|
||||||
|
if (htmlResponse.isSuccess()) {
|
||||||
|
// Load page in cheerio
|
||||||
|
const $ = cheerio.load(htmlResponse.value);
|
||||||
|
|
||||||
|
// Fetch the pages
|
||||||
|
const lastPage = parseInt($(WATCHED_THREAD.LAST_PAGE).text().trim());
|
||||||
|
const pages = await this.fetchPages(url, lastPage);
|
||||||
|
|
||||||
|
const watchedThreads = pages.map((r, idx) => {
|
||||||
|
const elements = r.applyOnSuccess(this.fetchPageThreadElements);
|
||||||
|
if (elements.isSuccess()) return elements.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return [].concat(...watchedThreads);
|
||||||
|
} else throw htmlResponse.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the pages containing the thread data.
|
||||||
|
* @param url Base URL to use for scraping a page
|
||||||
|
* @param n Total number of pages
|
||||||
|
* @param s Page to start from
|
||||||
|
*/
|
||||||
|
private async fetchPages(url: URL, n: number, s: number = 1): Promise<TFetchResult[]> {
|
||||||
|
// Local variables
|
||||||
|
const responsePromiseList: Promise<TFetchResult>[] = [];
|
||||||
|
|
||||||
|
// Fetch the page' HTML
|
||||||
|
for (let page = s; page <= n; page++) {
|
||||||
|
// Set the page URL
|
||||||
|
url.searchParams.set("page", page.toString());
|
||||||
|
|
||||||
|
// Fetch HTML but not wait for it
|
||||||
|
const promise = fetchHTML(url.toString())
|
||||||
|
responsePromiseList.push(promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the promises to resolve
|
||||||
|
return await Promise.all(responsePromiseList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets thread data starting from the source code of the page passed by parameter.
|
||||||
|
*/
|
||||||
|
private fetchPageThreadElements(html: string): IWatchedThread[] {
|
||||||
|
// Local variables
|
||||||
|
const $ = cheerio.load(html);
|
||||||
|
|
||||||
|
return $(WATCHED_THREAD.BODIES).map((idx, el) => {
|
||||||
|
// Parse the URL
|
||||||
|
const partialURL = $(el).find(WATCHED_THREAD.URL).attr("href");
|
||||||
|
const url = new URL(partialURL.replace("unread", ""), `${urls.F95_BASE_URL}`).toString();
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: url.toString(),
|
||||||
|
unread: partialURL.endsWith("unread"),
|
||||||
|
forum: $(el).find(WATCHED_THREAD.FORUM).text().trim()
|
||||||
|
} as IWatchedThread;
|
||||||
|
}).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
//#endregion Private methods
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue