Adapt to new Result class
parent
20e3d863ed
commit
138a112e96
|
@ -53,13 +53,15 @@ export default async function fetchPlatformData(): Promise<void> {
|
||||||
const html = await fetchHTML(f95url.F95_LATEST_UPDATES);
|
const html = await fetchHTML(f95url.F95_LATEST_UPDATES);
|
||||||
|
|
||||||
// Parse data
|
// Parse data
|
||||||
const data = parseLatestPlatformHTML(html);
|
if (html.isSuccess()) {
|
||||||
|
const data = parseLatestPlatformHTML(html.value);
|
||||||
|
|
||||||
// Assign data
|
// Assign data
|
||||||
assignLatestPlatformData(data);
|
assignLatestPlatformData(data);
|
||||||
|
|
||||||
// Cache data
|
// Cache data
|
||||||
saveCache(shared.cachePath);
|
saveCache(shared.cachePath);
|
||||||
|
} else throw html.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//#endregion Public methods
|
//#endregion Public methods
|
||||||
|
|
|
@ -42,18 +42,21 @@ async function fetchResultURLs(url: string, limit: number = 30): Promise<string[
|
||||||
|
|
||||||
// Fetch HTML and prepare Cheerio
|
// Fetch HTML and prepare Cheerio
|
||||||
const html = await fetchHTML(url);
|
const html = await fetchHTML(url);
|
||||||
const $ = cheerio.load(html);
|
|
||||||
|
|
||||||
// Here we get all the DIV that are the body of the various query results
|
if (html.isSuccess()) {
|
||||||
const results = $("body").find(f95Selector.GS_RESULT_BODY);
|
const $ = cheerio.load(html.value);
|
||||||
|
|
||||||
// Than we extract the URLs
|
// Here we get all the DIV that are the body of the various query results
|
||||||
const urls = results.slice(0, limit).map((idx, el) => {
|
const results = $("body").find(f95Selector.GS_RESULT_BODY);
|
||||||
const elementSelector = $(el);
|
|
||||||
return extractLinkFromResult(elementSelector);
|
|
||||||
}).get();
|
|
||||||
|
|
||||||
return urls;
|
// Than we extract the URLs
|
||||||
|
const urls = results.slice(0, limit).map((idx, el) => {
|
||||||
|
const elementSelector = $(el);
|
||||||
|
return extractLinkFromResult(elementSelector);
|
||||||
|
}).get();
|
||||||
|
|
||||||
|
return urls;
|
||||||
|
} else throw html.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -12,6 +12,8 @@ import { urls as f95url } from "./constants/url.js";
|
||||||
import { selectors as f95selector } from "./constants/css-selector.js";
|
import { selectors as f95selector } from "./constants/css-selector.js";
|
||||||
import LoginResult from "./classes/login-result.js";
|
import LoginResult from "./classes/login-result.js";
|
||||||
import credentials from "./classes/credentials.js";
|
import credentials from "./classes/credentials.js";
|
||||||
|
import { failure, Result, success } from "./classes/result.js";
|
||||||
|
import { GenericAxiosError, InvalidF95Token, UnexpectedResponseContentType } from "./classes/errors.js";
|
||||||
|
|
||||||
// Global variables
|
// Global variables
|
||||||
const userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) " +
|
const userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) " +
|
||||||
|
@ -31,26 +33,23 @@ const commonConfig = {
|
||||||
/**
|
/**
|
||||||
* Gets the HTML code of a page.
|
* Gets the HTML code of a page.
|
||||||
*/
|
*/
|
||||||
export async function fetchHTML(url: string): Promise<string|null> {
|
export async function fetchHTML(url: string): Promise<Result<GenericAxiosError | UnexpectedResponseContentType, string>> {
|
||||||
// Local variables
|
|
||||||
let returnValue = null;
|
|
||||||
|
|
||||||
// Fetch the response of the platform
|
// Fetch the response of the platform
|
||||||
const response = await fetchGETResponse(url);
|
const response = await fetchGETResponse(url);
|
||||||
|
|
||||||
// Manage response
|
if (response.isSuccess()) {
|
||||||
/* istambul ignore next */
|
const isHTML = response.value["content-type"].includes("text/html");
|
||||||
if (!response) {
|
|
||||||
shared.logger.warn(`Unable to fetch HTML for ${url}`);
|
|
||||||
}
|
|
||||||
/* istambul ignore next */
|
|
||||||
else if (!response.headers["content-type"].includes("text/html")) {
|
|
||||||
// The response is not a HTML page
|
|
||||||
shared.logger.warn(`The ${url} returned a ${response.headers["content-type"]} response`);
|
|
||||||
}
|
|
||||||
|
|
||||||
returnValue = response.data;
|
const unexpectedResponseError = new UnexpectedResponseContentType({
|
||||||
return returnValue;
|
id: 2,
|
||||||
|
message: `Expected HTML but received ${response.value["content-type"]}`,
|
||||||
|
error: null
|
||||||
|
});
|
||||||
|
|
||||||
|
return isHTML ?
|
||||||
|
success(response.value.data as string) :
|
||||||
|
failure(unexpectedResponseError);
|
||||||
|
} else return failure(response.value as GenericAxiosError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,7 +62,7 @@ export async function fetchHTML(url: string): Promise<string|null> {
|
||||||
*/
|
*/
|
||||||
export async function authenticate(credentials: credentials, force: boolean = false): Promise<LoginResult> {
|
export async function authenticate(credentials: credentials, force: boolean = false): Promise<LoginResult> {
|
||||||
shared.logger.info(`Authenticating with user ${credentials.username}`);
|
shared.logger.info(`Authenticating with user ${credentials.username}`);
|
||||||
if (!credentials.token) throw new Error(`Invalid token for auth: ${credentials.token}`);
|
if (!credentials.token) throw new InvalidF95Token(`Invalid token for auth: ${credentials.token}`);
|
||||||
|
|
||||||
// Secure the URL
|
// Secure the URL
|
||||||
const secureURL = enforceHttpsUrl(f95url.F95_LOGIN_URL);
|
const secureURL = enforceHttpsUrl(f95url.F95_LOGIN_URL);
|
||||||
|
@ -104,43 +103,43 @@ export async function authenticate(credentials: credentials, force: boolean = fa
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain the token used to authenticate the user to the platform.
|
* Obtain the token used to authenticate the user to the platform.
|
||||||
* @returns {Promise<String>} Token or `null` if an error arise
|
|
||||||
*/
|
*/
|
||||||
export async function getF95Token(): Promise<string|null> {
|
export async function getF95Token() {
|
||||||
// Fetch the response of the platform
|
// Fetch the response of the platform
|
||||||
const response = await fetchGETResponse(f95url.F95_LOGIN_URL);
|
const response = await fetchGETResponse(f95url.F95_LOGIN_URL);
|
||||||
/* istambul ignore next */
|
|
||||||
if (!response) {
|
|
||||||
shared.logger.warn("Unable to get the token for the session");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The response is a HTML page, we need to find the <input> with name "_xfToken"
|
if (response.isSuccess()) {
|
||||||
const $ = cheerio.load(response.data as string);
|
// The response is a HTML page, we need to find the <input> with name "_xfToken"
|
||||||
return $("body").find(f95selector.GET_REQUEST_TOKEN).attr("value");
|
const $ = cheerio.load(response.value.data as string);
|
||||||
|
return $("body").find(f95selector.GET_REQUEST_TOKEN).attr("value");
|
||||||
|
} else throw response.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//#region Utility methods
|
//#region Utility methods
|
||||||
/**
|
/**
|
||||||
* Performs a GET request to a specific URL and returns the response.
|
* Performs a GET request to a specific URL and returns the response.
|
||||||
* If the request generates an error (for example 400) `null` is returned.
|
|
||||||
*/
|
*/
|
||||||
export async function fetchGETResponse(url: string): Promise<AxiosResponse<unknown>> {
|
export async function fetchGETResponse(url: string): Promise<Result<GenericAxiosError, AxiosResponse<any>>>{
|
||||||
// Secure the URL
|
// Secure the URL
|
||||||
const secureURL = enforceHttpsUrl(url);
|
const secureURL = enforceHttpsUrl(url);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fetch and return the response
|
// Fetch and return the response
|
||||||
return await axios.get(secureURL, commonConfig);
|
const response = await axios.get(secureURL, commonConfig);
|
||||||
|
return success(response);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
shared.logger.error(`Error ${e.message} occurred while trying to fetch ${secureURL}`);
|
shared.logger.error(`Error ${e.message} occurred while trying to fetch ${secureURL}`);
|
||||||
return null;
|
const genericError = new GenericAxiosError({
|
||||||
|
id: 1,
|
||||||
|
message:`Error ${e.message} occurred while trying to fetch ${secureURL}`,
|
||||||
|
error: e
|
||||||
|
});
|
||||||
|
return failure(genericError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enforces the scheme of the URL is https and returns the new URL.
|
* Enforces the scheme of the URL is https and returns the new URL.
|
||||||
* @param {String} url
|
|
||||||
* @returns {String} Secure URL or `null` if the argument is not a string
|
* @returns {String} Secure URL or `null` if the argument is not a string
|
||||||
*/
|
*/
|
||||||
export function enforceHttpsUrl(url: string): string {
|
export function enforceHttpsUrl(url: string): string {
|
||||||
|
@ -149,12 +148,9 @@ export function enforceHttpsUrl(url: string): string {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the url belongs to the domain of the F95 platform.
|
* Check if the url belongs to the domain of the F95 platform.
|
||||||
* @param {String} url URL to check
|
|
||||||
* @returns {Boolean} true if the url belongs to the domain, false otherwise
|
|
||||||
*/
|
*/
|
||||||
export function isF95URL(url: string): boolean {
|
export function isF95URL(url: string): boolean {
|
||||||
if (url.toString().startsWith(f95url.F95_BASE_URL)) return true;
|
return url.toString().startsWith(f95url.F95_BASE_URL);
|
||||||
else return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -167,8 +163,7 @@ export function isStringAValidURL(url: string): boolean {
|
||||||
// Many thanks to Daveo at StackOverflow (https://preview.tinyurl.com/y2f2e2pc)
|
// Many thanks to Daveo at StackOverflow (https://preview.tinyurl.com/y2f2e2pc)
|
||||||
const expression = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
|
const expression = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/;
|
||||||
const regex = new RegExp(expression);
|
const regex = new RegExp(expression);
|
||||||
if (url.match(regex)) return true;
|
return url.match(regex).length > 0;
|
||||||
else return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -24,25 +24,26 @@ export async function getPostInformation<T extends IBasic>(url: string): Promise
|
||||||
|
|
||||||
// Fetch HTML and prepare Cheerio
|
// Fetch HTML and prepare Cheerio
|
||||||
const html = await fetchHTML(url);
|
const html = await fetchHTML(url);
|
||||||
if (!html) return null;
|
|
||||||
|
|
||||||
const $ = cheerio.load(html);
|
if (html.isSuccess()) {
|
||||||
const body = $("body");
|
const $ = cheerio.load(html.value);
|
||||||
const mainPost = $(f95Selector.GS_POSTS).first();
|
const body = $("body");
|
||||||
|
const mainPost = $(f95Selector.GS_POSTS).first();
|
||||||
|
|
||||||
// Extract data
|
// Extract data
|
||||||
const postData = parseCheerioMainPost($, mainPost);
|
const postData = parseCheerioMainPost($, mainPost);
|
||||||
const TJsonLD = getJSONLD(body);
|
const TJsonLD = getJSONLD(body);
|
||||||
|
|
||||||
// Fill in the HandiWork element with the information obtained
|
// Fill in the HandiWork element with the information obtained
|
||||||
const hw: HandiWork = {} as HandiWork;
|
const hw: HandiWork = {} as HandiWork;
|
||||||
fillWithJSONLD(hw, TJsonLD);
|
fillWithJSONLD(hw, TJsonLD);
|
||||||
fillWithPostData(hw, postData);
|
fillWithPostData(hw, postData);
|
||||||
fillWithPrefixes(hw, body);
|
fillWithPrefixes(hw, body);
|
||||||
hw.tags = extractTags(body);
|
hw.tags = extractTags(body);
|
||||||
|
|
||||||
shared.logger.info(`Founded data for ${hw.name}`);
|
shared.logger.info(`Founded data for ${hw.name}`);
|
||||||
return <T><unknown>hw;
|
return <T><unknown>hw;
|
||||||
|
} else throw html.value;
|
||||||
};
|
};
|
||||||
//#endregion Public methods
|
//#endregion Public methods
|
||||||
|
|
||||||
|
|
|
@ -36,20 +36,22 @@ async function fetchUsernameAndAvatar(): Promise<{ [s: string]: string; }> {
|
||||||
// Fetch page
|
// Fetch page
|
||||||
const html = await fetchHTML(f95url.F95_BASE_URL);
|
const html = await fetchHTML(f95url.F95_BASE_URL);
|
||||||
|
|
||||||
// Load HTML response
|
if (html.isSuccess()) {
|
||||||
const $ = cheerio.load(html);
|
// Load HTML response
|
||||||
const body = $("body");
|
const $ = cheerio.load(html.value);
|
||||||
|
const body = $("body");
|
||||||
|
|
||||||
// Fetch username
|
// Fetch username
|
||||||
const username = body.find(f95Selector.UD_USERNAME_ELEMENT).first().text().trim();
|
const username = body.find(f95Selector.UD_USERNAME_ELEMENT).first().text().trim();
|
||||||
|
|
||||||
// Fetch user avatar image source
|
// Fetch user avatar image source
|
||||||
const source = body.find(f95Selector.UD_AVATAR_PIC).first().attr("src");
|
const source = body.find(f95Selector.UD_AVATAR_PIC).first().attr("src");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
username,
|
username,
|
||||||
source
|
source
|
||||||
};
|
};
|
||||||
|
} else throw html.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,16 +75,18 @@ async function fetchWatchedGameThreadURLs(): Promise<string[]> {
|
||||||
// Fetch page
|
// Fetch page
|
||||||
const html = await fetchHTML(currentURL);
|
const html = await fetchHTML(currentURL);
|
||||||
|
|
||||||
// Load HTML response
|
if (html.isSuccess()) {
|
||||||
const $ = cheerio.load(html);
|
// Load HTML response
|
||||||
const body = $("body");
|
const $ = cheerio.load(html.value);
|
||||||
|
const body = $("body");
|
||||||
|
|
||||||
// Find the URLs
|
// Find the URLs
|
||||||
const urls = fetchPageURLs(body);
|
const urls = fetchPageURLs(body);
|
||||||
watchedGameThreadURLs.push(...urls);
|
watchedGameThreadURLs.push(...urls);
|
||||||
|
|
||||||
// Find the next page (if any)
|
// Find the next page (if any)
|
||||||
currentURL = fetchNextPageURL(body);
|
currentURL = fetchNextPageURL(body);
|
||||||
|
} else throw html.value;
|
||||||
}
|
}
|
||||||
while (currentURL);
|
while (currentURL);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue