From 6f97370710f9795206b3356b85ef6fad604fd4f4 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Fri, 12 May 2023 22:05:20 +0200
Subject: [PATCH 01/22] Add sharedfile comment support

---
 components/sharedfiles.js | 60 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 components/sharedfiles.js

diff --git a/components/sharedfiles.js b/components/sharedfiles.js
new file mode 100644
index 0000000..eda5ea8
--- /dev/null
+++ b/components/sharedfiles.js
@@ -0,0 +1,60 @@
+var SteamCommunity = require('../index.js');
+var SteamID = require('steamid');
+
+// Note: a CSteamSharedfile class does not exist because we can't get data using the "normal" xml way to fill a CSteamSharedfile object
+
+/**
+ * Deletes a comment from a sharedfile's comment section
+ * @param {SteamID | String} userID - ID of the user associated to this sharedfile
+ * @param {String} sid - ID of the sharedfile
+ * @param {String} cid - ID of the comment to delete
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+SteamCommunity.prototype.deleteSharedfileComment = function(userID, sid, cid, callback) {
+    if (typeof userID === "string") {
+        userID = new SteamID(userID);
+    }
+
+    this.httpRequestPost({
+        "uri": `https://steamcommunity.com/comment/PublishedFile_Public/delete/${userID.toString()}/${sid}/`,
+        "form": {
+            "gidcomment": cid,
+            "count": 10,
+            "sessionid": this.getSessionID()
+        }
+    }, function(err, response, body) {
+        if (!callback) {
+            return;
+        }
+
+        callback(null || err);
+    }, "steamcommunity");
+};
+
+/**
+ * Posts a comment to a sharedfile
+ * @param {SteamID | String} userID - ID of the user associated to this sharedfile
+ * @param {String} sid - ID of the sharedfile
+ * @param {String} message - Content of the comment to post
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+SteamCommunity.prototype.postSharedfileComment = function(userID, sid, message, callback) {
+    if (typeof userID === "string") {
+        userID = new SteamID(userID);
+    }
+
+    this.httpRequestPost({
+        "uri": `https://steamcommunity.com/comment/PublishedFile_Public/post/${userID.toString()}/${sid}/`,
+        "form": {
+            "comment": message,
+            "count": 10,
+            "sessionid": this.getSessionID()
+        }
+    }, function(err, response, body) {
+        if (!callback) {
+            return;
+        }
+
+        callback(null || err);
+    }, "steamcommunity");
+};
\ No newline at end of file

From 18011b3fac901969642a48b9012cadab3df449f6 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Fri, 12 May 2023 22:10:37 +0200
Subject: [PATCH 02/22] Add sharedfile voting support

---
 components/sharedfiles.js | 42 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/components/sharedfiles.js b/components/sharedfiles.js
index eda5ea8..cb8f116 100644
--- a/components/sharedfiles.js
+++ b/components/sharedfiles.js
@@ -55,6 +55,48 @@ SteamCommunity.prototype.postSharedfileComment = function(userID, sid, message,
             return;
         }
 
+        callback(null || err);
+    }, "steamcommunity");
+};
+
+/**
+ * Downvotes a sharedfile
+ * @param {String} sid - ID of the sharedfile
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+SteamCommunity.prototype.voteDownSharedfile = function(sid, callback) {
+    this.httpRequestPost({
+        "uri": "https://steamcommunity.com/sharedfiles/votedown",
+        "form": {
+            "id": sid,
+            "sessionid": this.getSessionID()
+        }
+    }, function(err, response, body) {
+        if (!callback) {
+            return;
+        }
+
+        callback(null || err);
+    }, "steamcommunity");
+};
+
+/**
+ * Upvotes a sharedfile
+ * @param {String} sid - ID of the sharedfile
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+SteamCommunity.prototype.voteUpSharedfile = function(sid, callback) {
+    this.httpRequestPost({
+        "uri": "https://steamcommunity.com/sharedfiles/voteup",
+        "form": {
+            "id": sid,
+            "sessionid": this.getSessionID()
+        }
+    }, function(err, response, body) {
+        if (!callback) {
+            return;
+        }
+
         callback(null || err);
     }, "steamcommunity");
 };
\ No newline at end of file

From d807f106532811294cc360ed288f58014c55ba27 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Fri, 12 May 2023 23:16:01 +0200
Subject: [PATCH 03/22] Add sharedfile subscribing support

---
 components/sharedfiles.js | 52 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/components/sharedfiles.js b/components/sharedfiles.js
index cb8f116..0d0a6b9 100644
--- a/components/sharedfiles.js
+++ b/components/sharedfiles.js
@@ -59,6 +59,58 @@ SteamCommunity.prototype.postSharedfileComment = function(userID, sid, message,
     }, "steamcommunity");
 };
 
+/**
+ * Subscribes to a sharedfile's comment section. Note: Checkbox on webpage does not update
+ * @param {SteamID | String} userID ID of the user associated to this sharedfile
+ * @param {String} sid ID of the sharedfileof
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+Bot.prototype.subscribeSharedfileComments = function(userID, sid, callback) {
+    if (typeof userID === "string") {
+        userID = new SteamID(userID);
+    }
+
+    this.httpRequestPost({
+        "uri": `https://steamcommunity.com/comment/PublishedFile_Public/subscribe/${userID.toString()}/${sid}/`,
+        "form": {
+            "count": 10,
+            "sessionid": this.getSessionID()
+        }
+    }, function(err, response, body) { // eslint-disable-line
+        if (!callback) {
+            return;
+        }
+
+        callback(null || err);
+    }, "steamcommunity");
+};
+
+/**
+ * Unsubscribes from a sharedfile's comment section. Note: Checkbox on webpage does not update
+ * @param {SteamID | String} userID - ID of the user associated to this sharedfile
+ * @param {String} sid - ID of the sharedfileof
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+Bot.prototype.unsubscribeSharedfileComments = function(userID, sid, callback) {
+    if (typeof userID === "string") {
+        userID = new SteamID(userID);
+    }
+
+    this.httpRequestPost({
+        "uri": `https://steamcommunity.com/comment/PublishedFile_Public/unsubscribe/${userID.toString()}/${sid}/`,
+        "form": {
+            "count": 10,
+            "sessionid": this.getSessionID()
+        }
+    }, function(err, response, body) { // eslint-disable-line
+        if (!callback) {
+            return;
+        }
+
+        callback(null || err);
+    }, "steamcommunity");
+};
+
 /**
  * Downvotes a sharedfile
  * @param {String} sid - ID of the sharedfile

From f09adc84a400f538c0a7967cbf416c40328c5ff0 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Fri, 12 May 2023 23:30:10 +0200
Subject: [PATCH 04/22] Add (disabled) sharedfile favorite support

---
 components/sharedfiles.js | 44 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/components/sharedfiles.js b/components/sharedfiles.js
index 0d0a6b9..13ac48a 100644
--- a/components/sharedfiles.js
+++ b/components/sharedfiles.js
@@ -31,6 +31,28 @@ SteamCommunity.prototype.deleteSharedfileComment = function(userID, sid, cid, ca
     }, "steamcommunity");
 };
 
+/**
+ * Favorites a sharedfile
+ * @param {String} sid - ID of the sharedfile
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+/* SteamCommunity.prototype.favoriteSharedfile = function(sid, callback) {
+    this.httpRequestPost({
+        "uri": "https://steamcommunity.com/sharedfiles/favorite",
+        "form": {
+            "id": sid,
+            "appid": , // TODO: How to get appid the sharedfile is associated to?
+            "sessionid": this.getSessionID()
+        }
+    }, function(err, response, body) {
+        if (!callback) {
+            return;
+        }
+
+        callback(null || err);
+    }, "steamcommunity");
+}; */
+
 /**
  * Posts a comment to a sharedfile
  * @param {SteamID | String} userID - ID of the user associated to this sharedfile
@@ -85,6 +107,28 @@ Bot.prototype.subscribeSharedfileComments = function(userID, sid, callback) {
     }, "steamcommunity");
 };
 
+/**
+ * Unfavorites a sharedfile
+ * @param {String} sid - ID of the sharedfile
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+/* SteamCommunity.prototype.unfavoriteSharedfile = function(sid, callback) {
+    this.httpRequestPost({
+        "uri": "https://steamcommunity.com/sharedfiles/unfavorite",
+        "form": {
+            "id": sid,
+            "appid": , // TODO: How to get appid the sharedfile is associated to?
+            "sessionid": this.getSessionID()
+        }
+    }, function(err, response, body) {
+        if (!callback) {
+            return;
+        }
+
+        callback(null || err);
+    }, "steamcommunity");
+}; */
+
 /**
  * Unsubscribes from a sharedfile's comment section. Note: Checkbox on webpage does not update
  * @param {SteamID | String} userID - ID of the user associated to this sharedfile

From b55516f67f953e3306903e705dac050004bae1a7 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Fri, 12 May 2023 23:46:31 +0200
Subject: [PATCH 05/22] Oops, wrong object name

---
 components/sharedfiles.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/components/sharedfiles.js b/components/sharedfiles.js
index 13ac48a..8670421 100644
--- a/components/sharedfiles.js
+++ b/components/sharedfiles.js
@@ -87,7 +87,7 @@ SteamCommunity.prototype.postSharedfileComment = function(userID, sid, message,
  * @param {String} sid ID of the sharedfileof
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
-Bot.prototype.subscribeSharedfileComments = function(userID, sid, callback) {
+SteamCommunity.prototype.subscribeSharedfileComments = function(userID, sid, callback) {
     if (typeof userID === "string") {
         userID = new SteamID(userID);
     }
@@ -135,7 +135,7 @@ Bot.prototype.subscribeSharedfileComments = function(userID, sid, callback) {
  * @param {String} sid - ID of the sharedfileof
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
-Bot.prototype.unsubscribeSharedfileComments = function(userID, sid, callback) {
+SteamCommunity.prototype.unsubscribeSharedfileComments = function(userID, sid, callback) {
     if (typeof userID === "string") {
         userID = new SteamID(userID);
     }

From c92801d25ee8c23f3956c0fc3d2b2ed6be5a17b4 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Sat, 13 May 2023 16:09:47 +0200
Subject: [PATCH 06/22] Load sharedfiles component

---
 index.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.js b/index.js
index 54d028d..10c58ba 100644
--- a/index.js
+++ b/index.js
@@ -573,6 +573,7 @@ require('./components/profile.js');
 require('./components/market.js');
 require('./components/groups.js');
 require('./components/users.js');
+require("./components/sharedfiles.js");
 require('./components/inventoryhistory.js');
 require('./components/webapi.js');
 require('./components/twofactor.js');

From c8642865a133a0b2e156d029655eaba3e9fde2b6 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Sun, 14 May 2023 14:50:32 +0200
Subject: [PATCH 07/22] Add sharedfile type enum

---
 resources/ESharedfileType.js | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 resources/ESharedfileType.js

diff --git a/resources/ESharedfileType.js b/resources/ESharedfileType.js
new file mode 100644
index 0000000..b8732ce
--- /dev/null
+++ b/resources/ESharedfileType.js
@@ -0,0 +1,13 @@
+/**
+ * @enum ESharedfileType
+ */
+module.exports = {
+    "Screenshot": 0,
+    "Artwork": 1,
+    "Guide": 2,
+
+    // Value-to-name mapping for convenience
+    "0": "Screenshot",
+    "1": "Artwork",
+    "2": "Guide"
+};
\ No newline at end of file

From 9ec5dcd7b0f8c44cce6ca2193bc93adbc571bac8 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Sun, 14 May 2023 15:00:45 +0200
Subject: [PATCH 08/22] Add sharedfile class with fully working scraper

---
 classes/CSteamSharedfile.js | 152 ++++++++++++++++++++++++++++++++++++
 package.json                |   1 +
 2 files changed, 153 insertions(+)
 create mode 100644 classes/CSteamSharedfile.js

diff --git a/classes/CSteamSharedfile.js b/classes/CSteamSharedfile.js
new file mode 100644
index 0000000..072996a
--- /dev/null
+++ b/classes/CSteamSharedfile.js
@@ -0,0 +1,152 @@
+const Cheerio = require("cheerio");
+const SteamID = require("steamid");
+const SteamCommunity  = require("../index.js");
+const steamIdResolver = require("steamid-resolver");
+const ESharedfileType = require("../resources/ESharedfileType.js");
+
+
+/**
+ * Scrape a sharedfile's DOM to get all available information
+ * @param {String} sid - ID of the sharedfile
+ * @param {function} callback -  First argument is null/Error, second is object containing all available information
+ */
+SteamCommunity.prototype.getSteamSharedfile = function(sid, callback) {
+    // Construct object holding all the data we can scrape
+    let sharedfile = {
+        id: sid,
+        type: null,
+        appID: null,
+        owner: null,
+        fileSize: null,
+        postDate: null,
+        resolution: null,
+        uniqueVisitorsCount: null,
+        favoritesCount: null,
+        upvoteCount: null
+    }
+
+
+    // Get DOM of sharedfile
+    this.httpRequestGet(`https://steamcommunity.com/sharedfiles/filedetails/?id=${sid}`, (err, res, body) => {
+        try {
+
+            /* --------------------- Preprocess output --------------------- */
+
+            // Load output into cheerio to make parsing easier
+            let $ = Cheerio.load(body);
+
+            // Dynamically map detailsStatsContainerLeft to detailsStatsContainerRight in an object to make readout easier. It holds size, post date and resolution.
+            let detailsStatsObj = {};
+            let detailsLeft     = $(".detailsStatsContainerLeft").children();
+            let detailsRight    = $(".detailsStatsContainerRight").children();
+
+            Object.keys(detailsLeft).forEach((e) => { // Dynamically get all details. Don't hardcore so that this also works for guides.
+                if (isNaN(e)) return; // Ignore invalid entries
+
+                detailsStatsObj[detailsLeft[e].children[0].data.trim()] = detailsRight[e].children[0].data;
+            });
+
+            // Dynamically map stats_table descriptions to values. This holds Unique Visitors and Current Favorites
+            let statsTableObj = {};
+            let statsTable = $(".stats_table").children();
+
+            Object.keys(statsTable).forEach((e, i) => {
+                if (isNaN(e)) return; // Ignore invalid entries
+
+                // Value description is at index 3, value data at index 1
+                statsTableObj[statsTable[e].children[3].children[0].data] = statsTable[e].children[1].children[0].data.replace(/,/g, ""); // Remove commas from 1k+ values
+            });
+
+
+            /* --------------------- Find and map values --------------------- */
+
+            // Find appID in share button onclick event
+            sharedfile.appID = Number($("#ShareItemBtn").attr()["onclick"].replace(`ShowSharePublishedFilePopup( '${sid}', '`, "").replace("' );", ""))
+
+
+            // Find owner profile link, convert to steamID64 using steamIdResolver lib and create a SteamID object
+            let ownerHref = $(".friendBlockLinkOverlay").attr()["href"];
+
+            steamIdResolver.customUrlToSteamID64(ownerHref, (err, steamID64) => { // Note: Callback will be called before this is done, takes around 1 sec to populate
+                if (!err) sharedfile.owner = new SteamID(steamID64);
+            });
+
+
+            // Find fileSize if not guide
+            sharedfile.fileSize = detailsStatsObj["File Size"] || null; // TODO: Convert to bytes? It seems like to always be MB but no guarantee
+
+            // Find postDate and convert to timestamp (Warning: Will get ugly)
+            let posted = detailsStatsObj["Posted"].replace(/,|@/g, "").split(" "); // Remove comma behind month and @, the date & time separator. Split by space to get: Day, Month, Year (if not current) and time
+            let months = { "Jan": "01", "Feb": "02", "Mar": "03", "Apr": "04", "May": "05", "Jun": "06", "Jul": "07", "Aug": "08", "Sep": "09", "Oct": "10", "Nov": "11", "Dec": "12" }; // Map all month abbreviations
+
+            if (posted[0].split(":")[0].length == 1) posted[0] = "0" + posted[0]; // Add zero if day is <10 to have a fixed length 
+
+            posted[1] = months[posted[1]]; // Replace month abbreviation with corresponding Number
+
+            if (!posted[2]) posted[2] = new Date().getUTCFullYear().toString(); // Add current year if Steam did not list one
+                else posted.splice(3, 1); // ...otherwise remove element 3 as it will be an empty String
+
+            // Convert AM/PM time to 24h format - Credit: https://stackoverflow.com/a/40197728 (Modified)
+            if (posted[3].split(":")[0].length == 1) posted[3] = "0" + posted[3]; // Add zero if hour is <10 to have a fixed length 
+
+            let time     = posted[3].substring(0, 5);
+            let modifier = posted[3].substring(5, 7);
+            let [hours, minutes] = time.split(":");
+            
+            if (hours === "12") hours = "00";
+            if (modifier === "pm") hours = parseInt(hours, 10) + 12;
+
+            sharedfile.postDate = Date.parse(`${posted[2]}-${posted[1]}-${posted[0]}T${hours}:${minutes}:00.000Z`); // Construct Date String and parse it to get Unix timestamp
+
+
+            // Find resolution if artwork or screenshot
+            sharedfile.resolution = detailsStatsObj["Size"] || null;
+
+
+            // Find uniqueVisitorsCount. We can't use ' || null' here as Number("0") casts to false
+            if (statsTableObj["Unique Visitors"]) sharedfile.uniqueVisitorsCount = Number(statsTableObj["Unique Visitors"]);
+
+
+            // Find favoritesCount. We can't use ' || null' here as Number("0") casts to false
+            if (statsTableObj["Current Favorites"]) sharedfile.favoritesCount = Number(statsTableObj["Current Favorites"]);
+
+
+            // Find upvoteCount. We can't use ' || null' here as Number("0") casts to false
+            let upvoteCount = $("#VotesUpCountContainer > #VotesUpCount").text();
+            if (upvoteCount) sharedfile.upvoteCount = Number(upvoteCount);
+
+
+            // Determine type by looking at the second breadcrumb. Find the first separator as it has a unique name and go to the next element which holds our value of interest
+            let breadcrumb = $(".breadcrumbs > .breadcrumb_separator").next().get(0).children[0].data || "";
+            
+            if (breadcrumb.includes("Screenshot")) sharedfile.type = ESharedfileType.Screenshot;
+            if (breadcrumb.includes("Artwork"))    sharedfile.type = ESharedfileType.Artwork;
+            if (breadcrumb.includes("Guide"))      sharedfile.type = ESharedfileType.Guide;
+
+
+            callback(null, new CSteamSharedfile(this, sharedfile));
+
+        } catch (err) {
+            callback(err, null);
+        }
+    });
+}
+
+
+function CSteamSharedfile(community, data) {
+    this._community = community;
+
+    // Clone all the data we recieved
+    Object.assign(this, data); // TODO: This is cleaner but might break IntelliSense
+
+    /* this.id = data.id;
+    this.type = data.type;
+    this.appID = data.appID;
+    this.owner = data.owner;
+    this.fileSize = data.fileSize;
+    this.postDate = data.postDate;
+    this.resolution = data.resolution;
+    this.uniqueVisitorsCount = data.uniqueVisitorsCount;
+    this.favoritesCount = data.favoritesCount;
+    this.upvoteCount = data.upvoteCount; */
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index afd186a..64709eb 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
 		"request": "^2.88.0",
 		"steam-totp": "^1.5.0",
 		"steamid": "^1.1.3",
+		"steamid-resolver": "^1.2.3",
 		"xml2js": "^0.4.22"
 	},
 	"engines": {

From d6b0fbd5d0a7914d5e88b4bb496c302546a017c8 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Sun, 14 May 2023 15:07:23 +0200
Subject: [PATCH 09/22] Update non-object methods to take appid param

---
 components/sharedfiles.js | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/components/sharedfiles.js b/components/sharedfiles.js
index 8670421..08f5c52 100644
--- a/components/sharedfiles.js
+++ b/components/sharedfiles.js
@@ -34,14 +34,15 @@ SteamCommunity.prototype.deleteSharedfileComment = function(userID, sid, cid, ca
 /**
  * Favorites a sharedfile
  * @param {String} sid - ID of the sharedfile
+ * @param {String} appid - ID of the app associated to this sharedfile
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
-/* SteamCommunity.prototype.favoriteSharedfile = function(sid, callback) {
+SteamCommunity.prototype.favoriteSharedfile = function(sid, appid, callback) {
     this.httpRequestPost({
         "uri": "https://steamcommunity.com/sharedfiles/favorite",
         "form": {
             "id": sid,
-            "appid": , // TODO: How to get appid the sharedfile is associated to?
+            "appid": appid,
             "sessionid": this.getSessionID()
         }
     }, function(err, response, body) {
@@ -51,7 +52,7 @@ SteamCommunity.prototype.deleteSharedfileComment = function(userID, sid, cid, ca
 
         callback(null || err);
     }, "steamcommunity");
-}; */
+};
 
 /**
  * Posts a comment to a sharedfile
@@ -110,14 +111,15 @@ SteamCommunity.prototype.subscribeSharedfileComments = function(userID, sid, cal
 /**
  * Unfavorites a sharedfile
  * @param {String} sid - ID of the sharedfile
+ * @param {String} appid - ID of the app associated to this sharedfile
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
-/* SteamCommunity.prototype.unfavoriteSharedfile = function(sid, callback) {
+SteamCommunity.prototype.unfavoriteSharedfile = function(sid, appid, callback) {
     this.httpRequestPost({
         "uri": "https://steamcommunity.com/sharedfiles/unfavorite",
         "form": {
             "id": sid,
-            "appid": , // TODO: How to get appid the sharedfile is associated to?
+            "appid": appid,
             "sessionid": this.getSessionID()
         }
     }, function(err, response, body) {
@@ -127,7 +129,7 @@ SteamCommunity.prototype.subscribeSharedfileComments = function(userID, sid, cal
 
         callback(null || err);
     }, "steamcommunity");
-}; */
+};
 
 /**
  * Unsubscribes from a sharedfile's comment section. Note: Checkbox on webpage does not update

From ba2782066d4a0b588b3a9c548585f2b2a79c16d5 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Sun, 14 May 2023 15:22:15 +0200
Subject: [PATCH 10/22] Misc

---
 classes/CSteamSharedfile.js | 5 ++---
 components/sharedfiles.js   | 5 ++---
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/classes/CSteamSharedfile.js b/classes/CSteamSharedfile.js
index 072996a..0094807 100644
--- a/classes/CSteamSharedfile.js
+++ b/classes/CSteamSharedfile.js
@@ -129,9 +129,8 @@ SteamCommunity.prototype.getSteamSharedfile = function(sid, callback) {
         } catch (err) {
             callback(err, null);
         }
-    });
-}
-
+    }, "steamcommunity");
+};
 
 function CSteamSharedfile(community, data) {
     this._community = community;
diff --git a/components/sharedfiles.js b/components/sharedfiles.js
index 08f5c52..be9fe3a 100644
--- a/components/sharedfiles.js
+++ b/components/sharedfiles.js
@@ -1,7 +1,6 @@
 var SteamCommunity = require('../index.js');
 var SteamID = require('steamid');
 
-// Note: a CSteamSharedfile class does not exist because we can't get data using the "normal" xml way to fill a CSteamSharedfile object
 
 /**
  * Deletes a comment from a sharedfile's comment section
@@ -85,7 +84,7 @@ SteamCommunity.prototype.postSharedfileComment = function(userID, sid, message,
 /**
  * Subscribes to a sharedfile's comment section. Note: Checkbox on webpage does not update
  * @param {SteamID | String} userID ID of the user associated to this sharedfile
- * @param {String} sid ID of the sharedfileof
+ * @param {String} sid ID of the sharedfile
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 SteamCommunity.prototype.subscribeSharedfileComments = function(userID, sid, callback) {
@@ -134,7 +133,7 @@ SteamCommunity.prototype.unfavoriteSharedfile = function(sid, appid, callback) {
 /**
  * Unsubscribes from a sharedfile's comment section. Note: Checkbox on webpage does not update
  * @param {SteamID | String} userID - ID of the user associated to this sharedfile
- * @param {String} sid - ID of the sharedfileof
+ * @param {String} sid - ID of the sharedfile
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 SteamCommunity.prototype.unsubscribeSharedfileComments = function(userID, sid, callback) {

From 350f6288e4f9796d6d123a1cf2c3d4ca10315e4e Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Sun, 14 May 2023 15:26:55 +0200
Subject: [PATCH 11/22] Add sharedfile object methods

---
 classes/CSteamSharedfile.js | 68 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 67 insertions(+), 1 deletion(-)

diff --git a/classes/CSteamSharedfile.js b/classes/CSteamSharedfile.js
index 0094807..40ccdfe 100644
--- a/classes/CSteamSharedfile.js
+++ b/classes/CSteamSharedfile.js
@@ -148,4 +148,70 @@ function CSteamSharedfile(community, data) {
     this.uniqueVisitorsCount = data.uniqueVisitorsCount;
     this.favoritesCount = data.favoritesCount;
     this.upvoteCount = data.upvoteCount; */
