[CodeFactor] Apply fixes
parent
ed77fa31f5
commit
8d9d1e11e4
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
to use this example, create an .env file
|
to use this example, create an .env file
|
||||||
in the project root with the following values:
|
in the project root with the following values:
|
||||||
|
|
||||||
F95_USERNAME = YOUR_USERNAME
|
F95_USERNAME = YOUR_USERNAME
|
||||||
|
@ -12,12 +12,12 @@ F95_PASSWORD = YOUR_PASSWORD
|
||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
// Modules from file
|
// Modules from file
|
||||||
import { login,
|
import { login,
|
||||||
getUserData,
|
getUserData,
|
||||||
getLatestUpdates,
|
getLatestUpdates,
|
||||||
LatestSearchQuery,
|
LatestSearchQuery,
|
||||||
Game,
|
Game,
|
||||||
searchHandiwork,
|
searchHandiwork,
|
||||||
HandiworkSearchQuery
|
HandiworkSearchQuery
|
||||||
} from "./index.js";
|
} from "./index.js";
|
||||||
|
|
||||||
|
|
24
src/index.ts
24
src/index.ts
|
@ -59,7 +59,7 @@ export function isLogged(): boolean { return shared.isLogged; };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log in to the F95Zone platform.
|
* Log in to the F95Zone platform.
|
||||||
*
|
*
|
||||||
* This **must** be the first operation performed before accessing any other script functions.
|
* This **must** be the first operation performed before accessing any other script functions.
|
||||||
*/
|
*/
|
||||||
export async function login(username: string, password: string): Promise<LoginResult> {
|
export async function login(username: string, password: string): Promise<LoginResult> {
|
||||||
|
@ -102,7 +102,7 @@ export async function login(username: string, password: string): Promise<LoginRe
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chek if exists a new version of the handiwork.
|
* Chek if exists a new version of the handiwork.
|
||||||
*
|
*
|
||||||
* You **must** be logged in to the portal before calling this method.
|
* You **must** be logged in to the portal before calling this method.
|
||||||
*/
|
*/
|
||||||
export async function checkIfHandiworkHasUpdate(hw: HandiWork): Promise<boolean> {
|
export async function checkIfHandiworkHasUpdate(hw: HandiWork): Promise<boolean> {
|
||||||
|
@ -127,9 +127,9 @@ export async function checkIfHandiworkHasUpdate(hw: HandiWork): Promise<boolean>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search for one or more handiworks identified by a specific query.
|
* Search for one or more handiworks identified by a specific query.
|
||||||
*
|
*
|
||||||
* You **must** be logged in to the portal before calling this method.
|
* You **must** be logged in to the portal before calling this method.
|
||||||
*
|
*
|
||||||
* @param {HandiworkSearchQuery} query Parameters used for the search.
|
* @param {HandiworkSearchQuery} query Parameters used for the search.
|
||||||
* @param {Number} limit Maximum number of results. Default: 10
|
* @param {Number} limit Maximum number of results. Default: 10
|
||||||
*/
|
*/
|
||||||
|
@ -137,12 +137,12 @@ export async function searchHandiwork<T extends IBasic>(query: HandiworkSearchQu
|
||||||
// Check if the user is logged
|
// Check if the user is logged
|
||||||
if (!shared.isLogged) throw new UserNotLogged(USER_NOT_LOGGED);
|
if (!shared.isLogged) throw new UserNotLogged(USER_NOT_LOGGED);
|
||||||
|
|
||||||
return await search<T>(query, limit);
|
return search<T>(query, limit);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given the url, it gets all the information about the handiwork requested.
|
* Given the url, it gets all the information about the handiwork requested.
|
||||||
*
|
*
|
||||||
* You **must** be logged in to the portal before calling this method.
|
* You **must** be logged in to the portal before calling this method.
|
||||||
*/
|
*/
|
||||||
export async function getHandiworkFromURL<T extends IBasic>(url: string): Promise<T> {
|
export async function getHandiworkFromURL<T extends IBasic>(url: string): Promise<T> {
|
||||||
|
@ -155,14 +155,14 @@ export async function getHandiworkFromURL<T extends IBasic>(url: string): Promis
|
||||||
if (!isF95URL(url)) throw new Error(`${url} is not a valid F95Zone URL`);
|
if (!isF95URL(url)) throw new Error(`${url} is not a valid F95Zone URL`);
|
||||||
|
|
||||||
// Get game data
|
// Get game data
|
||||||
return await getHandiworkInformation<T>(url);
|
return getHandiworkInformation<T>(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the data of the currently logged in user.
|
* Gets the data of the currently logged in user.
|
||||||
*
|
*
|
||||||
* You **must** be logged in to the portal before calling this method.
|
* You **must** be logged in to the portal before calling this method.
|
||||||
*
|
*
|
||||||
* @returns {Promise<UserProfile>} Data of the user currently logged in
|
* @returns {Promise<UserProfile>} Data of the user currently logged in
|
||||||
*/
|
*/
|
||||||
export async function getUserData(): Promise<UserProfile> {
|
export async function getUserData(): Promise<UserProfile> {
|
||||||
|
@ -178,9 +178,9 @@ export async function getUserData(): Promise<UserProfile> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the latest updated games that match the specified parameters.
|
* Gets the latest updated games that match the specified parameters.
|
||||||
*
|
*
|
||||||
* You **must** be logged in to the portal before calling this method.
|
* You **must** be logged in to the portal before calling this method.
|
||||||
*
|
*
|
||||||
* @param {LatestSearchQuery} query Parameters used for the search.
|
* @param {LatestSearchQuery} query Parameters used for the search.
|
||||||
* @param {Number} limit Maximum number of results. Default: 10
|
* @param {Number} limit Maximum number of results. Default: 10
|
||||||
*/
|
*/
|
||||||
|
@ -196,7 +196,7 @@ export async function getLatestUpdates<T extends IBasic>(query: LatestSearchQuer
|
||||||
|
|
||||||
// Get the data from urls
|
// Get the data from urls
|
||||||
const promiseList = urls.map((u: string) => getHandiworkInformation<T>(u));
|
const promiseList = urls.map((u: string) => getHandiworkInformation<T>(u));
|
||||||
return await Promise.all(promiseList);
|
return Promise.all(promiseList);
|
||||||
};
|
};
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
|
@ -45,7 +45,7 @@ export default class Thread {
|
||||||
public get id() { return this._id; }
|
public get id() { return this._id; }
|
||||||
/**
|
/**
|
||||||
* URL of the thread.
|
* URL of the thread.
|
||||||
*
|
*
|
||||||
* It may vary depending on any versions of the contained product.
|
* It may vary depending on any versions of the contained product.
|
||||||
*/
|
*/
|
||||||
public get url() { return this._url; }
|
public get url() { return this._url; }
|
||||||
|
@ -85,8 +85,8 @@ export default class Thread {
|
||||||
//#endregion Getters
|
//#endregion Getters
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes an object for mapping a thread.
|
* Initializes an object for mapping a thread.
|
||||||
*
|
*
|
||||||
* The unique ID of the thread must be specified.
|
* The unique ID of the thread must be specified.
|
||||||
*/
|
*/
|
||||||
constructor(id: number) { this._id = id; }
|
constructor(id: number) { this._id = id; }
|
||||||
|
@ -167,7 +167,7 @@ export default class Thread {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It processes the rating of the thread
|
* It processes the rating of the thread
|
||||||
* starting from the data contained in the JSON+LD tag.
|
* starting from the data contained in the JSON+LD tag.
|
||||||
*/
|
*/
|
||||||
private parseRating(data: TJsonLD): TRating {
|
private parseRating(data: TJsonLD): TRating {
|
||||||
|
@ -241,7 +241,7 @@ export default class Thread {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the post in the `index` position with respect to the posts in the thread.
|
* Gets the post in the `index` position with respect to the posts in the thread.
|
||||||
*
|
*
|
||||||
* `index` must be greater or equal to 1.
|
* `index` must be greater or equal to 1.
|
||||||
* If the post is not found, `null` is returned.
|
* If the post is not found, `null` is returned.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -149,7 +149,7 @@ export default class UserProfile extends PlatformUser {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the promises to resolve
|
// Wait for the promises to resolve
|
||||||
return await Promise.all(responsePromiseList);
|
return Promise.all(responsePromiseList);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default class PrefixParser {
|
||||||
//#region Private methods
|
//#region Private methods
|
||||||
/**
|
/**
|
||||||
* Gets the key associated with a given value from a dictionary.
|
* Gets the key associated with a given value from a dictionary.
|
||||||
* @param {Object} object Dictionary to search
|
* @param {Object} object Dictionary to search
|
||||||
* @param {Any} value Value associated with the key
|
* @param {Any} value Value associated with the key
|
||||||
* @returns {String|undefined} Key found or undefined
|
* @returns {String|undefined} Key found or undefined
|
||||||
*/
|
*/
|
||||||
|
@ -43,7 +43,7 @@ export default class PrefixParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search within the platform prefixes for the
|
* Search within the platform prefixes for the
|
||||||
* desired element and return the dictionary that contains it.
|
* desired element and return the dictionary that contains it.
|
||||||
* @param element Element to search in the prefixes as a key or as a value
|
* @param element Element to search in the prefixes as a key or as a value
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -13,21 +13,21 @@ import ThreadSearchQuery, { TThreadOrder } from './thread-search-query.js';
|
||||||
|
|
||||||
// Type definitions
|
// Type definitions
|
||||||
/**
|
/**
|
||||||
* Method of sorting results. Try to unify the two types of
|
* Method of sorting results. Try to unify the two types of
|
||||||
* sorts in the "Latest" section and in the "Thread search"
|
* sorts in the "Latest" section and in the "Thread search"
|
||||||
* section. Being dynamic research, if a sorting type is not
|
* section. Being dynamic research, if a sorting type is not
|
||||||
* available, the replacement sort is chosen.
|
* available, the replacement sort is chosen.
|
||||||
*
|
*
|
||||||
* `date`: Order based on the latest update
|
* `date`: Order based on the latest update
|
||||||
*
|
*
|
||||||
* `likes`: Order based on the number of likes received. Replacement: `replies`.
|
* `likes`: Order based on the number of likes received. Replacement: `replies`.
|
||||||
*
|
*
|
||||||
* `relevance`: Order based on the relevance of the result (or rating).
|
* `relevance`: Order based on the relevance of the result (or rating).
|
||||||
*
|
*
|
||||||
* `replies`: Order based on the number of answers to the thread. Replacement: `views`.
|
* `replies`: Order based on the number of answers to the thread. Replacement: `views`.
|
||||||
*
|
*
|
||||||
* `title`: Order based on the growing alphabetical order of the titles.
|
* `title`: Order based on the growing alphabetical order of the titles.
|
||||||
*
|
*
|
||||||
* `views`: Order based on the number of visits. Replacement: `replies`.
|
* `views`: Order based on the number of visits. Replacement: `replies`.
|
||||||
*/
|
*/
|
||||||
type THandiworkOrder = "date" | "likes" | "relevance" | "replies" | "title" | "views";
|
type THandiworkOrder = "date" | "likes" | "relevance" | "replies" | "title" | "views";
|
||||||
|
@ -80,8 +80,8 @@ export default class HandiworkSearchQuery implements IQuery {
|
||||||
//#region Public methods
|
//#region Public methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select what kind of search should be
|
* Select what kind of search should be
|
||||||
* performed based on the properties of
|
* performed based on the properties of
|
||||||
* the query.
|
* the query.
|
||||||
*/
|
*/
|
||||||
public selectSearchType(): "latest" | "thread" {
|
public selectSearchType(): "latest" | "thread" {
|
||||||
|
@ -89,8 +89,8 @@ export default class HandiworkSearchQuery implements IQuery {
|
||||||
const MAX_TAGS_LATEST_SEARCH = 5;
|
const MAX_TAGS_LATEST_SEARCH = 5;
|
||||||
const DEFAULT_SEARCH_TYPE = "latest";
|
const DEFAULT_SEARCH_TYPE = "latest";
|
||||||
|
|
||||||
// If the keywords are set or the number
|
// If the keywords are set or the number
|
||||||
// of included tags is greather than 5,
|
// of included tags is greather than 5,
|
||||||
// we must perform a thread search
|
// we must perform a thread search
|
||||||
if (this.keywords || this.includedTags.length > MAX_TAGS_LATEST_SEARCH) return "thread";
|
if (this.keywords || this.includedTags.length > MAX_TAGS_LATEST_SEARCH) return "thread";
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ export default class HandiworkSearchQuery implements IQuery {
|
||||||
private castToThread(): ThreadSearchQuery {
|
private castToThread(): ThreadSearchQuery {
|
||||||
// Cast the basic query object and copy common values
|
// Cast the basic query object and copy common values
|
||||||
const query: ThreadSearchQuery = new ThreadSearchQuery;
|
const query: ThreadSearchQuery = new ThreadSearchQuery;
|
||||||
Object.keys(this).forEach(key => {
|
Object.keys(this).forEach(key => {
|
||||||
if (query.hasOwnProperty(key)) {
|
if (query.hasOwnProperty(key)) {
|
||||||
query[key] = this[key];
|
query[key] = this[key];
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,15 +29,15 @@ export default class LatestSearchQuery implements IQuery {
|
||||||
|
|
||||||
public category: TCategory = 'games';
|
public category: TCategory = 'games';
|
||||||
/**
|
/**
|
||||||
* Ordering type.
|
* Ordering type.
|
||||||
*
|
*
|
||||||
* Default: `date`.
|
* Default: `date`.
|
||||||
*/
|
*/
|
||||||
public order: TLatestOrder = 'date';
|
public order: TLatestOrder = 'date';
|
||||||
/**
|
/**
|
||||||
* Date limit in days, to be understood as "less than".
|
* Date limit in days, to be understood as "less than".
|
||||||
* Use `1` to indicate "today" or `null` to indicate "anytime".
|
* Use `1` to indicate "today" or `null` to indicate "anytime".
|
||||||
*
|
*
|
||||||
* Default: `null`
|
* Default: `null`
|
||||||
*/
|
*/
|
||||||
public date: TDate = null;
|
public date: TDate = null;
|
||||||
|
@ -74,7 +74,7 @@ export default class LatestSearchQuery implements IQuery {
|
||||||
const decoded = decodeURIComponent(url.toString());
|
const decoded = decodeURIComponent(url.toString());
|
||||||
|
|
||||||
// Fetch the result
|
// Fetch the result
|
||||||
return await fetchGETResponse(decoded);
|
return fetchGETResponse(decoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
public findNearestDate(d: Date): TDate {
|
public findNearestDate(d: Date): TDate {
|
||||||
|
@ -124,7 +124,7 @@ export default class LatestSearchQuery implements IQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private dateDiffInDays(a: Date, b: Date) {
|
private dateDiffInDays(a: Date, b: Date) {
|
||||||
const MS_PER_DAY = 1000 * 60 * 60 * 24;
|
const MS_PER_DAY = 1000 * 60 * 60 * 24;
|
||||||
|
|
|
@ -82,7 +82,7 @@ export default class ThreadSearchQuery implements IQuery {
|
||||||
const params = this.preparePOSTParameters();
|
const params = this.preparePOSTParameters();
|
||||||
|
|
||||||
// Return the POST response
|
// Return the POST response
|
||||||
return await fetchPOSTResponse(urls.F95_SEARCH_URL, params);
|
return fetchPOSTResponse(urls.F95_SEARCH_URL, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion Public methods
|
//#endregion Public methods
|
||||||
|
|
|
@ -25,7 +25,7 @@ export const selectors = {
|
||||||
|
|
||||||
export const GENERIC = {
|
export const GENERIC = {
|
||||||
/**
|
/**
|
||||||
* The ID of the user currently logged into
|
* The ID of the user currently logged into
|
||||||
* the platform in the attribute `data-user-id`.
|
* the platform in the attribute `data-user-id`.
|
||||||
*/
|
*/
|
||||||
CURRENT_USER_ID: "span.avatar[data-user-id]",
|
CURRENT_USER_ID: "span.avatar[data-user-id]",
|
||||||
|
@ -41,17 +41,17 @@ export const WATCHED_THREAD = {
|
||||||
*/
|
*/
|
||||||
BODIES: "div.structItem-cell--main",
|
BODIES: "div.structItem-cell--main",
|
||||||
/**
|
/**
|
||||||
* Link element containing the partial URL
|
* Link element containing the partial URL
|
||||||
* of the thread in the `href` attribute.
|
* of the thread in the `href` attribute.
|
||||||
*
|
*
|
||||||
* It may be followed by the `/unread` segment.
|
* It may be followed by the `/unread` segment.
|
||||||
*
|
*
|
||||||
* For use within a `WATCHED_THREAD.BODIES` selector.
|
* For use within a `WATCHED_THREAD.BODIES` selector.
|
||||||
*/
|
*/
|
||||||
URL: "div > a[data-tp-primary]",
|
URL: "div > a[data-tp-primary]",
|
||||||
/**
|
/**
|
||||||
* Name of the forum to which the thread belongs as text.
|
* Name of the forum to which the thread belongs as text.
|
||||||
*
|
*
|
||||||
* For use within a `WATCHED_THREAD.BODIES` selector.
|
* For use within a `WATCHED_THREAD.BODIES` selector.
|
||||||
*/
|
*/
|
||||||
FORUM: "div.structItem-cell--main > div.structItem-minor > ul.structItem-parts > li:last-of-type > a",
|
FORUM: "div.structItem-cell--main > div.structItem-minor > ul.structItem-parts > li:last-of-type > a",
|
||||||
|
@ -64,19 +64,19 @@ export const WATCHED_THREAD = {
|
||||||
export const THREAD = {
|
export const THREAD = {
|
||||||
/**
|
/**
|
||||||
* Number of pages in the thread (as text of the element).
|
* Number of pages in the thread (as text of the element).
|
||||||
*
|
*
|
||||||
* Two identical elements are identified.
|
* Two identical elements are identified.
|
||||||
*/
|
*/
|
||||||
LAST_PAGE: "ul.pageNav-main > li:last-child > a",
|
LAST_PAGE: "ul.pageNav-main > li:last-child > a",
|
||||||
/**
|
/**
|
||||||
* Identify the creator of the thread.
|
* Identify the creator of the thread.
|
||||||
*
|
*
|
||||||
* The ID is contained in the `data-user-id` attribute.
|
* The ID is contained in the `data-user-id` attribute.
|
||||||
*/
|
*/
|
||||||
OWNER_ID: "div.uix_headerInner > * a.username[data-user-id]",
|
OWNER_ID: "div.uix_headerInner > * a.username[data-user-id]",
|
||||||
/**
|
/**
|
||||||
* Contains the creation date of the thread.
|
* Contains the creation date of the thread.
|
||||||
*
|
*
|
||||||
* The date is contained in the `datetime` attribute as an ISO string.
|
* The date is contained in the `datetime` attribute as an ISO string.
|
||||||
*/
|
*/
|
||||||
CREATION: "div.uix_headerInner > * time",
|
CREATION: "div.uix_headerInner > * time",
|
||||||
|
@ -94,7 +94,7 @@ export const THREAD = {
|
||||||
TITLE: "h1.p-title-value",
|
TITLE: "h1.p-title-value",
|
||||||
/**
|
/**
|
||||||
* JSON containing thread information.
|
* JSON containing thread information.
|
||||||
*
|
*
|
||||||
* Two different elements are found.
|
* Two different elements are found.
|
||||||
*/
|
*/
|
||||||
JSONLD: "script[type=\"application/ld+json\"]",
|
JSONLD: "script[type=\"application/ld+json\"]",
|
||||||
|
@ -106,44 +106,44 @@ export const THREAD = {
|
||||||
|
|
||||||
export const POST = {
|
export const POST = {
|
||||||
/**
|
/**
|
||||||
* Unique post number for the current thread.
|
* Unique post number for the current thread.
|
||||||
*
|
*
|
||||||
* For use within a `THREAD.POSTS_IN_PAGE` selector.
|
* For use within a `THREAD.POSTS_IN_PAGE` selector.
|
||||||
*/
|
*/
|
||||||
NUMBER: "* ul.message-attribution-opposite > li > a:not([id])[rel=\"nofollow\"]",
|
NUMBER: "* ul.message-attribution-opposite > li > a:not([id])[rel=\"nofollow\"]",
|
||||||
/**
|
/**
|
||||||
* Unique ID of the post in the F95Zone platform in the `id` attribute.
|
* Unique ID of the post in the F95Zone platform in the `id` attribute.
|
||||||
*
|
*
|
||||||
* For use within a `THREAD.POSTS_IN_PAGE` selector.
|
* For use within a `THREAD.POSTS_IN_PAGE` selector.
|
||||||
*/
|
*/
|
||||||
ID: "span[id^=\"post\"]",
|
ID: "span[id^=\"post\"]",
|
||||||
/**
|
/**
|
||||||
* Unique ID of the post author in the `data-user-id` attribute.
|
* Unique ID of the post author in the `data-user-id` attribute.
|
||||||
*
|
*
|
||||||
* For use within a `THREAD.POSTS_IN_PAGE` selector.
|
* For use within a `THREAD.POSTS_IN_PAGE` selector.
|
||||||
*/
|
*/
|
||||||
OWNER_ID: "* div.message-cell--user > * a[data-user-id]",
|
OWNER_ID: "* div.message-cell--user > * a[data-user-id]",
|
||||||
/**
|
/**
|
||||||
* Main body of the post where the message written by the user is contained.
|
* Main body of the post where the message written by the user is contained.
|
||||||
*
|
*
|
||||||
* For use within a `THREAD.POSTS_IN_PAGE` selector.
|
* For use within a `THREAD.POSTS_IN_PAGE` selector.
|
||||||
*/
|
*/
|
||||||
BODY: "* article.message-body > div.bbWrapper",
|
BODY: "* article.message-body > div.bbWrapper",
|
||||||
/**
|
/**
|
||||||
* Publication date of the post contained in the `datetime` attribute as an ISO date.
|
* Publication date of the post contained in the `datetime` attribute as an ISO date.
|
||||||
*
|
*
|
||||||
* For use within a `THREAD.POSTS_IN_PAGE` selector.
|
* For use within a `THREAD.POSTS_IN_PAGE` selector.
|
||||||
*/
|
*/
|
||||||
PUBLISH_DATE: "* div.message-attribution-main > a > time",
|
PUBLISH_DATE: "* div.message-attribution-main > a > time",
|
||||||
/**
|
/**
|
||||||
* Last modified date of the post contained in the `datetime` attribute as the ISO date.
|
* Last modified date of the post contained in the `datetime` attribute as the ISO date.
|
||||||
*
|
*
|
||||||
* For use within a `THREAD.POSTS_IN_PAGE` selector.
|
* For use within a `THREAD.POSTS_IN_PAGE` selector.
|
||||||
*/
|
*/
|
||||||
LAST_EDIT: "* div.message-lastEdit > time",
|
LAST_EDIT: "* div.message-lastEdit > time",
|
||||||
/**
|
/**
|
||||||
* Gets the element only if the post has been bookmarked.
|
* Gets the element only if the post has been bookmarked.
|
||||||
*
|
*
|
||||||
* For use within a `THREAD.POSTS_IN_PAGE` selector.
|
* For use within a `THREAD.POSTS_IN_PAGE` selector.
|
||||||
*/
|
*/
|
||||||
BOOKMARKED: "* ul.message-attribution-opposite >li > a[title=\"Bookmark\"].is-bookmarked",
|
BOOKMARKED: "* ul.message-attribution-opposite >li > a[title=\"Bookmark\"].is-bookmarked",
|
||||||
|
@ -152,37 +152,37 @@ export const POST = {
|
||||||
export const MEMBER = {
|
export const MEMBER = {
|
||||||
/**
|
/**
|
||||||
* Name of the user.
|
* Name of the user.
|
||||||
*
|
*
|
||||||
* It also contains the unique ID of the user in the `data-user-id` attribute.
|
* It also contains the unique ID of the user in the `data-user-id` attribute.
|
||||||
*/
|
*/
|
||||||
NAME: "span[class^=\"username\"]",
|
NAME: "span[class^=\"username\"]",
|
||||||
/**
|
/**
|
||||||
* Title of the user in the platform.
|
* Title of the user in the platform.
|
||||||
*
|
*
|
||||||
* i.e.: Member
|
* i.e.: Member
|
||||||
*/
|
*/
|
||||||
TITLE: "span.userTitle",
|
TITLE: "span.userTitle",
|
||||||
/**
|
/**
|
||||||
* Avatar used by the user.
|
* Avatar used by the user.
|
||||||
*
|
*
|
||||||
* Source in the attribute `src`.
|
* Source in the attribute `src`.
|
||||||
*/
|
*/
|
||||||
AVATAR: "span.avatarWrapper > a.avatar > img",
|
AVATAR: "span.avatarWrapper > a.avatar > img",
|
||||||
/**
|
/**
|
||||||
* User assigned banners.
|
* User assigned banners.
|
||||||
*
|
*
|
||||||
* The last element is always empty and can be ignored.
|
* The last element is always empty and can be ignored.
|
||||||
*/
|
*/
|
||||||
BANNERS: "em.userBanner > strong",
|
BANNERS: "em.userBanner > strong",
|
||||||
/**
|
/**
|
||||||
* Date the user joined the platform.
|
* Date the user joined the platform.
|
||||||
*
|
*
|
||||||
* The date is contained in the `datetime` attribute as an ISO string.
|
* The date is contained in the `datetime` attribute as an ISO string.
|
||||||
*/
|
*/
|
||||||
JOINED: "div.uix_memberHeader__extra > div.memberHeader-blurb:nth-child(1) > * time",
|
JOINED: "div.uix_memberHeader__extra > div.memberHeader-blurb:nth-child(1) > * time",
|
||||||
/**
|
/**
|
||||||
* Last time the user connected to the platform.
|
* Last time the user connected to the platform.
|
||||||
*
|
*
|
||||||
* The date is contained in the `datetime` attribute as an ISO string.
|
* The date is contained in the `datetime` attribute as an ISO string.
|
||||||
*/
|
*/
|
||||||
LAST_SEEN: "div.uix_memberHeader__extra > div.memberHeader-blurb:nth-child(2) > * time",
|
LAST_SEEN: "div.uix_memberHeader__extra > div.memberHeader-blurb:nth-child(2) > * time",
|
||||||
|
@ -193,14 +193,14 @@ export const MEMBER = {
|
||||||
AMOUNT_DONATED: "div.pairJustifier > dl:nth-child(5) > dd",
|
AMOUNT_DONATED: "div.pairJustifier > dl:nth-child(5) > dd",
|
||||||
/**
|
/**
|
||||||
* Button used to follow/unfollow the user.
|
* Button used to follow/unfollow the user.
|
||||||
*
|
*
|
||||||
* If the text is `Unfollow` then the user is followed.
|
* If the text is `Unfollow` then the user is followed.
|
||||||
* If the text is `Follow` then the user is not followed.
|
* If the text is `Follow` then the user is not followed.
|
||||||
*/
|
*/
|
||||||
FOLLOWED: "div.memberHeader-buttons > div.buttonGroup:first-child > a[data-sk-follow] > span",
|
FOLLOWED: "div.memberHeader-buttons > div.buttonGroup:first-child > a[data-sk-follow] > span",
|
||||||
/**
|
/**
|
||||||
* Button used to ignore/unignore the user.
|
* Button used to ignore/unignore the user.
|
||||||
*
|
*
|
||||||
* If the text is `Unignore` then the user is ignored.
|
* If the text is `Unignore` then the user is ignored.
|
||||||
* If the text is `Ignore` then the user is not ignored.
|
* If the text is `Ignore` then the user is not ignored.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -8,8 +8,8 @@ export const urls = {
|
||||||
F95_LATEST_PHP: "https://f95zone.to/new_latest.php",
|
F95_LATEST_PHP: "https://f95zone.to/new_latest.php",
|
||||||
F95_BOOKMARKS: "https://f95zone.to/account/bookmarks",
|
F95_BOOKMARKS: "https://f95zone.to/account/bookmarks",
|
||||||
/**
|
/**
|
||||||
* Add the unique ID of the post to
|
* Add the unique ID of the post to
|
||||||
* get the thread page where the post
|
* get the thread page where the post
|
||||||
* is present.
|
* is present.
|
||||||
*/
|
*/
|
||||||
F95_POSTS: "https://f95zone.to/posts/",
|
F95_POSTS: "https://f95zone.to/posts/",
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { urls as f95url } from "../constants/url.js";
|
||||||
* You *must* be logged.
|
* You *must* be logged.
|
||||||
* @param {LatestSearchQuery} query
|
* @param {LatestSearchQuery} query
|
||||||
* Query used for the search
|
* Query used for the search
|
||||||
* @param {Number} limit
|
* @param {Number} limit
|
||||||
* Maximum number of items to get. Default: 30
|
* Maximum number of items to get. Default: 30
|
||||||
* @returns {Promise<String[]>} URLs of the handiworks
|
* @returns {Promise<String[]>} URLs of the handiworks
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -43,7 +43,7 @@ interface ILatestResource {
|
||||||
|
|
||||||
//#region Public methods
|
//#region Public methods
|
||||||
/**
|
/**
|
||||||
* Gets the basic data used for game data processing
|
* Gets the basic data used for game data processing
|
||||||
* (such as graphics engines and progress statuses)
|
* (such as graphics engines and progress statuses)
|
||||||
*/
|
*/
|
||||||
export default async function fetchPlatformData(): Promise<void> {
|
export default async function fetchPlatformData(): Promise<void> {
|
||||||
|
@ -106,7 +106,7 @@ function saveCache(path: string): void {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* Given the HTML code of the response from the F95Zone,
|
* Given the HTML code of the response from the F95Zone,
|
||||||
* parse it and return the result.
|
* parse it and return the result.
|
||||||
*/
|
*/
|
||||||
function parseLatestPlatformHTML(html: string): ILatestResource{
|
function parseLatestPlatformHTML(html: string): ILatestResource{
|
||||||
|
|
|
@ -36,6 +36,6 @@ export default async function executeQuery(query: any, limit: number = 30): Prom
|
||||||
"handiwork");
|
"handiwork");
|
||||||
|
|
||||||
// Fetch and return the urls
|
// Fetch and return the urls
|
||||||
return await searchMap[key](query, limit);
|
return searchMap[key](query, limit);
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
|
@ -25,7 +25,7 @@ export default async function fetchThreadHandiworkURLs(query: ThreadSearchQuery,
|
||||||
const response = await query.execute();
|
const response = await query.execute();
|
||||||
|
|
||||||
// Fetch the results from F95 and return the handiwork urls
|
// Fetch the results from F95 and return the handiwork urls
|
||||||
if (response.isSuccess()) return await fetchResultURLs(response.value.data as string, limit);
|
if (response.isSuccess()) return fetchResultURLs(response.value.data as string, limit);
|
||||||
else throw response.value
|
else throw response.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { failure, Result, success } from "./classes/result.js";
|
||||||
import { GenericAxiosError, InvalidF95Token, UnexpectedResponseContentType } from "./classes/errors.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) " +
|
||||||
"AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15";
|
"AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
axiosCookieJarSupport.default(axios);
|
axiosCookieJarSupport.default(axios);
|
||||||
|
@ -61,15 +61,15 @@ export async function fetchHTML(url: string): Promise<Result<GenericAxiosError |
|
||||||
error: null
|
error: null
|
||||||
});
|
});
|
||||||
|
|
||||||
return isHTML ?
|
return isHTML ?
|
||||||
success(response.value.data as string) :
|
success(response.value.data as string) :
|
||||||
failure(unexpectedResponseError);
|
failure(unexpectedResponseError);
|
||||||
} else return failure(response.value as GenericAxiosError);
|
} else return failure(response.value as GenericAxiosError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It authenticates to the platform using the credentials
|
* It authenticates to the platform using the credentials
|
||||||
* and token obtained previously. Save cookies on your
|
* and token obtained previously. Save cookies on your
|
||||||
* device after authentication.
|
* device after authentication.
|
||||||
* @param {module:./classes/credentials.ts:Credentials} credentials Platform access credentials
|
* @param {module:./classes/credentials.ts:Credentials} credentials Platform access credentials
|
||||||
* @param {Boolean} force Specifies whether the request should be forced, ignoring any saved cookies
|
* @param {Boolean} force Specifies whether the request should be forced, ignoring any saved cookies
|
||||||
|
@ -173,7 +173,7 @@ export function isF95URL(url: string): boolean {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the string passed by parameter has a
|
* Checks if the string passed by parameter has a
|
||||||
* properly formatted and valid path to a URL (HTTP/HTTPS).
|
* properly formatted and valid path to a URL (HTTP/HTTPS).
|
||||||
* @param {String} url String to check for correctness
|
* @param {String} url String to check for correctness
|
||||||
*/
|
*/
|
||||||
|
@ -187,7 +187,7 @@ export function isStringAValidURL(url: string): boolean {
|
||||||
/**
|
/**
|
||||||
* Check if a particular URL is valid and reachable on the web.
|
* Check if a particular URL is valid and reachable on the web.
|
||||||
* @param {string} url URL to check
|
* @param {string} url URL to check
|
||||||
* @param {boolean} [checkRedirect]
|
* @param {boolean} [checkRedirect]
|
||||||
* If true, the function will consider redirects a violation and return false.
|
* If true, the function will consider redirects a violation and return false.
|
||||||
* Default: false
|
* Default: false
|
||||||
* @returns {Promise<Boolean>} true if the URL exists, false otherwise
|
* @returns {Promise<Boolean>} true if the URL exists, false otherwise
|
||||||
|
|
|
@ -87,7 +87,7 @@ function toUpperCaseArray(a: string[]): string[] {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the string `s` is in the dict `a`.
|
* Check if the string `s` is in the dict `a`.
|
||||||
*
|
*
|
||||||
* Case insensitive.
|
* Case insensitive.
|
||||||
*/
|
*/
|
||||||
function stringInDict(s: string, a: TPrefixDict): boolean {
|
function stringInDict(s: string, a: TPrefixDict): boolean {
|
||||||
|
@ -99,7 +99,7 @@ function stringInDict(s: string, a: TPrefixDict): boolean {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a string to a boolean.
|
* Convert a string to a boolean.
|
||||||
*
|
*
|
||||||
* Check also for `yes`/`no` and `1`/`0`.
|
* Check also for `yes`/`no` and `1`/`0`.
|
||||||
*/
|
*/
|
||||||
function stringToBoolean(s: string): boolean {
|
function stringToBoolean(s: string): boolean {
|
||||||
|
@ -116,7 +116,7 @@ function stringToBoolean(s: string): boolean {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the element with the given name or `undefined`.
|
* Gets the element with the given name or `undefined`.
|
||||||
*
|
*
|
||||||
* Case-insensitive.
|
* Case-insensitive.
|
||||||
*/
|
*/
|
||||||
function getPostElementByName(elements: IPostElement[], name: string): IPostElement | undefined {
|
function getPostElementByName(elements: IPostElement[], name: string): IPostElement | undefined {
|
||||||
|
@ -127,7 +127,7 @@ function getPostElementByName(elements: IPostElement[], name: string): IPostElem
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the post prefixes.
|
* Parse the post prefixes.
|
||||||
*
|
*
|
||||||
* In particular, it elaborates the following prefixes for games:
|
* In particular, it elaborates the following prefixes for games:
|
||||||
* `Engine`, `Status`, `Mod`.
|
* `Engine`, `Status`, `Mod`.
|
||||||
*/
|
*/
|
||||||
|
@ -174,7 +174,7 @@ function fillWithPrefixes(hw: HandiWork, prefixes: string[]) {
|
||||||
/**
|
/**
|
||||||
* Compiles a HandiWork object with the data extracted
|
* Compiles a HandiWork object with the data extracted
|
||||||
* from the main post of the HandiWork page.
|
* from the main post of the HandiWork page.
|
||||||
*
|
*
|
||||||
* The values that will be added are:
|
* The values that will be added are:
|
||||||
* `Overview`, `OS`, `Language`, `Version`, `Installation`,
|
* `Overview`, `OS`, `Language`, `Version`, `Installation`,
|
||||||
* `Pages`, `Resolution`, `Lenght`, `Genre`, `Censored`,
|
* `Pages`, `Resolution`, `Lenght`, `Genre`, `Censored`,
|
||||||
|
|
|
@ -21,11 +21,11 @@ export interface ILink extends IPostElement {
|
||||||
* Given a post of a thread page it extracts the information contained in the body.
|
* Given a post of a thread page it extracts the information contained in the body.
|
||||||
*/
|
*/
|
||||||
export function parseF95ThreadPost($: cheerio.Root, post: cheerio.Cheerio): IPostElement[] {
|
export function parseF95ThreadPost($: cheerio.Root, post: cheerio.Cheerio): IPostElement[] {
|
||||||
// The data is divided between "tag" and "text" elements.
|
// The data is divided between "tag" and "text" elements.
|
||||||
// Simple data is composed of a "tag" element followed
|
// Simple data is composed of a "tag" element followed
|
||||||
// by a "text" element, while more complex data (contained
|
// by a "text" element, while more complex data (contained
|
||||||
// in spoilers) is composed of a "tag" element, followed
|
// in spoilers) is composed of a "tag" element, followed
|
||||||
// by a text containing only ":" and then by an additional
|
// by a text containing only ":" and then by an additional
|
||||||
// "tag" element having as the first term "Spoiler"
|
// "tag" element having as the first term "Spoiler"
|
||||||
|
|
||||||
// First fetch all the elements in the post
|
// First fetch all the elements in the post
|
||||||
|
@ -44,13 +44,13 @@ export function parseF95ThreadPost($: cheerio.Root, post: cheerio.Cheerio): IPos
|
||||||
//#region Private methods
|
//#region Private methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a spoiler element by getting its text broken
|
* Process a spoiler element by getting its text broken
|
||||||
* down by any other spoiler elements present.
|
* down by any other spoiler elements present.
|
||||||
*/
|
*/
|
||||||
function parseCheerioSpoilerNode($: cheerio.Root, spoiler: cheerio.Cheerio): IPostElement {
|
function parseCheerioSpoilerNode($: cheerio.Root, spoiler: cheerio.Cheerio): IPostElement {
|
||||||
// A spoiler block is composed of a div with class "bbCodeSpoiler",
|
// A spoiler block is composed of a div with class "bbCodeSpoiler",
|
||||||
// containing a div "bbCodeSpoiler-content" containing, in cascade,
|
// containing a div "bbCodeSpoiler-content" containing, in cascade,
|
||||||
// a div with class "bbCodeBlock--spoiler" and a div with class "bbCodeBlock-content".
|
// a div with class "bbCodeBlock--spoiler" and a div with class "bbCodeBlock-content".
|
||||||
// This last tag contains the required data.
|
// This last tag contains the required data.
|
||||||
|
|
||||||
// Local variables
|
// Local variables
|
||||||
|
@ -94,7 +94,7 @@ function parseCheerioSpoilerNode($: cheerio.Root, spoiler: cheerio.Cheerio): IPo
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the node passed as a parameter is of text type.
|
* Check if the node passed as a parameter is of text type.
|
||||||
* This also includes formatted nodes (i.e. `<b>`).
|
* This also includes formatted nodes (i.e. `<b>`).
|
||||||
*/
|
*/
|
||||||
function isTextNode(node: cheerio.Element): boolean {
|
function isTextNode(node: cheerio.Element): boolean {
|
||||||
|
@ -106,7 +106,7 @@ function isTextNode(node: cheerio.Element): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the text of the node only, excluding child nodes.
|
* Gets the text of the node only, excluding child nodes.
|
||||||
* Also includes formatted text elements (i.e. `<b>`).
|
* Also includes formatted text elements (i.e. `<b>`).
|
||||||
*/
|
*/
|
||||||
function getCheerioNonChildrenText(node: cheerio.Cheerio): string {
|
function getCheerioNonChildrenText(node: cheerio.Cheerio): string {
|
||||||
|
@ -120,7 +120,7 @@ function getCheerioNonChildrenText(node: cheerio.Cheerio): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process a node and see if it contains a
|
* Process a node and see if it contains a
|
||||||
* link or image. If not, it returns `null`.
|
* link or image. If not, it returns `null`.
|
||||||
*/
|
*/
|
||||||
function parseCheerioLinkNode(element: cheerio.Cheerio): ILink | null {
|
function parseCheerioLinkNode(element: cheerio.Cheerio): ILink | null {
|
||||||
|
@ -149,7 +149,7 @@ function parseCheerioLinkNode(element: cheerio.Cheerio): ILink | null {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collapse an `IPostElement` element with a single subnode
|
* Collapse an `IPostElement` element with a single subnode
|
||||||
* in the `Content` field in case it has no information.
|
* in the `Content` field in case it has no information.
|
||||||
*/
|
*/
|
||||||
function reducePostElement(element: IPostElement): IPostElement {
|
function reducePostElement(element: IPostElement): IPostElement {
|
||||||
|
@ -234,7 +234,7 @@ function parseCheerioNode($: cheerio.Root, node: cheerio.Element, reduce = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It simplifies the `IPostElement` elements by associating
|
* It simplifies the `IPostElement` elements by associating
|
||||||
* the corresponding value to each characterizing element (i.e. author).
|
* the corresponding value to each characterizing element (i.e. author).
|
||||||
*/
|
*/
|
||||||
function parsePostElements(elements: IPostElement[]): IPostElement[] {
|
function parsePostElements(elements: IPostElement[]): IPostElement[] {
|
||||||
|
|
Loading…
Reference in New Issue