Updated API
Reworked what data are needed to scrape a game, now the API support the scraping of the user data, especially the watched games threadspull/4/head
parent
37454a01ee
commit
331dc930b1
270
app/index.js
270
app/index.js
|
@ -1,6 +1,7 @@
|
|||
'use strict';
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const UserData = require('./scripts/user-data').UserData;
|
||||
const LoginResult = require('./scripts/login-result').LoginResult;
|
||||
const GameInfo = require('./scripts/game-info').GameInfo;
|
||||
const GameDownload = require('./scripts/game-download').GameDownload;
|
||||
|
@ -12,7 +13,8 @@ const urlExist = require('url-exist');
|
|||
const F95_BASE_URL = 'https://f95zone.to';
|
||||
const F95_SEARCH_URL = 'https://f95zone.to/search';
|
||||
const F95_LATEST_UPDATES = 'https://f95zone.to/latest';
|
||||
const F95_LOGIN_URL = 'https://f95zone.to/login'
|
||||
const F95_LOGIN_URL = 'https://f95zone.to/login';
|
||||
const F95_WATCHED_THREADS = 'https://f95zone.to/watched/threads';
|
||||
//#endregion
|
||||
|
||||
//#region CSS Selectors
|
||||
|
@ -30,6 +32,16 @@ const THREAD_POSTS = 'article.message-body:first-child > div.bbWrapper:first-of-
|
|||
const GAME_TITLE = 'h1.p-title-value';
|
||||
const GAME_IMAGES = 'img[src^="https://attachments.f95zone.to"]';
|
||||
const LOGIN_MESSAGE_ERROR = 'div.blockMessage.blockMessage--error.blockMessage--iconic';
|
||||
const GAME_TAGS = 'a.tagItem';
|
||||
const USERNAME_ELEMENT = 'a[href="/account/"] > span.p-navgroup-linkText';
|
||||
const AVATAR_PIC = 'a[href="/account/"] > span.avatar > img[class^="avatar"]';
|
||||
const UNREAD_THREAD_CHECKBOX = 'input[type="checkbox"][name="unread"]';
|
||||
const ONLY_GAMES_THREAD_OPTION = 'select[name="nodes[]"] > option[value="2"]';
|
||||
const FILTER_THREADS_BUTTON = 'button[class="button--primary button"]';
|
||||
const GAME_TITLE_PREFIXES = 'h1.p-title-value > a.labelLink > span[dir="auto"]';
|
||||
const WATCHED_THREAD_URLS = 'a[href^="/threads/"][data-tp-primary]';
|
||||
const WATCHED_THREAD_NEXT_PAGE = 'a.pageNav-jump--next';
|
||||
const WATCHED_THREAD_FILTER_POPUP_BUTTON = 'a.filterBar-menuTrigger';
|
||||
//#endregion CSS Selectors
|
||||
|
||||
//#region Game prefixes
|
||||
|
@ -83,16 +95,14 @@ let _debug = false;
|
|||
*
|
||||
* @param {Boolean} value
|
||||
*/
|
||||
module.exports.debug = function(value){
|
||||
module.exports.debug = function (value) {
|
||||
_debug = value;
|
||||
}
|
||||
module.exports.isLogged = function() {
|
||||
module.exports.isLogged = function () {
|
||||
return _isLogged;
|
||||
};
|
||||
//#endregion Properties
|
||||
|
||||
|
||||
|
||||
//#region Export methods
|
||||
/**
|
||||
* @public
|
||||
|
@ -103,8 +113,8 @@ module.exports.isLogged = function() {
|
|||
* @returns {Promise<LoginResult>} Result of the operation
|
||||
*/
|
||||
module.exports.login = async function (username, password) {
|
||||
if(_isLogged){
|
||||
if(_debug) console.log("Already logged in");
|
||||
if (_isLogged) {
|
||||
if (_debug) console.log("Already logged in");
|
||||
let result = new LoginResult();
|
||||
result.success = true;
|
||||
result.message = 'Already logged in';
|
||||
|
@ -150,7 +160,7 @@ module.exports.loadF95BaseData = async function () {
|
|||
return false;
|
||||
}
|
||||
|
||||
if(_debug) console.log('Loading base data...');
|
||||
if (_debug) console.log('Loading base data...');
|
||||
|
||||
// Prepare a new web page
|
||||
let browser = await prepareBrowser();
|
||||
|
@ -199,7 +209,8 @@ module.exports.getGameVersion = async function (info) {
|
|||
* You **must** be logged in to the portal before calling this method.
|
||||
* @param {String} name Name of the game searched
|
||||
* @param {Boolean} includeMods Indicates whether to also take mods into account when searching
|
||||
* @returns {Promise<GameInfo>} Information about the game searched or null if no game were found
|
||||
* @returns {Promise<GameInfo[]>} List of information obtained where each item corresponds to
|
||||
* an identified game (in the case of homonymy). If no games were found, null is returned
|
||||
*/
|
||||
module.exports.getGameData = async function (name, includeMods) {
|
||||
if (!_isLogged) {
|
||||
|
@ -209,19 +220,21 @@ module.exports.getGameData = async function (name, includeMods) {
|
|||
|
||||
// Gets the search results of the game being searched for
|
||||
let browser = await prepareBrowser();
|
||||
let infoList = await getSearchGameResults(browser, name, _cookies);
|
||||
let urlList = await getSearchGameResults(browser, name, _cookies);
|
||||
|
||||
// Process previous partial results
|
||||
let result = null;
|
||||
for (let info of infoList) {
|
||||
let promiseList = [];
|
||||
for (let url of urlList) {
|
||||
// Start looking for information
|
||||
promiseList.push(getGameInfo(browser, url));
|
||||
}
|
||||
|
||||
// Filter for mods
|
||||
let result = [];
|
||||
for (let info of await Promise.all(promiseList)) {
|
||||
// Skip mods if not required
|
||||
if (info.isMod && !includeMods) continue;
|
||||
|
||||
// TODO
|
||||
// What if there are more games with the same name?
|
||||
// For the moment, return the first
|
||||
result = await getGameInfo(browser, info);
|
||||
break;
|
||||
else result.push(info);
|
||||
}
|
||||
|
||||
await browser.close();
|
||||
|
@ -233,7 +246,7 @@ module.exports.getGameData = async function (name, includeMods) {
|
|||
* @param {*} platform
|
||||
* @param {*} url
|
||||
*/
|
||||
module.exports.getDownloadLink = async function(platform, url){
|
||||
module.exports.getDownloadLink = async function (platform, url) {
|
||||
if (!_isLogged) {
|
||||
console.warn('user not authenticated, unable to continue');
|
||||
return null;
|
||||
|
@ -246,11 +259,44 @@ module.exports.getDownloadLink = async function(platform, url){
|
|||
}
|
||||
/**
|
||||
* @public
|
||||
* Properly closes all instances opened by the API.
|
||||
* @returns {Promise<void>}
|
||||
* Gets the data of the currently logged in user.
|
||||
* @returns {Promise<UserData>} Data of the user currently logged in or null if an error arise
|
||||
*/
|
||||
module.exports.close = async function() {
|
||||
if(_debug) console.log("Closing F95API")
|
||||
module.exports.getUserData = async function () {
|
||||
if (!_isLogged) {
|
||||
console.warn('user not authenticated, unable to continue');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Prepare a new web page
|
||||
let browser = await prepareBrowser();
|
||||
let page = await preparePage(browser); // Set new isolated page
|
||||
await page.setCookie(..._cookies); // Set cookies to avoid login
|
||||
await page.goto(F95_BASE_URL); // Go to base page
|
||||
|
||||
// Explicitly wait for the required items to load
|
||||
await page.waitForSelector(USERNAME_ELEMENT);
|
||||
await page.waitForSelector(AVATAR_PIC);
|
||||
|
||||
let threads = getUserWatchedGameThreads(browser);
|
||||
|
||||
let username = await page.evaluate((selector) =>
|
||||
document.querySelector(selector).innerText,
|
||||
USERNAME_ELEMENT);
|
||||
|
||||
let avatarSrc = await page.evaluate((selector) =>
|
||||
document.querySelector(selector).getAttribute('src'),
|
||||
AVATAR_PIC);
|
||||
|
||||
let ud = new UserData();
|
||||
ud.username = username;
|
||||
ud.avatarSrc = isStringAValidURL(avatarSrc) ? new URL(avatarSrc) : null;
|
||||
ud.watchedThreads = await threads;
|
||||
|
||||
await page.close();
|
||||
await browser.close();
|
||||
|
||||
return ud;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
|
@ -266,7 +312,7 @@ module.exports.close = async function() {
|
|||
async function prepareBrowser() {
|
||||
// Create a headless browser
|
||||
let browser = await puppeteer.launch({
|
||||
headless: true,
|
||||
headless: false,
|
||||
});
|
||||
|
||||
return browser;
|
||||
|
@ -424,7 +470,7 @@ function isF95URL(url) {
|
|||
* @param {String} url String to check for correctness
|
||||
* @returns {Boolean} true if the string is a valid URL, false otherwise
|
||||
*/
|
||||
function isStringAValidURL(url){
|
||||
function isStringAValidURL(url) {
|
||||
try {
|
||||
new URL(url);
|
||||
return true;
|
||||
|
@ -479,25 +525,68 @@ async function loginF95(browser, username, password) {
|
|||
if (errorMessage === 'Incorrect password. Please try again.') result.message = 'Incorrect password';
|
||||
else if (errorMessage === "The requested user '" + username + "' could not be found.") result.message = 'Incorrect username';
|
||||
else result.message = errorMessage;
|
||||
}
|
||||
else result.message = "Unknown error";
|
||||
} else result.message = "Unknown error";
|
||||
await page.close(); // Close the page
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @private
|
||||
* Gets the list of URLs of threads the user follows.
|
||||
* @param {puppeteer.Browser} browser Browser object used for navigation
|
||||
* @returns {Promise<URL[]>} URL list
|
||||
*/
|
||||
async function getUserData(){
|
||||
async function getUserWatchedGameThreads(browser) {
|
||||
let page = await preparePage(browser); // Set new isolated page
|
||||
await page.goto(F95_WATCHED_THREADS); // Go to the thread page
|
||||
|
||||
}
|
||||
// Explicitly wait for the required items to load
|
||||
await page.waitForSelector(WATCHED_THREAD_FILTER_POPUP_BUTTON);
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
async function getUserWatchedGameThreads(){
|
||||
// Show the popup
|
||||
await page.click(WATCHED_THREAD_FILTER_POPUP_BUTTON);
|
||||
await page.waitForSelector(UNREAD_THREAD_CHECKBOX);
|
||||
await page.waitForSelector(ONLY_GAMES_THREAD_OPTION);
|
||||
await page.waitForSelector(FILTER_THREADS_BUTTON);
|
||||
|
||||
// Set the filters
|
||||
await page.evaluate((selector) =>
|
||||
document.querySelector(selector).removeAttribute('checked'),
|
||||
UNREAD_THREAD_CHECKBOX); // Also read the threads already read
|
||||
|
||||
await page.click(ONLY_GAMES_THREAD_OPTION);
|
||||
|
||||
// Filter the threads
|
||||
await page.click(FILTER_THREADS_BUTTON);
|
||||
await page.waitForSelector(WATCHED_THREAD_URLS);
|
||||
|
||||
// Get the threads urls
|
||||
let urls = [];
|
||||
let nextPageExists = false;
|
||||
do {
|
||||
// Get all the URLs
|
||||
for (let handle of await page.$$(WATCHED_THREAD_URLS)) {
|
||||
let src = await page.evaluate((element) => element.href, handle);
|
||||
// If 'unread' is left, it will redirect to the last unread post
|
||||
let url = new URL(src.replace('/unread', ''));
|
||||
urls.push(url);
|
||||
}
|
||||
|
||||
nextPageExists = await page.evaluate((selector) =>
|
||||
document.querySelector(selector),
|
||||
WATCHED_THREAD_NEXT_PAGE);
|
||||
|
||||
// Click to next page
|
||||
if (nextPageExists) {
|
||||
await page.click(WATCHED_THREAD_NEXT_PAGE);
|
||||
await page.waitForSelector(WATCHED_THREAD_URLS);
|
||||
}
|
||||
}
|
||||
while (nextPageExists);
|
||||
|
||||
await page.close();
|
||||
return urls;
|
||||
}
|
||||
//#endregion User
|
||||
|
||||
|
@ -506,33 +595,42 @@ async function getUserWatchedGameThreads(){
|
|||
* @private
|
||||
* Get information from the game's main page.
|
||||
* @param {puppeteer.Browser} browser Browser object used for navigation
|
||||
* @param {GameInfo} info Partial game information
|
||||
* @param {URL} url URL of the game/mod to extract data from
|
||||
* @return {Promise<GameInfo>} Complete information about the game you are looking for
|
||||
*/
|
||||
async function getGameInfo(browser, info) {
|
||||
if(_debug) console.log('Obtaining game info');
|
||||
async function getGameInfo(browser, url) {
|
||||
if (_debug) console.log('Obtaining game info');
|
||||
|
||||
// Verify the correctness of the URL
|
||||
if (!isF95URL(info.f95url)) throw info.f95url + ' is not a valid F95Zone URL';
|
||||
let exists = await urlExist(info.f95url.toString());
|
||||
if (!isF95URL(url)) throw url + ' is not a valid F95Zone URL';
|
||||
let exists = await urlExist(url.toString());
|
||||
if (!exists) return new GameInfo();
|
||||
|
||||
let page = await preparePage(browser); // Set new isolated page
|
||||
await page.setCookie(..._cookies); // Set cookies to avoid login
|
||||
await page.goto(info.f95url.toString(), {
|
||||
await page.goto(url.toString(), {
|
||||
waitUntil: WAIT_STATEMENT
|
||||
}); // Go to the game page and wait until it loads
|
||||
|
||||
// Object to fill with information
|
||||
let info = new GameInfo();
|
||||
|
||||
// Get the game/mod name (without square brackets)
|
||||
let title = getGameTitle(page);
|
||||
|
||||
// Get the game/mod author (without square brackets)
|
||||
let author = getGameAuthor(page);
|
||||
|
||||
// Get the game tags
|
||||
let tags = getGameTags(page);
|
||||
|
||||
// Get the game title image (the first is what we are searching)
|
||||
let previewSource = await getGamePreviewSource(page);
|
||||
if (previewSource === null) console.warn('Cannot find game preview image for ' + await title);
|
||||
|
||||
// Parse the prefixes
|
||||
info = await parsePrefixes(page, info); // Fill status/engines/isMod
|
||||
|
||||
// Gets the first post, where are listed all the game's informations
|
||||
let post = (await page.$$(THREAD_POSTS))[0];
|
||||
|
||||
|
@ -553,12 +651,14 @@ async function getGameInfo(browser, info) {
|
|||
info.name = await title;
|
||||
info.author = await author;
|
||||
info.overview = overview;
|
||||
info.tags = await tags;
|
||||
info.f95url = url;
|
||||
info.version = info.isMod ? parsedInfos['MOD VERSION'] : parsedInfos['VERSION'];
|
||||
info.lastUpdate = info.isMod ? parsedInfos['UPDATED'] : parsedInfos['THREAD UPDATED'];
|
||||
info.previewSource = previewSource;
|
||||
|
||||
await page.close(); // Close the page
|
||||
if(_debug) console.log('Founded data for ' + await title);
|
||||
if (_debug) console.log('Founded data for ' + info.name);
|
||||
return info;
|
||||
}
|
||||
|
||||
|
@ -640,11 +740,55 @@ async function getGameTitle(page) {
|
|||
const structuredTitle = HTMLParser.parse(titleHTML);
|
||||
|
||||
// The last element **shoud be** the title without prefixes (engines, status, other...)
|
||||
var gameTitle = structuredTitle.childNodes.pop().rawText;
|
||||
let gameTitle = structuredTitle.childNodes.pop().rawText;
|
||||
const endTitleIndex = gameTitle.indexOf('[');
|
||||
return gameTitle.substring(0, endTitleIndex).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Get the list of tags associated with the game.
|
||||
* @param {puppeteer.Page} page Page containing the tags to be extrapolated
|
||||
* @returns {Promise<String[]>} List of uppercase tags
|
||||
*/
|
||||
async function getGameTags(page) {
|
||||
let tags = [];
|
||||
|
||||
// Get the game tags
|
||||
for (let handle of await page.$$(GAME_TAGS)) {
|
||||
let tag = await page.evaluate((element) => element.innerText, handle);
|
||||
tags.push(tag.toUpperCase());
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Process the game title prefixes to extract information such as game status,
|
||||
* graphics engine used, and whether it is a mod or original game.
|
||||
* @param {puppeteer.Page} page Page containing the prefixes to be extrapolated
|
||||
* @param {GameInfo} info Object to assign the identified information to
|
||||
* @returns {Promise<GameInfo>} GameInfo object passed in to which the identified information has been added
|
||||
*/
|
||||
async function parsePrefixes(page, info) {
|
||||
// The 'Ongoing' status is not specified, only 'Abandoned'/'OnHold'/'Complete'
|
||||
info.status = 'Ongoing';
|
||||
for (let handle of await page.$$(GAME_TITLE_PREFIXES)) {
|
||||
let value = await page.evaluate((element) => element.innerText, handle);
|
||||
|
||||
// Clean the prefix
|
||||
let prefix = value.toUpperCase().replace('[', '').replace(']', '').trim();
|
||||
|
||||
// Getting infos...
|
||||
if (_statuses.includes(prefix)) info.status = prefix;
|
||||
else if (_engines.includes(prefix)) info.engine = prefix;
|
||||
|
||||
// This is not a game but a mod
|
||||
else if (prefix === MOD_PREFIX) info.isMod = true;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param {puppeteer.Browser} browser
|
||||
|
@ -680,7 +824,7 @@ async function getGameDownloadLink(browser, url) {
|
|||
* Search the F95Zone portal to find possible conversations regarding the game you are looking for.
|
||||
* @param {puppeteer.Browser} browser Browser object used for navigation
|
||||
* @param {String} gamename Name of the game to search for
|
||||
* @returns {Promise<GameInfo[]>} List of information obtained from the preliminary research on the F95 portal
|
||||
* @returns {Promise<URL[]>} List of URL of possible games obtained from the preliminary research on the F95 portal
|
||||
*/
|
||||
async function getSearchGameResults(browser, gamename) {
|
||||
if (_debug) console.log('Searching ' + gamename + ' on F95Zone');
|
||||
|
@ -690,10 +834,12 @@ async function getSearchGameResults(browser, gamename) {
|
|||
await page.goto(F95_SEARCH_URL, {
|
||||
waitUntil: WAIT_STATEMENT
|
||||
}); // Go to the search form and wait for it
|
||||
|
||||
// Explicitly wait for the required items to load
|
||||
await page.waitForSelector(SEARCH_FORM_TEXTBOX);
|
||||
await page.waitForSelector(TITLE_ONLY_CHECKBOX);
|
||||
await page.waitForSelector(SEARCH_BUTTON);
|
||||
|
||||
await page.type(SEARCH_FORM_TEXTBOX, gamename) // Type the game we desire
|
||||
await page.click(TITLE_ONLY_CHECKBOX) // Select only the thread with the game in the titles
|
||||
await Promise.all([
|
||||
|
@ -707,13 +853,13 @@ async function getSearchGameResults(browser, gamename) {
|
|||
let threadTitleList = await page.$$(THREAD_TITLE);
|
||||
|
||||
// For each title extract the info about the conversation
|
||||
if(_debug) console.log('Extracting info from conversation titles');
|
||||
if (_debug) console.log('Extracting info from conversation titles');
|
||||
let results = [];
|
||||
for (const title of threadTitleList) {
|
||||
var info = await getSearchThreadInfo(page, title);
|
||||
for (let title of threadTitleList) {
|
||||
let gameUrl = await getOnlyGameThreads(page, title);
|
||||
|
||||
// Append the game's informations
|
||||
if (info !== null) results.push(info);
|
||||
if (gameUrl !== null) results.push(gameUrl);
|
||||
}
|
||||
if (_debug) console.log('Find ' + results.length + ' conversations');
|
||||
await page.close(); // Close the page
|
||||
|
@ -723,42 +869,26 @@ async function getSearchGameResults(browser, gamename) {
|
|||
|
||||
/**
|
||||
* @private
|
||||
* Starting from the title of a conversation on the F95 portal,
|
||||
* he obtains basic information about the element (in case it is a game)
|
||||
* Return the link of a conversation if it is a game or a mod
|
||||
* @param {puppeteer.Page} page Page containing the conversation to be analyzed
|
||||
* @param {puppeteer.ElementHandle} titleHandle Title of the conversation to be analyzed
|
||||
* @return {Promise<GameInfo>} Object containing the information obtained from the analysis or null if it is not a game conversation
|
||||
* @return {Promise<URL>} URL of the game/mod
|
||||
*/
|
||||
async function getSearchThreadInfo(page, titleHandle) {
|
||||
|
||||
let info = new GameInfo();
|
||||
|
||||
async function getOnlyGameThreads(page, titleHandle) {
|
||||
// Get the URL of the thread from the title
|
||||
let relativeURLThread = await page.evaluate((element) => element.querySelector('a').href, titleHandle);
|
||||
info.f95url = new URL(relativeURLThread, F95_BASE_URL);
|
||||
let url = new URL(relativeURLThread, F95_BASE_URL);
|
||||
|
||||
// Select infos like the engine used for the game or it's status
|
||||
// parsing the title
|
||||
let elements = await titleHandle.$$('span[dir="auto"]');
|
||||
|
||||
// The 'Ongoing' status is not specified, only 'Abandoned'/'OnHold'/'Complete' are
|
||||
info.status = 'Ongoing';
|
||||
for (let element of elements) {
|
||||
// Parse prefixes to ignore game recommendation
|
||||
for (let element of await titleHandle.$$('span[dir="auto"]')) {
|
||||
// Elaborate the prefixes
|
||||
let prefix = await page.evaluate(element => element.textContent.toUpperCase(), element);
|
||||
prefix = prefix.replace('[', '').replace(']', '');
|
||||
|
||||
// This is not a game nor a mod, we can exit
|
||||
if (prefix === GAME_RECOMMENDATION_PREFIX) return null;
|
||||
|
||||
// Getting infos...
|
||||
else if (_statuses.includes(prefix)) info.status = prefix;
|
||||
else if (_engines.includes(prefix)) info.engine = prefix;
|
||||
|
||||
// This is probably a mod for the game we are searching
|
||||
else if (prefix === MOD_PREFIX) info.isMod = true;
|
||||
}
|
||||
return info;
|
||||
return url;
|
||||
}
|
||||
//#endregion Game search
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
const UNKNOWN = 'Unknown';
|
||||
|
||||
class GameInfo {
|
||||
constructor() {
|
||||
this.UNKNOWN = 'Unknown';
|
||||
|
||||
/**
|
||||
* Game name
|
||||
* @type String
|
||||
*/
|
||||
this.name = this.UNKNOWN;
|
||||
this.name = UNKNOWN;
|
||||
/**
|
||||
* Game author
|
||||
* @type String
|
||||
*/
|
||||
this.author = this.UNKNOWN;
|
||||
this.author = UNKNOWN;
|
||||
/**
|
||||
* URL to the game's official conversation on the F95Zone portal
|
||||
* @type URL
|
||||
|
@ -21,17 +21,22 @@ class GameInfo {
|
|||
* Game description
|
||||
* @type String
|
||||
*/
|
||||
this.overview = this.UNKNOWN;
|
||||
this.overview = UNKNOWN;
|
||||
/**
|
||||
* List of tags associated with the game
|
||||
* @type String[]
|
||||
*/
|
||||
this.tags = [];
|
||||
/**
|
||||
* Graphics engine used for game development
|
||||
* @type String
|
||||
*/
|
||||
this.engine = this.UNKNOWN;
|
||||
this.engine = UNKNOWN;
|
||||
/**
|
||||
* Progress of the game
|
||||
* @type String
|
||||
*/
|
||||
this.status = this.UNKNOWN;
|
||||
this.status = UNKNOWN;
|
||||
/**
|
||||
* Game description image URL
|
||||
* @type URL
|
||||
|
@ -41,17 +46,17 @@ class GameInfo {
|
|||
* Game version
|
||||
* @type String
|
||||
*/
|
||||
this.version = this.UNKNOWN;
|
||||
this.version = UNKNOWN;
|
||||
/**
|
||||
* Last time the game underwent updates
|
||||
* @type String
|
||||
*/
|
||||
this.lastUpdate = this.UNKNOWN;
|
||||
this.lastUpdate = UNKNOWN;
|
||||
/**
|
||||
* Last time the local copy of the game was run
|
||||
* @type String
|
||||
*/
|
||||
this.lastPlayed = this.UNKNOWN;
|
||||
this.lastPlayed = UNKNOWN;
|
||||
/**
|
||||
* Specifies if the game is original or a mod
|
||||
* @type Boolean
|
||||
|
@ -61,7 +66,7 @@ class GameInfo {
|
|||
* Directory containing the local copy of the game
|
||||
* @type String
|
||||
*/
|
||||
this.gameDir = this.UNKNOWN;
|
||||
this.gameDir = UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,24 @@
|
|||
/**
|
||||
* Class containing the data of the user currently connected to the F95Zone platform.
|
||||
*/
|
||||
class UserData {
|
||||
constructor(){
|
||||
constructor() {
|
||||
/**
|
||||
* User username.
|
||||
* @type String
|
||||
*/
|
||||
this.username = "";
|
||||
/**
|
||||
* Path to the user 's profile picture.
|
||||
* @type URL
|
||||
*/
|
||||
this.avatarSrc = null;
|
||||
/**
|
||||
* List of followed thread URLs.
|
||||
* @type URL[]
|
||||
*/
|
||||
this.watchedThreads = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.UserData = UserData;
|
|
@ -1,4 +1,4 @@
|
|||
const { debug, login, close, getGameData, loadF95BaseData } = require("../app/index");
|
||||
const { debug, login, getGameData, loadF95BaseData, getUserData } = require("../app/index");
|
||||
|
||||
debug(true);
|
||||
main();
|
||||
|
@ -7,9 +7,10 @@ async function main() {
|
|||
let loginResult = await login("MillenniumEarl", "f9vTcRNuvxj4YpK");
|
||||
|
||||
if (loginResult.success) {
|
||||
await loadF95BaseData();
|
||||
let data = await getGameData("kingdom of deception", false);
|
||||
// await loadF95BaseData();
|
||||
// let data = await getGameData("kingdom of deception", false);
|
||||
// console.log(data.pop());
|
||||
let data = await getUserData();
|
||||
console.log(data);
|
||||
}
|
||||
await close();
|
||||
}
|
|
@ -251,14 +251,12 @@
|
|||
"version": "14.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz",
|
||||
"integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"@types/yauzl": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz",
|
||||
"integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
|
@ -268,7 +266,6 @@
|
|||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"event-target-shim": "^5.0.0"
|
||||
}
|
||||
|
@ -276,8 +273,7 @@
|
|||
"agent-base": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz",
|
||||
"integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g=="
|
||||
},
|
||||
"aggregate-error": {
|
||||
"version": "3.1.0",
|
||||
|
@ -365,14 +361,12 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"dev": true
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||
},
|
||||
"base64-js": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "2.1.0",
|
||||
|
@ -384,7 +378,6 @@
|
|||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz",
|
||||
"integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
|
@ -395,7 +388,6 @@
|
|||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -420,7 +412,6 @@
|
|||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
|
||||
"integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"base64-js": "^1.0.2",
|
||||
"ieee754": "^1.1.4"
|
||||
|
@ -429,8 +420,7 @@
|
|||
"buffer-crc32": {
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
||||
"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=",
|
||||
"dev": true
|
||||
"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
|
||||
},
|
||||
"caching-transform": {
|
||||
"version": "4.0.0",
|
||||
|
@ -500,8 +490,7 @@
|
|||
"chownr": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||
},
|
||||
"clean-stack": {
|
||||
"version": "2.2.0",
|
||||
|
@ -544,8 +533,7 @@
|
|||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||
},
|
||||
"convert-source-map": {
|
||||
"version": "1.7.0",
|
||||
|
@ -571,7 +559,6 @@
|
|||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
|
||||
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
|
@ -612,8 +599,7 @@
|
|||
"devtools-protocol": {
|
||||
"version": "0.0.799653",
|
||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.799653.tgz",
|
||||
"integrity": "sha512-t1CcaZbvm8pOlikqrsIM9GOa7Ipp07+4h/q9u0JXBWjPCjHdBl9KkddX87Vv9vBHoBGtwV79sYQNGnQM6iS5gg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-t1CcaZbvm8pOlikqrsIM9GOa7Ipp07+4h/q9u0JXBWjPCjHdBl9KkddX87Vv9vBHoBGtwV79sYQNGnQM6iS5gg=="
|
||||
},
|
||||
"diff": {
|
||||
"version": "4.0.2",
|
||||
|
@ -631,7 +617,6 @@
|
|||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
|
@ -708,14 +693,12 @@
|
|||
"event-target-shim": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
|
||||
},
|
||||
"extract-zip": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
|
||||
"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/yauzl": "^2.9.1",
|
||||
"debug": "^4.1.1",
|
||||
|
@ -727,7 +710,6 @@
|
|||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
|
||||
"integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pend": "~1.2.0"
|
||||
}
|
||||
|
@ -756,7 +738,6 @@
|
|||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"locate-path": "^5.0.0",
|
||||
"path-exists": "^4.0.0"
|
||||
|
@ -790,14 +771,12 @@
|
|||
"fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
||||
"dev": true
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.1.3",
|
||||
|
@ -840,7 +819,6 @@
|
|||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pump": "^3.0.0"
|
||||
}
|
||||
|
@ -849,7 +827,6 @@
|
|||
"version": "7.1.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
|
@ -920,8 +897,7 @@
|
|||
"he": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
|
||||
},
|
||||
"html-escaper": {
|
||||
"version": "2.0.2",
|
||||
|
@ -933,7 +909,6 @@
|
|||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz",
|
||||
"integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"agent-base": "5",
|
||||
"debug": "4"
|
||||
|
@ -942,8 +917,7 @@
|
|||
"ieee754": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
||||
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
|
||||
},
|
||||
"imurmurhash": {
|
||||
"version": "0.1.4",
|
||||
|
@ -961,7 +935,6 @@
|
|||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
|
@ -970,14 +943,12 @@
|
|||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"ip-regex": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.2.0.tgz",
|
||||
"integrity": "sha512-n5cDDeTWWRwK1EBoWwRti+8nP4NbytBBY0pldmnIkq6Z55KNFmWofh4rl9dPZpj+U/nVq7gweR3ylrvMt4YZ5A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-n5cDDeTWWRwK1EBoWwRti+8nP4NbytBBY0pldmnIkq6Z55KNFmWofh4rl9dPZpj+U/nVq7gweR3ylrvMt4YZ5A=="
|
||||
},
|
||||
"is-arguments": {
|
||||
"version": "1.0.4",
|
||||
|
@ -1097,7 +1068,6 @@
|
|||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-url-superb/-/is-url-superb-3.0.0.tgz",
|
||||
"integrity": "sha512-3faQP+wHCGDQT1qReM5zCPx2mxoal6DzbzquFlCYJLWyy4WPTved33ea2xFbX37z4NoriEwZGIYhFtx8RUB5wQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"url-regex": "^5.0.0"
|
||||
}
|
||||
|
@ -1269,14 +1239,12 @@
|
|||
"ky": {
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/ky/-/ky-0.19.1.tgz",
|
||||
"integrity": "sha512-ZwciYrfaWpDI72U2HAruuGYGFW3PCfGNdWWSANGGssg9BGm4rRJ9s/sApiiRpj+8Y245/hlZW9c60zudLr6iwA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-ZwciYrfaWpDI72U2HAruuGYGFW3PCfGNdWWSANGGssg9BGm4rRJ9s/sApiiRpj+8Y245/hlZW9c60zudLr6iwA=="
|
||||
},
|
||||
"ky-universal": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.5.0.tgz",
|
||||
"integrity": "sha512-O+0wjCua5i45lYBZrBy8TyRDRVodtsmzVC/MlE5FN7ZMFu/Icz7ekbZ85sdFw0F/JwGhXZTaKjXq9IgUGwGedQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"abort-controller": "^3.0.0",
|
||||
"node-fetch": "^2.6.0"
|
||||
|
@ -1286,7 +1254,6 @@
|
|||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-locate": "^4.1.0"
|
||||
}
|
||||
|
@ -1377,7 +1344,6 @@
|
|||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
@ -1391,8 +1357,7 @@
|
|||
"mkdirp-classic": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
|
||||
},
|
||||
"mocha": {
|
||||
"version": "8.1.3",
|
||||
|
@ -1632,20 +1597,17 @@
|
|||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||
},
|
||||
"node-html-parser": {
|
||||
"version": "1.2.21",
|
||||
"resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-1.2.21.tgz",
|
||||
"integrity": "sha512-6vDhgen6J332syN5HUmeT4FfBG7m6bFRrPN+FXY8Am7FGuVpsIxTASVbeoO5PF2IHbX2s+WEIudb1hgxOjllNQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"he": "1.2.0"
|
||||
}
|
||||
|
@ -1728,7 +1690,6 @@
|
|||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -1737,7 +1698,6 @@
|
|||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-try": "^2.0.0"
|
||||
}
|
||||
|
@ -1746,7 +1706,6 @@
|
|||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-limit": "^2.2.0"
|
||||
}
|
||||
|
@ -1763,8 +1722,7 @@
|
|||
"p-try": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
|
||||
},
|
||||
"package-hash": {
|
||||
"version": "4.0.0",
|
||||
|
@ -1781,14 +1739,12 @@
|
|||
"path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||
"dev": true
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
||||
},
|
||||
"path-key": {
|
||||
"version": "3.1.1",
|
||||
|
@ -1811,8 +1767,7 @@
|
|||
"pend": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
||||
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
|
||||
"dev": true
|
||||
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
|
||||
},
|
||||
"picomatch": {
|
||||
"version": "2.2.2",
|
||||
|
@ -1824,7 +1779,6 @@
|
|||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"find-up": "^4.0.0"
|
||||
}
|
||||
|
@ -1841,8 +1795,7 @@
|
|||
"progress": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="
|
||||
},
|
||||
"promise.allsettled": {
|
||||
"version": "1.0.2",
|
||||
|
@ -1860,14 +1813,12 @@
|
|||
"proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"pump": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
|
@ -1877,7 +1828,6 @@
|
|||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-5.3.1.tgz",
|
||||
"integrity": "sha512-YTM1RaBeYrj6n7IlRXRYLqJHF+GM7tasbvrNFx6w1S16G76NrPq7oYFKLDO+BQsXNtS8kW2GxWCXjIMPvfDyaQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "^4.1.0",
|
||||
"devtools-protocol": "0.0.799653",
|
||||
|
@ -1905,7 +1855,6 @@
|
|||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
|
@ -1961,7 +1910,6 @@
|
|||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
|
@ -2075,7 +2023,6 @@
|
|||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
},
|
||||
|
@ -2083,8 +2030,7 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2122,7 +2068,6 @@
|
|||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.0.tgz",
|
||||
"integrity": "sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
|
@ -2134,7 +2079,6 @@
|
|||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.4.tgz",
|
||||
"integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bl": "^4.0.3",
|
||||
"end-of-stream": "^1.4.1",
|
||||
|
@ -2157,14 +2101,12 @@
|
|||
"through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
|
||||
"dev": true
|
||||
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
|
||||
},
|
||||
"tlds": {
|
||||
"version": "1.210.0",
|
||||
"resolved": "https://registry.npmjs.org/tlds/-/tlds-1.210.0.tgz",
|
||||
"integrity": "sha512-5bzt4JE+NlnwiKpVW9yzWxuc44m+t2opmPG+eSKDp5V5qdyGvjMngKgBb5ZK8GiheQMbRTCKpRwFJeIEO6pV7Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-5bzt4JE+NlnwiKpVW9yzWxuc44m+t2opmPG+eSKDp5V5qdyGvjMngKgBb5ZK8GiheQMbRTCKpRwFJeIEO6pV7Q=="
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
|
@ -2206,7 +2148,6 @@
|
|||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
|
||||
"integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer": "^5.2.1",
|
||||
"through": "^2.3.8"
|
||||
|
@ -2216,7 +2157,6 @@
|
|||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/url-exist/-/url-exist-2.0.2.tgz",
|
||||
"integrity": "sha512-JqLjYS8pU9xZtY3ro4c54CztoP5R8qRyMlg2Cxr4M9YD1NCe57MOsZHF1rP3y+qQcc7cqiZBBd4Cu5oehcJRlQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-url-superb": "^3.0.0",
|
||||
"ky": "^0.19.0",
|
||||
|
@ -2227,7 +2167,6 @@
|
|||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/url-regex/-/url-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-O08GjTiAFNsSlrUWfqF1jH0H1W3m35ZyadHrGv5krdnmPPoxP27oDTqux/579PtaroiSGm5yma6KT1mHFH6Y/g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ip-regex": "^4.1.0",
|
||||
"tlds": "^1.203.0"
|
||||
|
@ -2236,8 +2175,7 @@
|
|||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
|
@ -2349,8 +2287,7 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"write-file-atomic": {
|
||||
"version": "3.0.3",
|
||||
|
@ -2367,8 +2304,7 @@
|
|||
"ws": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
|
||||
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.0",
|
||||
|
@ -2547,7 +2483,6 @@
|
|||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
|
||||
"integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-crc32": "~0.2.3",
|
||||
"fd-slicer": "~1.1.0"
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
"main": "./app/index.js",
|
||||
"scripts": {
|
||||
"unit-test": "nyc --reporter=text mocha",
|
||||
"test": "node ./app/test.js",
|
||||
"debug": "nodemon --inspect ."
|
||||
"test": "node ./app/test.js"
|
||||
},
|
||||
"keywords": [
|
||||
"f95zone",
|
||||
|
|
Loading…
Reference in New Issue