-}
\ No newline at end of file
+}
+
+/**
+ * Deletes a comment from this sharedfile's comment section
+ * @param {String} cid - ID of the comment to delete
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+CSteamSharedfile.prototype.deleteComment = function(cid, callback) {
+    this._community.deleteSharedfileComment(this.userID, this.sid, cid, callback);
+};
+
+/**
+ * Favorites this sharedfile
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+CSteamSharedfile.prototype.favorite = function(callback) {
+    this._community.favoriteSharedfile(this.sid, this.appID, callback);
+};
+
+/**
+ * Posts a comment to this sharedfile
+ * @param {String} message - Content of the comment to post
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+CSteamSharedfile.prototype.comment = function(message, callback) {
+    this._community.postSharedfileComment(this.owner, this.sid, message, callback);
+};
+
+/**
+ * Subscribes to this sharedfile's comment section. Note: Checkbox on webpage does not update
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+CSteamSharedfile.prototype.subscribe = function(callback) {
+    this._community.subscribeSharedfileComments(this.owner, this.sid, callback);
+};
+
+/**
+ * Unfavorites this sharedfile
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+CSteamSharedfile.prototype.unfavorite = function(callback) {
+    this._community.unfavoriteSharedfile(this.sid, this.appID, callback);
+};
+
+/**
+ * Unsubscribes from this sharedfile's comment section. Note: Checkbox on webpage does not update
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+CSteamSharedfile.prototype.unsubscribe = function(callback) {
+    this._community.unsubscribeSharedfileComments(this.owner, this.sid, callback);
+};
+
+/**
+ * Downvotes this sharedfile
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+CSteamSharedfile.prototype.voteDown = function(callback) {
+    this._community.voteDownSharedfile(this.sid, callback);
+};
+
+/**
+ * Upvotes this sharedfile
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+CSteamSharedfile.prototype.voteUp = function(callback) {
+    this._community.voteUpSharedfile(this.sid, callback);
+};
\ No newline at end of file

From c0be17cd612817a628f999de5a59a424c6bf3037 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Sun, 14 May 2023 15:47:49 +0200
Subject: [PATCH 12/22] Load CSteamSharedfile class

---
 index.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/index.js b/index.js
index 10c58ba..64e3756 100644
--- a/index.js
+++ b/index.js
@@ -573,7 +573,7 @@ require('./components/profile.js');
 require('./components/market.js');
 require('./components/groups.js');
 require('./components/users.js');
-require("./components/sharedfiles.js");
+require('./components/sharedfiles.js');
 require('./components/inventoryhistory.js');
 require('./components/webapi.js');
 require('./components/twofactor.js');
@@ -582,6 +582,7 @@ require('./components/help.js');
 require('./classes/CMarketItem.js');
 require('./classes/CMarketSearchResult.js');
 require('./classes/CSteamGroup.js');
+require('./classes/CSteamSharedfile.js');
 require('./classes/CSteamUser.js');
 
 /**

From 8538589fef6d3a7da7d2e1074e08beab28b0bc47 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Sun, 14 May 2023 16:12:59 +0200
Subject: [PATCH 13/22] Fix owner remaining null and wrong param

---
 classes/CSteamSharedfile.js | 34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/classes/CSteamSharedfile.js b/classes/CSteamSharedfile.js
index 40ccdfe..04ba9c0 100644
--- a/classes/CSteamSharedfile.js
+++ b/classes/CSteamSharedfile.js
@@ -64,14 +64,6 @@ SteamCommunity.prototype.getSteamSharedfile = function(sid, callback) {
             sharedfile.appID = Number($("#ShareItemBtn").attr()["onclick"].replace(`ShowSharePublishedFilePopup( '${sid}', '`, "").replace("' );", ""))
 
 
-            // Find owner profile link, convert to steamID64 using steamIdResolver lib and create a SteamID object
-            let ownerHref = $(".friendBlockLinkOverlay").attr()["href"];
-
-            steamIdResolver.customUrlToSteamID64(ownerHref, (err, steamID64) => { // Note: Callback will be called before this is done, takes around 1 sec to populate
-                if (!err) sharedfile.owner = new SteamID(steamID64);
-            });
-
-
             // Find fileSize if not guide
             sharedfile.fileSize = detailsStatsObj["File Size"] || null; // TODO: Convert to bytes? It seems like to always be MB but no guarantee
 
@@ -124,7 +116,15 @@ SteamCommunity.prototype.getSteamSharedfile = function(sid, callback) {
             if (breadcrumb.includes("Guide"))      sharedfile.type = ESharedfileType.Guide;
 
 
-            callback(null, new CSteamSharedfile(this, sharedfile));
+            // Find owner profile link, convert to steamID64 using steamIdResolver lib and create a SteamID object
+            let ownerHref = $(".friendBlockLinkOverlay").attr()["href"];
+
+            steamIdResolver.customUrlToSteamID64(ownerHref, (err, steamID64) => { // This request takes <1 sec
+                if (!err) sharedfile.owner = new SteamID(steamID64);
+
+                // Make callback when ID was resolved as otherwise owner will always be null
+                callback(null, new CSteamSharedfile(this, sharedfile));
+            });
 
         } catch (err) {
             callback(err, null);
@@ -156,7 +156,7 @@ function CSteamSharedfile(community, data) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.deleteComment = function(cid, callback) {
-    this._community.deleteSharedfileComment(this.userID, this.sid, cid, callback);
+    this._community.deleteSharedfileComment(this.userID, this.id, cid, callback);
 };
 
 /**
@@ -164,7 +164,7 @@ CSteamSharedfile.prototype.deleteComment = function(cid, callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.favorite = function(callback) {
-    this._community.favoriteSharedfile(this.sid, this.appID, callback);
+    this._community.favoriteSharedfile(this.id, this.appID, callback);
 };
 
 /**
@@ -173,7 +173,7 @@ CSteamSharedfile.prototype.favorite = function(callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.comment = function(message, callback) {
-    this._community.postSharedfileComment(this.owner, this.sid, message, callback);
+    this._community.postSharedfileComment(this.owner, this.id, message, callback);
 };
 
 /**
@@ -181,7 +181,7 @@ CSteamSharedfile.prototype.comment = function(message, callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.subscribe = function(callback) {
-    this._community.subscribeSharedfileComments(this.owner, this.sid, callback);
+    this._community.subscribeSharedfileComments(this.owner, this.id, callback);
 };
 
 /**
@@ -189,7 +189,7 @@ CSteamSharedfile.prototype.subscribe = function(callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.unfavorite = function(callback) {
-    this._community.unfavoriteSharedfile(this.sid, this.appID, callback);
+    this._community.unfavoriteSharedfile(this.id, this.appID, callback);
 };
 
 /**
@@ -197,7 +197,7 @@ CSteamSharedfile.prototype.unfavorite = function(callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.unsubscribe = function(callback) {
-    this._community.unsubscribeSharedfileComments(this.owner, this.sid, callback);
+    this._community.unsubscribeSharedfileComments(this.owner, this.id, callback);
 };
 
 /**
@@ -205,7 +205,7 @@ CSteamSharedfile.prototype.unsubscribe = function(callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.voteDown = function(callback) {
-    this._community.voteDownSharedfile(this.sid, callback);
+    this._community.voteDownSharedfile(this.id, callback);
 };
 
 /**
@@ -213,5 +213,5 @@ CSteamSharedfile.prototype.voteDown = function(callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.voteUp = function(callback) {
-    this._community.voteUpSharedfile(this.sid, callback);
+    this._community.voteUpSharedfile(this.id, callback);
 };
\ No newline at end of file

From 81d8dc2a5cefa0940bbc93cd6eb1bafbeec5a255 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Sun, 14 May 2023 16:27:09 +0200
Subject: [PATCH 14/22] Use decodeSteamTime() helper instead of doing it
 manually

---
 classes/CSteamSharedfile.js | 30 +++++++-----------------------
 1 file changed, 7 insertions(+), 23 deletions(-)

diff --git a/classes/CSteamSharedfile.js b/classes/CSteamSharedfile.js
index 04ba9c0..de9db50 100644
--- a/classes/CSteamSharedfile.js
+++ b/classes/CSteamSharedfile.js
@@ -1,7 +1,8 @@
 const Cheerio = require("cheerio");
 const SteamID = require("steamid");
+const Helpers = require('../components/helpers.js');
 const SteamCommunity  = require("../index.js");
-const steamIdResolver = require("steamid-resolver");
+const SteamIdResolver = require("steamid-resolver");
 const ESharedfileType = require("../resources/ESharedfileType.js");
 
 
@@ -67,28 +68,11 @@ SteamCommunity.prototype.getSteamSharedfile = function(sid, callback) {
             // Find fileSize if not guide
             sharedfile.fileSize = detailsStatsObj["File Size"] || null; // TODO: Convert to bytes? It seems like to always be MB but no guarantee
 
-            // Find postDate and convert to timestamp (Warning: Will get ugly)
-            let posted = detailsStatsObj["Posted"].replace(/,|@/g, "").split(" "); // Remove comma behind month and @, the date & time separator. Split by space to get: Day, Month, Year (if not current) and time
-            let months = { "Jan": "01", "Feb": "02", "Mar": "03", "Apr": "04", "May": "05", "Jun": "06", "Jul": "07", "Aug": "08", "Sep": "09", "Oct": "10", "Nov": "11", "Dec": "12" }; // Map all month abbreviations
 
-            if (posted[0].split(":")[0].length == 1) posted[0] = "0" + posted[0]; // Add zero if day is <10 to have a fixed length 
+            // Find postDate and convert to timestamp
+            let posted = detailsStatsObj["Posted"].trim();
 
-            posted[1] = months[posted[1]]; // Replace month abbreviation with corresponding Number
-
-            if (!posted[2]) posted[2] = new Date().getUTCFullYear().toString(); // Add current year if Steam did not list one
-                else posted.splice(3, 1); // ...otherwise remove element 3 as it will be an empty String
-
-            // Convert AM/PM time to 24h format - Credit: https://stackoverflow.com/a/40197728 (Modified)
-            if (posted[3].split(":")[0].length == 1) posted[3] = "0" + posted[3]; // Add zero if hour is <10 to have a fixed length 
-
-            let time     = posted[3].substring(0, 5);
-            let modifier = posted[3].substring(5, 7);
-            let [hours, minutes] = time.split(":");
-            
-            if (hours === "12") hours = "00";
-            if (modifier === "pm") hours = parseInt(hours, 10) + 12;
-
-            sharedfile.postDate = Date.parse(`${posted[2]}-${posted[1]}-${posted[0]}T${hours}:${minutes}:00.000Z`); // Construct Date String and parse it to get Unix timestamp
+            sharedfile.postDate = Date.parse(Helpers.decodeSteamTime(posted)); // Pass String into helper and parse the returned String to get a Unix timestamp
 
 
             // Find resolution if artwork or screenshot
@@ -116,10 +100,10 @@ SteamCommunity.prototype.getSteamSharedfile = function(sid, callback) {
             if (breadcrumb.includes("Guide"))      sharedfile.type = ESharedfileType.Guide;
 
 
-            // Find owner profile link, convert to steamID64 using steamIdResolver lib and create a SteamID object
+            // Find owner profile link, convert to steamID64 using SteamIdResolver lib and create a SteamID object
             let ownerHref = $(".friendBlockLinkOverlay").attr()["href"];
 
-            steamIdResolver.customUrlToSteamID64(ownerHref, (err, steamID64) => { // This request takes <1 sec
+            SteamIdResolver.customUrlToSteamID64(ownerHref, (err, steamID64) => { // This request takes <1 sec
                 if (!err) sharedfile.owner = new SteamID(steamID64);
 
                 // Make callback when ID was resolved as otherwise owner will always be null

From 897ad161544f80fb2a4d01a7b8bb34b9b0a41071 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Sun, 14 May 2023 18:52:16 +0200
Subject: [PATCH 15/22] Formatting

---
 classes/CSteamSharedfile.js  | 212 ++++++++++++++++++---------------
 components/sharedfiles.js    | 224 +++++++++++++++++------------------
 resources/ESharedfileType.js |  14 +--
 3 files changed, 236 insertions(+), 214 deletions(-)

diff --git a/classes/CSteamSharedfile.js b/classes/CSteamSharedfile.js
index de9db50..8ed43ad 100644
--- a/classes/CSteamSharedfile.js
+++ b/classes/CSteamSharedfile.js
@@ -1,9 +1,9 @@
-const Cheerio = require("cheerio");
-const SteamID = require("steamid");
+const Cheerio = require('cheerio');
+const SteamID = require('steamid');
 const Helpers = require('../components/helpers.js');
-const SteamCommunity  = require("../index.js");
-const SteamIdResolver = require("steamid-resolver");
-const ESharedfileType = require("../resources/ESharedfileType.js");
+const SteamCommunity  = require('../index.js');
+const SteamIdResolver = require('steamid-resolver');
+const ESharedfileType = require('../resources/ESharedfileType.js');
 
 
 /**
@@ -12,126 +12,148 @@ const ESharedfileType = require("../resources/ESharedfileType.js");
  * @param {function} callback -  First argument is null/Error, second is object containing all available information
  */
 SteamCommunity.prototype.getSteamSharedfile = function(sid, callback) {
-    // Construct object holding all the data we can scrape
-    let sharedfile = {
-        id: sid,
-        type: null,
-        appID: null,
-        owner: null,
-        fileSize: null,
-        postDate: null,
-        resolution: null,
-        uniqueVisitorsCount: null,
-        favoritesCount: null,
-        upvoteCount: null
-    }
+
+	// Construct object holding all the data we can scrape
+	let sharedfile = {
+		id: sid,
+		type: null,
+		appID: null,
+		owner: null,
+		fileSize: null,
+		postDate: null,
+		resolution: null,
+		uniqueVisitorsCount: null,
+		favoritesCount: null,
+		upvoteCount: null
+	};
 
 
-    // Get DOM of sharedfile
-    this.httpRequestGet(`https://steamcommunity.com/sharedfiles/filedetails/?id=${sid}`, (err, res, body) => {
-        try {
+	// Get DOM of sharedfile
+	this.httpRequestGet(`https://steamcommunity.com/sharedfiles/filedetails/?id=${sid}`, (err, res, body) => {
+		try {
 
-            /* --------------------- Preprocess output --------------------- */
+			/* --------------------- Preprocess output --------------------- */
 
-            // Load output into cheerio to make parsing easier
-            let $ = Cheerio.load(body);
+			// Load output into cheerio to make parsing easier
+			let $ = Cheerio.load(body);
 
-            // Dynamically map detailsStatsContainerLeft to detailsStatsContainerRight in an object to make readout easier. It holds size, post date and resolution.
-            let detailsStatsObj = {};
-            let detailsLeft     = $(".detailsStatsContainerLeft").children();
-            let detailsRight    = $(".detailsStatsContainerRight").children();
+			// Dynamically map detailsStatsContainerLeft to detailsStatsContainerRight in an object to make readout easier. It holds size, post date and resolution.
+			let detailsStatsObj = {};
+			let detailsLeft     = $(".detailsStatsContainerLeft").children();
+			let detailsRight    = $(".detailsStatsContainerRight").children();
 
-            Object.keys(detailsLeft).forEach((e) => { // Dynamically get all details. Don't hardcore so that this also works for guides.
-                if (isNaN(e)) return; // Ignore invalid entries
+			Object.keys(detailsLeft).forEach((e) => { // Dynamically get all details. Don't hardcore so that this also works for guides.
+				if (isNaN(e)) {
+					return; // Ignore invalid entries
+				}
 
-                detailsStatsObj[detailsLeft[e].children[0].data.trim()] = detailsRight[e].children[0].data;
-            });
+				detailsStatsObj[detailsLeft[e].children[0].data.trim()] = detailsRight[e].children[0].data;
+			});
 
-            // Dynamically map stats_table descriptions to values. This holds Unique Visitors and Current Favorites
-            let statsTableObj = {};
-            let statsTable = $(".stats_table").children();
+			// Dynamically map stats_table descriptions to values. This holds Unique Visitors and Current Favorites
+			let statsTableObj = {};
+			let statsTable    = $(".stats_table").children();
 
-            Object.keys(statsTable).forEach((e, i) => {
-                if (isNaN(e)) return; // Ignore invalid entries
+			Object.keys(statsTable).forEach((e) => {
+				if (isNaN(e)) {
+					return; // Ignore invalid entries
+				}
 
-                // Value description is at index 3, value data at index 1
-                statsTableObj[statsTable[e].children[3].children[0].data] = statsTable[e].children[1].children[0].data.replace(/,/g, ""); // Remove commas from 1k+ values
-            });
+				// Value description is at index 3, value data at index 1
+				statsTableObj[statsTable[e].children[3].children[0].data] = statsTable[e].children[1].children[0].data.replace(/,/g, ""); // Remove commas from 1k+ values
+			});
 
 
-            /* --------------------- Find and map values --------------------- */
+			/* --------------------- Find and map values --------------------- */
 
-            // Find appID in share button onclick event
-            sharedfile.appID = Number($("#ShareItemBtn").attr()["onclick"].replace(`ShowSharePublishedFilePopup( '${sid}', '`, "").replace("' );", ""))
+			// Find appID in share button onclick event
+			sharedfile.appID = Number($("#ShareItemBtn").attr()["onclick"].replace(`ShowSharePublishedFilePopup( '${sid}', '`, "").replace("' );", ""));
 
 
-            // Find fileSize if not guide
-            sharedfile.fileSize = detailsStatsObj["File Size"] || null; // TODO: Convert to bytes? It seems like to always be MB but no guarantee
+			// Find fileSize if not guide
+			sharedfile.fileSize = detailsStatsObj["File Size"] || null; // TODO: Convert to bytes? It seems like to always be MB but no guarantee
 
 
-            // Find postDate and convert to timestamp
-            let posted = detailsStatsObj["Posted"].trim();
+			// Find postDate and convert to timestamp
+			let posted = detailsStatsObj["Posted"].trim();
 
-            sharedfile.postDate = Date.parse(Helpers.decodeSteamTime(posted)); // Pass String into helper and parse the returned String to get a Unix timestamp
+			sharedfile.postDate = Date.parse(Helpers.decodeSteamTime(posted)); // Pass String into helper and parse the returned String to get a Unix timestamp
 
 
-            // Find resolution if artwork or screenshot
-            sharedfile.resolution = detailsStatsObj["Size"] || null;
+			// Find resolution if artwork or screenshot
+			sharedfile.resolution = detailsStatsObj["Size"] || null;
 
 
-            // Find uniqueVisitorsCount. We can't use ' || null' here as Number("0") casts to false
-            if (statsTableObj["Unique Visitors"]) sharedfile.uniqueVisitorsCount = Number(statsTableObj["Unique Visitors"]);
+			// Find uniqueVisitorsCount. We can't use ' || null' here as Number("0") casts to false
+			if (statsTableObj["Unique Visitors"]) {
+				sharedfile.uniqueVisitorsCount = Number(statsTableObj["Unique Visitors"]);
+			}
 
 
-            // Find favoritesCount. We can't use ' || null' here as Number("0") casts to false
-            if (statsTableObj["Current Favorites"]) sharedfile.favoritesCount = Number(statsTableObj["Current Favorites"]);
+			// Find favoritesCount. We can't use ' || null' here as Number("0") casts to false
+			if (statsTableObj["Current Favorites"]) {
+				sharedfile.favoritesCount = Number(statsTableObj["Current Favorites"]);
+			}
 
 
-            // Find upvoteCount. We can't use ' || null' here as Number("0") casts to false
-            let upvoteCount = $("#VotesUpCountContainer > #VotesUpCount").text();
-            if (upvoteCount) sharedfile.upvoteCount = Number(upvoteCount);
+			// Find upvoteCount. We can't use ' || null' here as Number("0") casts to false
+			let upvoteCount = $("#VotesUpCountContainer > #VotesUpCount").text();
+
+			if (upvoteCount) {
+				sharedfile.upvoteCount = Number(upvoteCount);
+			}
 
 
-            // Determine type by looking at the second breadcrumb. Find the first separator as it has a unique name and go to the next element which holds our value of interest
-            let breadcrumb = $(".breadcrumbs > .breadcrumb_separator").next().get(0).children[0].data || "";
-            
-            if (breadcrumb.includes("Screenshot")) sharedfile.type = ESharedfileType.Screenshot;
-            if (breadcrumb.includes("Artwork"))    sharedfile.type = ESharedfileType.Artwork;
-            if (breadcrumb.includes("Guide"))      sharedfile.type = ESharedfileType.Guide;
+			// Determine type by looking at the second breadcrumb. Find the first separator as it has a unique name and go to the next element which holds our value of interest
+			let breadcrumb = $(".breadcrumbs > .breadcrumb_separator").next().get(0).children[0].data || "";
+
+			if (breadcrumb.includes("Screenshot")) {
+				sharedfile.type = ESharedfileType.Screenshot;
+			}
+
+			if (breadcrumb.includes("Artwork")) {
+				sharedfile.type = ESharedfileType.Artwork;
+			}
+
+			if (breadcrumb.includes("Guide")) {
+				sharedfile.type = ESharedfileType.Guide;
+			}
 
 
-            // Find owner profile link, convert to steamID64 using SteamIdResolver lib and create a SteamID object
-            let ownerHref = $(".friendBlockLinkOverlay").attr()["href"];
+			// Find owner profile link, convert to steamID64 using SteamIdResolver lib and create a SteamID object
+			let ownerHref = $(".friendBlockLinkOverlay").attr()["href"];
 
-            SteamIdResolver.customUrlToSteamID64(ownerHref, (err, steamID64) => { // This request takes <1 sec
-                if (!err) sharedfile.owner = new SteamID(steamID64);
+			SteamIdResolver.customUrlToSteamID64(ownerHref, (err, steamID64) => { // This request takes <1 sec
+				if (!err) {
+					sharedfile.owner = new SteamID(steamID64);
+				}
 
-                // Make callback when ID was resolved as otherwise owner will always be null
-                callback(null, new CSteamSharedfile(this, sharedfile));
-            });
+				// Make callback when ID was resolved as otherwise owner will always be null
+				callback(null, new CSteamSharedfile(this, sharedfile));
+			});
 
-        } catch (err) {
-            callback(err, null);
-        }
-    }, "steamcommunity");
+		} catch (err) {
+			callback(err, null);
+		}
+	}, "steamcommunity");
 };
 
 function CSteamSharedfile(community, data) {
-    this._community = community;
+	this._community = community;
 
-    // Clone all the data we recieved
-    Object.assign(this, data); // TODO: This is cleaner but might break IntelliSense
+	// Clone all the data we recieved
+	Object.assign(this, data); // TODO: This is cleaner but might break IntelliSense. I'm leaving the block below to be reactivated if necessary
 
-    /* this.id = data.id;
-    this.type = data.type;
-    this.appID = data.appID;
-    this.owner = data.owner;
-    this.fileSize = data.fileSize;
-    this.postDate = data.postDate;
-    this.resolution = data.resolution;
-    this.uniqueVisitorsCount = data.uniqueVisitorsCount;
-    this.favoritesCount = data.favoritesCount;
-    this.upvoteCount = data.upvoteCount; */
+	/* this.id = data.id;
+	this.type = data.type;
+	this.appID = data.appID;
+	this.owner = data.owner;
+	this.fileSize = data.fileSize;
+	this.postDate = data.postDate;
+	this.resolution = data.resolution;
+	this.uniqueVisitorsCount = data.uniqueVisitorsCount;
+	this.favoritesCount = data.favoritesCount;
+	this.upvoteCount = data.upvoteCount; */
 }
 
 /**
@@ -140,7 +162,7 @@ function CSteamSharedfile(community, data) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.deleteComment = function(cid, callback) {
-    this._community.deleteSharedfileComment(this.userID, this.id, cid, callback);
+	this._community.deleteSharedfileComment(this.userID, this.id, cid, callback);
 };
 
 /**
@@ -148,7 +170,7 @@ CSteamSharedfile.prototype.deleteComment = function(cid, callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.favorite = function(callback) {
-    this._community.favoriteSharedfile(this.id, this.appID, callback);
+	this._community.favoriteSharedfile(this.id, this.appID, callback);
 };
 
 /**
@@ -157,7 +179,7 @@ CSteamSharedfile.prototype.favorite = function(callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.comment = function(message, callback) {
-    this._community.postSharedfileComment(this.owner, this.id, message, callback);
+	this._community.postSharedfileComment(this.owner, this.id, message, callback);
 };
 
 /**
@@ -165,7 +187,7 @@ CSteamSharedfile.prototype.comment = function(message, callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.subscribe = function(callback) {
-    this._community.subscribeSharedfileComments(this.owner, this.id, callback);
+	this._community.subscribeSharedfileComments(this.owner, this.id, callback);
 };
 
 /**
@@ -173,7 +195,7 @@ CSteamSharedfile.prototype.subscribe = function(callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.unfavorite = function(callback) {
-    this._community.unfavoriteSharedfile(this.id, this.appID, callback);
+	this._community.unfavoriteSharedfile(this.id, this.appID, callback);
 };
 
 /**
@@ -181,7 +203,7 @@ CSteamSharedfile.prototype.unfavorite = function(callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.unsubscribe = function(callback) {
-    this._community.unsubscribeSharedfileComments(this.owner, this.id, callback);
+	this._community.unsubscribeSharedfileComments(this.owner, this.id, callback);
 };
 
 /**
@@ -189,7 +211,7 @@ CSteamSharedfile.prototype.unsubscribe = function(callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.voteDown = function(callback) {
-    this._community.voteDownSharedfile(this.id, callback);
+	this._community.voteDownSharedfile(this.id, callback);
 };
 
 /**
@@ -197,5 +219,5 @@ CSteamSharedfile.prototype.voteDown = function(callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 CSteamSharedfile.prototype.voteUp = function(callback) {
-    this._community.voteUpSharedfile(this.id, callback);
+	this._community.voteUpSharedfile(this.id, callback);
 };
\ No newline at end of file
diff --git a/components/sharedfiles.js b/components/sharedfiles.js
index be9fe3a..3ef690a 100644
--- a/components/sharedfiles.js
+++ b/components/sharedfiles.js
@@ -10,24 +10,24 @@ var SteamID = require('steamid');
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 SteamCommunity.prototype.deleteSharedfileComment = function(userID, sid, cid, callback) {
-    if (typeof userID === "string") {
-        userID = new SteamID(userID);
-    }
+	if (typeof userID === "string") {
+		userID = new SteamID(userID);
+	}
 
-    this.httpRequestPost({
-        "uri": `https://steamcommunity.com/comment/PublishedFile_Public/delete/${userID.toString()}/${sid}/`,
-        "form": {
-            "gidcomment": cid,
-            "count": 10,
-            "sessionid": this.getSessionID()
-        }
-    }, function(err, response, body) {
-        if (!callback) {
-            return;
-        }
+	this.httpRequestPost({
+		"uri": `https://steamcommunity.com/comment/PublishedFile_Public/delete/${userID.toString()}/${sid}/`,
+		"form": {
+			"gidcomment": cid,
+			"count": 10,
+			"sessionid": this.getSessionID()
+		}
+	}, function(err, response, body) {
+		if (!callback) {
+			return;
+		}
 
-        callback(null || err);
-    }, "steamcommunity");
+		callback(null || err);
+	}, "steamcommunity");
 };
 
 /**
@@ -37,20 +37,20 @@ SteamCommunity.prototype.deleteSharedfileComment = function(userID, sid, cid, ca
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 SteamCommunity.prototype.favoriteSharedfile = function(sid, appid, callback) {
-    this.httpRequestPost({
-        "uri": "https://steamcommunity.com/sharedfiles/favorite",
-        "form": {
-            "id": sid,
-            "appid": appid,
-            "sessionid": this.getSessionID()
-        }
-    }, function(err, response, body) {
-        if (!callback) {
-            return;
-        }
+	this.httpRequestPost({
+		"uri": "https://steamcommunity.com/sharedfiles/favorite",
+		"form": {
+			"id": sid,
+			"appid": appid,
+			"sessionid": this.getSessionID()
+		}
+	}, function(err, response, body) {
+		if (!callback) {
+			return;
+		}
 
-        callback(null || err);
-    }, "steamcommunity");
+		callback(null || err);
+	}, "steamcommunity");
 };
 
 /**
@@ -61,24 +61,24 @@ SteamCommunity.prototype.favoriteSharedfile = function(sid, appid, callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 SteamCommunity.prototype.postSharedfileComment = function(userID, sid, message, callback) {
-    if (typeof userID === "string") {
-        userID = new SteamID(userID);
-    }
+	if (typeof userID === "string") {
+		userID = new SteamID(userID);
+	}
 
-    this.httpRequestPost({
-        "uri": `https://steamcommunity.com/comment/PublishedFile_Public/post/${userID.toString()}/${sid}/`,
-        "form": {
-            "comment": message,
-            "count": 10,
-            "sessionid": this.getSessionID()
-        }
-    }, function(err, response, body) {
-        if (!callback) {
-            return;
-        }
+	this.httpRequestPost({
+		"uri": `https://steamcommunity.com/comment/PublishedFile_Public/post/${userID.toString()}/${sid}/`,
+		"form": {
+			"comment": message,
+			"count": 10,
+			"sessionid": this.getSessionID()
+		}
+	}, function(err, response, body) {
+		if (!callback) {
+			return;
+		}
 
-        callback(null || err);
-    }, "steamcommunity");
+		callback(null || err);
+	}, "steamcommunity");
 };
 
 /**
@@ -88,23 +88,23 @@ SteamCommunity.prototype.postSharedfileComment = function(userID, sid, message,
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 SteamCommunity.prototype.subscribeSharedfileComments = function(userID, sid, callback) {
-    if (typeof userID === "string") {
-        userID = new SteamID(userID);
-    }
+	if (typeof userID === "string") {
+		userID = new SteamID(userID);
+	}
 
-    this.httpRequestPost({
-        "uri": `https://steamcommunity.com/comment/PublishedFile_Public/subscribe/${userID.toString()}/${sid}/`,
-        "form": {
-            "count": 10,
-            "sessionid": this.getSessionID()
-        }
-    }, function(err, response, body) { // eslint-disable-line
-        if (!callback) {
-            return;
-        }
+	this.httpRequestPost({
+		"uri": `https://steamcommunity.com/comment/PublishedFile_Public/subscribe/${userID.toString()}/${sid}/`,
+		"form": {
+			"count": 10,
+			"sessionid": this.getSessionID()
+		}
+	}, function(err, response, body) { // eslint-disable-line
+		if (!callback) {
+			return;
+		}
 
-        callback(null || err);
-    }, "steamcommunity");
+		callback(null || err);
+	}, "steamcommunity");
 };
 
 /**
@@ -114,20 +114,20 @@ SteamCommunity.prototype.subscribeSharedfileComments = function(userID, sid, cal
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 SteamCommunity.prototype.unfavoriteSharedfile = function(sid, appid, callback) {
-    this.httpRequestPost({
-        "uri": "https://steamcommunity.com/sharedfiles/unfavorite",
-        "form": {
-            "id": sid,
-            "appid": appid,
-            "sessionid": this.getSessionID()
-        }
-    }, function(err, response, body) {
-        if (!callback) {
-            return;
-        }
+	this.httpRequestPost({
+		"uri": "https://steamcommunity.com/sharedfiles/unfavorite",
+		"form": {
+			"id": sid,
+			"appid": appid,
+			"sessionid": this.getSessionID()
+		}
+	}, function(err, response, body) {
+		if (!callback) {
+			return;
+		}
 
-        callback(null || err);
-    }, "steamcommunity");
+		callback(null || err);
+	}, "steamcommunity");
 };
 
 /**
@@ -137,23 +137,23 @@ SteamCommunity.prototype.unfavoriteSharedfile = function(sid, appid, callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 SteamCommunity.prototype.unsubscribeSharedfileComments = function(userID, sid, callback) {
-    if (typeof userID === "string") {
-        userID = new SteamID(userID);
-    }
+	if (typeof userID === "string") {
+		userID = new SteamID(userID);
+	}
 
-    this.httpRequestPost({
-        "uri": `https://steamcommunity.com/comment/PublishedFile_Public/unsubscribe/${userID.toString()}/${sid}/`,
-        "form": {
-            "count": 10,
-            "sessionid": this.getSessionID()
-        }
-    }, function(err, response, body) { // eslint-disable-line
-        if (!callback) {
-            return;
-        }
+	this.httpRequestPost({
+		"uri": `https://steamcommunity.com/comment/PublishedFile_Public/unsubscribe/${userID.toString()}/${sid}/`,
+		"form": {
+			"count": 10,
+			"sessionid": this.getSessionID()
+		}
+	}, function(err, response, body) { // eslint-disable-line
+		if (!callback) {
+			return;
+		}
 
-        callback(null || err);
-    }, "steamcommunity");
+		callback(null || err);
+	}, "steamcommunity");
 };
 
 /**
@@ -162,19 +162,19 @@ SteamCommunity.prototype.unsubscribeSharedfileComments = function(userID, sid, c
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 SteamCommunity.prototype.voteDownSharedfile = function(sid, callback) {
-    this.httpRequestPost({
-        "uri": "https://steamcommunity.com/sharedfiles/votedown",
-        "form": {
-            "id": sid,
-            "sessionid": this.getSessionID()
-        }
-    }, function(err, response, body) {
-        if (!callback) {
-            return;
-        }
+	this.httpRequestPost({
+		"uri": "https://steamcommunity.com/sharedfiles/votedown",
+		"form": {
+			"id": sid,
+			"sessionid": this.getSessionID()
+		}
+	}, function(err, response, body) {
+		if (!callback) {
+			return;
+		}
 
-        callback(null || err);
-    }, "steamcommunity");
+		callback(null || err);
+	}, "steamcommunity");
 };
 
 /**
@@ -183,17 +183,17 @@ SteamCommunity.prototype.voteDownSharedfile = function(sid, callback) {
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
 SteamCommunity.prototype.voteUpSharedfile = function(sid, callback) {
-    this.httpRequestPost({
-        "uri": "https://steamcommunity.com/sharedfiles/voteup",
-        "form": {
-            "id": sid,
-            "sessionid": this.getSessionID()
-        }
-    }, function(err, response, body) {
-        if (!callback) {
-            return;
-        }
+	this.httpRequestPost({
+		"uri": "https://steamcommunity.com/sharedfiles/voteup",
+		"form": {
+			"id": sid,
+			"sessionid": this.getSessionID()
+		}
+	}, function(err, response, body) {
+		if (!callback) {
+			return;
+		}
 
-        callback(null || err);
-    }, "steamcommunity");
+		callback(null || err);
+	}, "steamcommunity");
 };
\ No newline at end of file
diff --git a/resources/ESharedfileType.js b/resources/ESharedfileType.js
index b8732ce..fc528d5 100644
--- a/resources/ESharedfileType.js
+++ b/resources/ESharedfileType.js
@@ -2,12 +2,12 @@
  * @enum ESharedfileType
  */
 module.exports = {
-    "Screenshot": 0,
-    "Artwork": 1,
-    "Guide": 2,
+	"Screenshot": 0,
+	"Artwork": 1,
+	"Guide": 2,
 
-    // Value-to-name mapping for convenience
-    "0": "Screenshot",
-    "1": "Artwork",
-    "2": "Guide"
+	// Value-to-name mapping for convenience
+	"0": "Screenshot",
+	"1": "Artwork",
+	"2": "Guide"
 };
