diff --git a/app/index.js b/app/index.js index 4d22a99..4f4afc1 100644 --- a/app/index.js +++ b/app/index.js @@ -12,11 +12,13 @@ const Credentials = require("./scripts/classes/credentials.js"); const GameInfo = require("./scripts/classes/game-info.js"); const LoginResult = require("./scripts/classes/login-result.js"); const UserData = require("./scripts/classes/user-data.js"); +const TagParser = require("./scripts/classes/tag-parser.js"); //#region Export classes module.exports.GameInfo = GameInfo; module.exports.LoginResult = LoginResult; module.exports.UserData = UserData; +module.exports.TagParser = TagParser; //#endregion Export classes //#region Export properties diff --git a/app/scripts/classes/tag-parser.js b/app/scripts/classes/tag-parser.js new file mode 100644 index 0000000..70355a6 --- /dev/null +++ b/app/scripts/classes/tag-parser.js @@ -0,0 +1,90 @@ +"use strict"; + +// Public modules from npm +const cheerio = require("cheerio"); + +// Modules from file +const { fetchHTML } = require("../network-helper.js"); +const f95Selector = require("../constants/css-selector.js"); +const { F95_LATEST_UPDATES } = require("../constants/url.js"); + +class TagParser { + constructor() { + /** + * Dictionary mapping the keys to the F95API game tags. + * @type Object. + */ + this._tagsDict = {}; + } + + //#region Private methods + /** + * @private + * Gets the key associated with a given value from a dictionary. + * @param {Object} object Dictionary to search + * @param {Any} value Value associated with the key + * @returns {String|undefined} Key found or undefined + */ + _getKeyByValue(object, value) { + return Object.keys(object).find(key => object[key] === value); + } + //#endregion Private methods + + /** + * @public + * Gets all tags, with their IDs, from F95Zone. + */ + async fetch() { + // Clean dictionary + this._tagsDict = {}; + + // Load the HTML + const html = await fetchHTML(F95_LATEST_UPDATES); + const $ = cheerio.load(html); + + // Search for the tags + const unparsedText = $(f95Selector.LU_TAGS_SCRIPT).html().trim(); + const startIndex = unparsedText.indexOf("{"); + const endIndex = unparsedText.lastIndexOf("}"); + const parsedText = unparsedText.substring(startIndex, endIndex + 1); + const data = JSON.parse(parsedText); + + // Extract only the data we need + this._tagsDict = data.tags; + } + + /** + * @public + * Convert a list of tags to their respective IDs. + * @param {String[]} tags + */ + tagsToIDs(tags) { + const ids = []; + for(const tag of tags) { + // Extract the key from the value + const key = this._getKeyByValue(this._tagsDict, tag); + if(key) ids.push(parseInt(key)); + } + return ids.sort((a, b) => a - b); // JS sort alphabetically, same old problem + } + + /** + * @public + * It converts a list of IDs into their respective tags. + * @param {number[]} ids + */ + idsToTags(ids) { + const tags = []; + for(const id of ids) { + // Check if the key exists in the dict + const exist = id in this._tagsDict; + if (!exist) continue; + + // Save the value + tags.push(this._tagsDict[id]); + } + return tags.sort(); + } +} + +module.exports = TagParser; \ No newline at end of file diff --git a/app/scripts/constants/css-selector.js b/app/scripts/constants/css-selector.js index 172b511..aef8c13 100644 --- a/app/scripts/constants/css-selector.js +++ b/app/scripts/constants/css-selector.js @@ -20,4 +20,5 @@ module.exports = Object.freeze({ UD_USERNAME_ELEMENT: "a[href=\"/account/\"] > span.p-navgroup-linkText", UD_AVATAR_PIC: "a[href=\"/account/\"] > span.avatar > img[class^=\"avatar\"]", LOGIN_MESSAGE_ERROR: "div.blockMessage.blockMessage--error.blockMessage--iconic", + LU_TAGS_SCRIPT: "script:contains('latestUpdates')", });