From e88c808986a6dd8288671a55a7ed0f75dd2c8bf9 Mon Sep 17 00:00:00 2001 From: MillenniumEarl Date: Wed, 3 Mar 2021 10:57:50 +0100 Subject: [PATCH] Implement class --- src/scripts/classes/mapping/user-profile.ts | 156 ++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/scripts/classes/mapping/user-profile.ts diff --git a/src/scripts/classes/mapping/user-profile.ts b/src/scripts/classes/mapping/user-profile.ts new file mode 100644 index 0000000..008b421 --- /dev/null +++ b/src/scripts/classes/mapping/user-profile.ts @@ -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; + +/** + * 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 { + // 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 { + // Local variables + const responsePromiseList: Promise[] = []; + + // 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 + +}