\ No newline at end of file

From 4723bd93a61a8aae0cf725d8d452fab3577173a3 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Mon, 15 May 2023 22:27:37 +0200
Subject: [PATCH 16/22] Improve resolveVanityURL() and move to helpers

---
 components/helpers.js          | 40 ++++++++++++++++++++++++++++++++++
 components/inventoryhistory.js | 19 ++--------------
 2 files changed, 42 insertions(+), 17 deletions(-)

diff --git a/components/helpers.js b/components/helpers.js
index 1da5443..b354da5 100644
--- a/components/helpers.js
+++ b/components/helpers.js
@@ -1,4 +1,6 @@
 const EResult = require('../resources/EResult.js');
+const request = require('request');
+const xml2js  = require('xml2js');
 
 exports.isSteamID = function(input) {
 	var keys = Object.keys(input);
@@ -54,3 +56,41 @@ exports.eresultError = function(eresult) {
 	err.eresult = eresult;
 	return err;
 };
+
+/**
+ * Resolves a Steam profile URL to get steamID64 and vanityURL
+ * @param {String} url - Full steamcommunity profile URL or only the vanity part.
+ * @param {Object} callback - First argument is null/Error, second is object containing vanityURL (String) and steamID (String)
+ */
+exports.resolveVanityURL = function(url, callback) {
+	// Precede url param if only the vanity was provided
+	if (!url.includes("steamcommunity.com")) {
+		url = "https://steamcommunity.com/id/" + url;
+	}
+
+	// Make request to get XML data
+	request(url + "/?xml=1", function(err, response, body) {
+		if (err) {
+			callback(err);
+			return;
+		}
+
+		// Parse XML data returned from Steam into an object
+		new xml2js.Parser().parseString(body, (err, parsed) => {
+			if (err) {
+				callback(new Error("Couldn't parse XML response"));
+				return;
+			}
+
+			if (parsed.response && parsed.response.error) {
+				callback(new Error("Couldn't find Steam ID"));
+				return;
+			}
+
+			let steamID64 = parsed.profile.steamID64;
+			let vanityURL = parsed.profile.customURL;
+
+			callback(null, {"vanityURL": vanityURL, "steamID": steamID64});
+		});
+	});
+};
\ No newline at end of file
diff --git a/components/inventoryhistory.js b/components/inventoryhistory.js
index fd7c522..961d9c1 100644
--- a/components/inventoryhistory.js
+++ b/components/inventoryhistory.js
@@ -1,5 +1,6 @@
 var SteamCommunity = require('../index.js');
 var CEconItem = require('../classes/CEconItem.js');
+var Helpers = require('./helpers.js');
 var SteamID = require('steamid');
 var request = require('request');
 var Cheerio = require('cheerio');
@@ -142,7 +143,7 @@ SteamCommunity.prototype.getInventoryHistory = function(options, callback) {
 		}
 
 		if (options.resolveVanityURLs) {
-			Async.map(vanityURLs, resolveVanityURL, function(err, results) {
+			Async.map(vanityURLs, Helpers.resolveVanityURL, function(err, results) {
 				if (err) {
 					callback(err);
 					return;
@@ -170,19 +171,3 @@ SteamCommunity.prototype.getInventoryHistory = function(options, callback) {
 	}, "steamcommunity");
 };
 
-function resolveVanityURL(vanityURL, callback) {
-	request("https://steamcommunity.com/id/" + vanityURL + "/?xml=1", function(err, response, body) {
-		if (err) {
-			callback(err);
-			return;
-		}
-
-		var match = body.match(/<steamID64>(\d+)<\/steamID64>/);
-		if (!match || !match[1]) {
-			callback(new Error("Couldn't find Steam ID"));
-			return;
-		}
-
-		callback(null, {"vanityURL": vanityURL, "steamID": match[1]});
-	});
-}

From 27d2daac61113dddef2ece07bc4a58a72f6ec12e Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Mon, 15 May 2023 22:35:39 +0200
Subject: [PATCH 17/22] Remove steamid-resolver dep and use internal helper

---
 classes/CSteamSharedfile.js | 7 +++----
 package.json                | 1 -
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/classes/CSteamSharedfile.js b/classes/CSteamSharedfile.js
index 8ed43ad..8401cd6 100644
--- a/classes/CSteamSharedfile.js
+++ b/classes/CSteamSharedfile.js
@@ -2,14 +2,13 @@ const Cheerio = require('cheerio');
 const SteamID = require('steamid');
 const Helpers = require('../components/helpers.js');
 const SteamCommunity  = require('../index.js');
-const SteamIdResolver = require('steamid-resolver');
 const ESharedfileType = require('../resources/ESharedfileType.js');
 
 
 /**
  * Scrape a sharedfile's DOM to get all available information
  * @param {String} sid - ID of the sharedfile
- * @param {function} callback -  First argument is null/Error, second is object containing all available information
+ * @param {function} callback - First argument is null/Error, second is object containing all available information
  */
 SteamCommunity.prototype.getSteamSharedfile = function(sid, callback) {
 
@@ -123,9 +122,9 @@ SteamCommunity.prototype.getSteamSharedfile = function(sid, callback) {
 			// Find owner profile link, convert to steamID64 using SteamIdResolver lib and create a SteamID object
 			let ownerHref = $(".friendBlockLinkOverlay").attr()["href"];
 
-			SteamIdResolver.customUrlToSteamID64(ownerHref, (err, steamID64) => { // This request takes <1 sec
+			Helpers.resolveVanityURL(ownerHref, (err, data) => { // This request takes <1 sec
 				if (!err) {
-					sharedfile.owner = new SteamID(steamID64);
+					sharedfile.owner = new SteamID(data.steamID);
 				}
 
 				// Make callback when ID was resolved as otherwise owner will always be null
diff --git a/package.json b/package.json
index 64709eb..afd186a 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,6 @@
 		"request": "^2.88.0",
 		"steam-totp": "^1.5.0",
 		"steamid": "^1.1.3",
-		"steamid-resolver": "^1.2.3",
 		"xml2js": "^0.4.22"
 	},
 	"engines": {

From f7d7cf0660c5a15fc24ca770afeb3f9e40936403 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Mon, 15 May 2023 22:42:22 +0200
Subject: [PATCH 18/22] Remove support for up- & downvoting sharedfiles

---
 classes/CSteamSharedfile.js | 16 --------------
 components/sharedfiles.js   | 42 -------------------------------------
 2 files changed, 58 deletions(-)

diff --git a/classes/CSteamSharedfile.js b/classes/CSteamSharedfile.js
index 8401cd6..432f09a 100644
--- a/classes/CSteamSharedfile.js
+++ b/classes/CSteamSharedfile.js
@@ -204,19 +204,3 @@ CSteamSharedfile.prototype.unfavorite = function(callback) {
 CSteamSharedfile.prototype.unsubscribe = function(callback) {
 	this._community.unsubscribeSharedfileComments(this.owner, this.id, callback);
 };
-
-/**
- * Downvotes this sharedfile
- * @param {function} callback - Takes only an Error object/null as the first argument
- */
-CSteamSharedfile.prototype.voteDown = function(callback) {
-	this._community.voteDownSharedfile(this.id, callback);
-};
-
-/**
- * Upvotes this sharedfile
- * @param {function} callback - Takes only an Error object/null as the first argument
- */
-CSteamSharedfile.prototype.voteUp = function(callback) {
-	this._community.voteUpSharedfile(this.id, callback);
-};
\ No newline at end of file
diff --git a/components/sharedfiles.js b/components/sharedfiles.js
index 3ef690a..00a754c 100644
--- a/components/sharedfiles.js
+++ b/components/sharedfiles.js
@@ -155,45 +155,3 @@ SteamCommunity.prototype.unsubscribeSharedfileComments = function(userID, sid, c
 		callback(null || err);
 	}, "steamcommunity");
 };
-
-/**
- * Downvotes a sharedfile
- * @param {String} sid - ID of the sharedfile
- * @param {function} callback - Takes only an Error object/null as the first argument
- */
-SteamCommunity.prototype.voteDownSharedfile = function(sid, callback) {
-	this.httpRequestPost({
-		"uri": "https://steamcommunity.com/sharedfiles/votedown",
-		"form": {
-			"id": sid,
-			"sessionid": this.getSessionID()
-		}
-	}, function(err, response, body) {
-		if (!callback) {
-			return;
-		}
-
-		callback(null || err);
-	}, "steamcommunity");
-};
-
-/**
- * Upvotes a sharedfile
- * @param {String} sid - ID of the sharedfile
- * @param {function} callback - Takes only an Error object/null as the first argument
- */
-SteamCommunity.prototype.voteUpSharedfile = function(sid, callback) {
-	this.httpRequestPost({
-		"uri": "https://steamcommunity.com/sharedfiles/voteup",
-		"form": {
-			"id": sid,
-			"sessionid": this.getSessionID()
-		}
-	}, function(err, response, body) {
-		if (!callback) {
-			return;
-		}
-
-		callback(null || err);
-	}, "steamcommunity");
-};
\ No newline at end of file

From f96d25ad52cb65b3355bea7b25b8aee597728206 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Mon, 15 May 2023 22:45:33 +0200
Subject: [PATCH 19/22] Rename sid to sharedFileId

---
 classes/CSteamSharedfile.js | 10 +++++-----
 components/sharedfiles.js   | 36 ++++++++++++++++++------------------
 2 files changed, 23 insertions(+), 23 deletions(-)

diff --git a/classes/CSteamSharedfile.js b/classes/CSteamSharedfile.js
index 432f09a..80d2a85 100644
--- a/classes/CSteamSharedfile.js
+++ b/classes/CSteamSharedfile.js
@@ -7,14 +7,14 @@ const ESharedfileType = require('../resources/ESharedfileType.js');
 
 /**
  * Scrape a sharedfile's DOM to get all available information
- * @param {String} sid - ID of the sharedfile
+ * @param {String} sharedFileId - ID of the sharedfile
  * @param {function} callback - First argument is null/Error, second is object containing all available information
  */
-SteamCommunity.prototype.getSteamSharedfile = function(sid, callback) {
+SteamCommunity.prototype.getSteamSharedfile = function(sharedFileId, callback) {
 
 	// Construct object holding all the data we can scrape
 	let sharedfile = {
-		id: sid,
+		id: sharedFileId,
 		type: null,
 		appID: null,
 		owner: null,
@@ -28,7 +28,7 @@ SteamCommunity.prototype.getSteamSharedfile = function(sid, callback) {
 
 
 	// Get DOM of sharedfile
-	this.httpRequestGet(`https://steamcommunity.com/sharedfiles/filedetails/?id=${sid}`, (err, res, body) => {
+	this.httpRequestGet(`https://steamcommunity.com/sharedfiles/filedetails/?id=${sharedFileId}`, (err, res, body) => {
 		try {
 
 			/* --------------------- Preprocess output --------------------- */
@@ -66,7 +66,7 @@ SteamCommunity.prototype.getSteamSharedfile = function(sid, callback) {
 			/* --------------------- Find and map values --------------------- */
 
 			// Find appID in share button onclick event
-			sharedfile.appID = Number($("#ShareItemBtn").attr()["onclick"].replace(`ShowSharePublishedFilePopup( '${sid}', '`, "").replace("' );", ""));
+			sharedfile.appID = Number($("#ShareItemBtn").attr()["onclick"].replace(`ShowSharePublishedFilePopup( '${sharedFileId}', '`, "").replace("' );", ""));
 
 
 			// Find fileSize if not guide
diff --git a/components/sharedfiles.js b/components/sharedfiles.js
index 00a754c..c051943 100644
--- a/components/sharedfiles.js
+++ b/components/sharedfiles.js
@@ -5,17 +5,17 @@ var SteamID = require('steamid');
 /**
  * Deletes a comment from a sharedfile's comment section
  * @param {SteamID | String} userID - ID of the user associated to this sharedfile
- * @param {String} sid - ID of the sharedfile
+ * @param {String} sharedFileId - ID of the sharedfile
  * @param {String} cid - ID of the comment to delete
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
-SteamCommunity.prototype.deleteSharedfileComment = function(userID, sid, cid, callback) {
+SteamCommunity.prototype.deleteSharedfileComment = function(userID, sharedFileId, cid, callback) {
 	if (typeof userID === "string") {
 		userID = new SteamID(userID);
 	}
 
 	this.httpRequestPost({
-		"uri": `https://steamcommunity.com/comment/PublishedFile_Public/delete/${userID.toString()}/${sid}/`,
+		"uri": `https://steamcommunity.com/comment/PublishedFile_Public/delete/${userID.toString()}/${sharedFileId}/`,
 		"form": {
 			"gidcomment": cid,
 			"count": 10,
@@ -32,15 +32,15 @@ SteamCommunity.prototype.deleteSharedfileComment = function(userID, sid, cid, ca
 
 /**
  * Favorites a sharedfile
- * @param {String} sid - ID of the sharedfile
+ * @param {String} sharedFileId - ID of the sharedfile
  * @param {String} appid - ID of the app associated to this sharedfile
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
-SteamCommunity.prototype.favoriteSharedfile = function(sid, appid, callback) {
+SteamCommunity.prototype.favoriteSharedfile = function(sharedFileId, appid, callback) {
 	this.httpRequestPost({
 		"uri": "https://steamcommunity.com/sharedfiles/favorite",
 		"form": {
-			"id": sid,
+			"id": sharedFileId,
 			"appid": appid,
 			"sessionid": this.getSessionID()
 		}
@@ -56,17 +56,17 @@ SteamCommunity.prototype.favoriteSharedfile = function(sid, appid, callback) {
 /**
  * Posts a comment to a sharedfile
  * @param {SteamID | String} userID - ID of the user associated to this sharedfile
- * @param {String} sid - ID of the sharedfile
+ * @param {String} sharedFileId - ID of the sharedfile
  * @param {String} message - Content of the comment to post
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
-SteamCommunity.prototype.postSharedfileComment = function(userID, sid, message, callback) {
+SteamCommunity.prototype.postSharedfileComment = function(userID, sharedFileId, message, callback) {
 	if (typeof userID === "string") {
 		userID = new SteamID(userID);
 	}
 
 	this.httpRequestPost({
-		"uri": `https://steamcommunity.com/comment/PublishedFile_Public/post/${userID.toString()}/${sid}/`,
+		"uri": `https://steamcommunity.com/comment/PublishedFile_Public/post/${userID.toString()}/${sharedFileId}/`,
 		"form": {
 			"comment": message,
 			"count": 10,
@@ -84,16 +84,16 @@ SteamCommunity.prototype.postSharedfileComment = function(userID, sid, message,
 /**
  * Subscribes to a sharedfile's comment section. Note: Checkbox on webpage does not update
  * @param {SteamID | String} userID ID of the user associated to this sharedfile
- * @param {String} sid ID of the sharedfile
+ * @param {String} sharedFileId ID of the sharedfile
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
-SteamCommunity.prototype.subscribeSharedfileComments = function(userID, sid, callback) {
+SteamCommunity.prototype.subscribeSharedfileComments = function(userID, sharedFileId, callback) {
 	if (typeof userID === "string") {
 		userID = new SteamID(userID);
 	}
 
 	this.httpRequestPost({
-		"uri": `https://steamcommunity.com/comment/PublishedFile_Public/subscribe/${userID.toString()}/${sid}/`,
+		"uri": `https://steamcommunity.com/comment/PublishedFile_Public/subscribe/${userID.toString()}/${sharedFileId}/`,
 		"form": {
 			"count": 10,
 			"sessionid": this.getSessionID()
@@ -109,15 +109,15 @@ SteamCommunity.prototype.subscribeSharedfileComments = function(userID, sid, cal
 
 /**
  * Unfavorites a sharedfile
- * @param {String} sid - ID of the sharedfile
+ * @param {String} sharedFileId - ID of the sharedfile
  * @param {String} appid - ID of the app associated to this sharedfile
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
-SteamCommunity.prototype.unfavoriteSharedfile = function(sid, appid, callback) {
+SteamCommunity.prototype.unfavoriteSharedfile = function(sharedFileId, appid, callback) {
 	this.httpRequestPost({
 		"uri": "https://steamcommunity.com/sharedfiles/unfavorite",
 		"form": {
-			"id": sid,
+			"id": sharedFileId,
 			"appid": appid,
 			"sessionid": this.getSessionID()
 		}
@@ -133,16 +133,16 @@ SteamCommunity.prototype.unfavoriteSharedfile = function(sid, appid, callback) {
 /**
  * Unsubscribes from a sharedfile's comment section. Note: Checkbox on webpage does not update
  * @param {SteamID | String} userID - ID of the user associated to this sharedfile
- * @param {String} sid - ID of the sharedfile
+ * @param {String} sharedFileId - ID of the sharedfile
  * @param {function} callback - Takes only an Error object/null as the first argument
  */
-SteamCommunity.prototype.unsubscribeSharedfileComments = function(userID, sid, callback) {
+SteamCommunity.prototype.unsubscribeSharedfileComments = function(userID, sharedFileId, callback) {
 	if (typeof userID === "string") {
 		userID = new SteamID(userID);
 	}
 
 	this.httpRequestPost({
-		"uri": `https://steamcommunity.com/comment/PublishedFile_Public/unsubscribe/${userID.toString()}/${sid}/`,
+		"uri": `https://steamcommunity.com/comment/PublishedFile_Public/unsubscribe/${userID.toString()}/${sharedFileId}/`,
 		"form": {
 			"count": 10,
 			"sessionid": this.getSessionID()

From 33bc8d83c93ad3d26da657ad0b3fb2ea22de9d0b Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Sun, 28 May 2023 13:45:00 +0200
Subject: [PATCH 20/22] Add support for determining up/downvote status

---
 classes/CSteamSharedfile.js | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/classes/CSteamSharedfile.js b/classes/CSteamSharedfile.js
index 80d2a85..6935787 100644
--- a/classes/CSteamSharedfile.js
+++ b/classes/CSteamSharedfile.js
@@ -23,7 +23,9 @@ SteamCommunity.prototype.getSteamSharedfile = function(sharedFileId, callback) {
 		resolution: null,
 		uniqueVisitorsCount: null,
 		favoritesCount: null,
-		upvoteCount: null
+		upvoteCount: null,
+		isUpvoted: null,
+		isDownvoted: null
 	};
 
 
@@ -103,6 +105,11 @@ SteamCommunity.prototype.getSteamSharedfile = function(sharedFileId, callback) {
 			}
 
 
+			// Determine if this account has already voted on this sharedfile
+			sharedfile.isUpvoted   = String($(".workshopItemControlCtn > #VoteUpBtn")[0].attribs["class"]).includes("toggled");   // Check if upvote btn class contains "toggled"
+			sharedfile.isDownvoted = String($(".workshopItemControlCtn > #VoteDownBtn")[0].attribs["class"]).includes("toggled"); // Check if downvote btn class contains "toggled"
+
+
 			// Determine type by looking at the second breadcrumb. Find the first separator as it has a unique name and go to the next element which holds our value of interest
 			let breadcrumb = $(".breadcrumbs > .breadcrumb_separator").next().get(0).children[0].data || "";
 

From e23fa02c91efe792b6e0bf9e3ca37a35fc236e17 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Sun, 28 May 2023 14:02:40 +0200
Subject: [PATCH 21/22] Add support for reading numRatings of guides

---
 classes/CSteamSharedfile.js | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/classes/CSteamSharedfile.js b/classes/CSteamSharedfile.js
index 6935787..2523b48 100644
--- a/classes/CSteamSharedfile.js
+++ b/classes/CSteamSharedfile.js
@@ -24,6 +24,7 @@ SteamCommunity.prototype.getSteamSharedfile = function(sharedFileId, callback) {
 		uniqueVisitorsCount: null,
 		favoritesCount: null,
 		upvoteCount: null,
+		guideNumRatings: null,
 		isUpvoted: null,
 		isDownvoted: null
 	};
@@ -105,6 +106,12 @@ SteamCommunity.prototype.getSteamSharedfile = function(sharedFileId, callback) {
 			}
 
 
+			// Find numRatings if this is a guide as they use a different voting system
+			let numRatings = $(".ratingSection > .numRatings").text().replace(" ratings", "")
+
+			sharedfile.guideNumRatings = Number(numRatings) || null; // Set to null if not a guide or if the guide does not have enough ratings to show a value
+
+
 			// Determine if this account has already voted on this sharedfile
 			sharedfile.isUpvoted   = String($(".workshopItemControlCtn > #VoteUpBtn")[0].attribs["class"]).includes("toggled");   // Check if upvote btn class contains "toggled"
 			sharedfile.isDownvoted = String($(".workshopItemControlCtn > #VoteDownBtn")[0].attribs["class"]).includes("toggled"); // Check if downvote btn class contains "toggled"

From 440b2f9ba98f0c9d3be243b379a3def367ce7dd9 Mon Sep 17 00:00:00 2001
From: 3urobeat <35304405+HerrEurobeat@users.noreply.github.com>
Date: Mon, 29 May 2023 19:29:24 +0200
Subject: [PATCH 22/22] Add JsDoc

---
 classes/CSteamSharedfile.js | 24 +++++++++++-------------
 1 file changed, 11 insertions(+), 13 deletions(-)

diff --git a/classes/CSteamSharedfile.js b/classes/CSteamSharedfile.js
index 2523b48..889039a 100644
--- a/classes/CSteamSharedfile.js
+++ b/classes/CSteamSharedfile.js
@@ -107,7 +107,7 @@ SteamCommunity.prototype.getSteamSharedfile = function(sharedFileId, callback) {
 
 
 			// Find numRatings if this is a guide as they use a different voting system
-			let numRatings = $(".ratingSection > .numRatings").text().replace(" ratings", "")
+			let numRatings = $(".ratingSection > .numRatings").text().replace(" ratings", "");
 
 			sharedfile.guideNumRatings = Number(numRatings) || null; // Set to null if not a guide or if the guide does not have enough ratings to show a value
 
@@ -151,22 +151,20 @@ SteamCommunity.prototype.getSteamSharedfile = function(sharedFileId, callback) {
 	}, "steamcommunity");
 };
 
+/**
+ * Constructor - Creates a new Sharedfile object
+ * @class
+ * @param {SteamCommunity} community
+ * @param {{ id: string, type: ESharedfileType, appID: number, owner: SteamID|null, fileSize: string|null, postDate: number, resolution: string|null, uniqueVisitorsCount: number, favoritesCount: number, upvoteCount: number|null, guideNumRatings: Number|null, isUpvoted: boolean, isDownvoted: boolean }} data
+ */
 function CSteamSharedfile(community, data) {
+	/**
+	 * @type {SteamCommunity}
+	 */
 	this._community = community;
 
 	// Clone all the data we recieved
-	Object.assign(this, data); // TODO: This is cleaner but might break IntelliSense. I'm leaving the block below to be reactivated if necessary
-
-	/* this.id = data.id;
-	this.type = data.type;
-	this.appID = data.appID;
-	this.owner = data.owner;
-	this.fileSize = data.fileSize;
-	this.postDate = data.postDate;
-	this.resolution = data.resolution;
-	this.uniqueVisitorsCount = data.uniqueVisitorsCount;
-	this.favoritesCount = data.favoritesCount;
-	this.upvoteCount = data.upvoteCount; */
+	Object.assign(this, data);
 }
 
 /**