diff --git a/src/data/fileStructure.json b/src/data/fileStructure.json index f0ddae49..f191c665 100644 --- a/src/data/fileStructure.json +++ b/src/data/fileStructure.json @@ -448,13 +448,18 @@ { "path": "src/libraryPatches/CSteamSharedFile.js", "url": "https://raw.githubusercontent.com/3urobeat/steam-comment-service-bot/beta-testing/src/libraryPatches/CSteamSharedFile.js", - "checksum": "9f155bb4bded0f93b388399f17e859a8" + "checksum": "445874b13cdd04a885790508055202be" }, { "path": "src/libraryPatches/EDiscussionType.js", "url": "https://raw.githubusercontent.com/3urobeat/steam-comment-service-bot/beta-testing/src/libraryPatches/EDiscussionType.js", "checksum": "4a801c29078172b6b35e86843670ae6e" }, + { + "path": "src/libraryPatches/ESharedFileType.js", + "url": "https://raw.githubusercontent.com/3urobeat/steam-comment-service-bot/beta-testing/src/libraryPatches/ESharedFileType.js", + "checksum": "86de7b8864dd78fcfc542e88bc4c5a68" + }, { "path": "src/libraryPatches/README.md", "url": "https://raw.githubusercontent.com/3urobeat/steam-comment-service-bot/beta-testing/src/libraryPatches/README.md", @@ -478,7 +483,7 @@ { "path": "src/libraryPatches/sharedfiles.js", "url": "https://raw.githubusercontent.com/3urobeat/steam-comment-service-bot/beta-testing/src/libraryPatches/sharedfiles.js", - "checksum": "a33395c4ab436fa534e404487d8e7925" + "checksum": "778ee43931a32422004d50168679127b" }, { "path": "src/pluginSystem/handlePluginData.js", @@ -628,7 +633,7 @@ { "path": "types/types.d.ts", "url": "https://raw.githubusercontent.com/3urobeat/steam-comment-service-bot/beta-testing/types/types.d.ts", - "checksum": "c42ffd93755b217a59a82852946a87e5" + "checksum": "1570c999b124f71794ed294650f133d1" } ] } \ No newline at end of file diff --git a/src/libraryPatches/CSteamSharedFile.js b/src/libraryPatches/CSteamSharedFile.js index b490a884..79925b84 100644 --- a/src/libraryPatches/CSteamSharedFile.js +++ b/src/libraryPatches/CSteamSharedFile.js @@ -4,7 +4,7 @@ const SteamID = require('steamid'); const SteamCommunity = require('steamcommunity'); const Helpers = require('../../node_modules/steamcommunity/components/helpers.js'); -const ESharedFileType = require('../../node_modules/steamcommunity/resources/ESharedFileType.js'); +const ESharedFileType = require("./ESharedFileType.js"); /** @@ -22,6 +22,8 @@ SteamCommunity.prototype.getSteamSharedFile = function(sharedFileId, callback) { fileSize: null, postDate: null, resolution: null, + categories: [], + tags: [], uniqueVisitorsCount: null, favoritesCount: null, upvoteCount: null, @@ -44,6 +46,27 @@ SteamCommunity.prototype.getSteamSharedFile = function(sharedFileId, callback) { // Load output into cheerio to make parsing easier let $ = Cheerio.load(body); + + // 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; + } + + if (breadcrumb.includes("Workshop")) { + sharedfile.type = ESharedFileType.Workshop; + } + + // 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(); @@ -57,6 +80,7 @@ SteamCommunity.prototype.getSteamSharedFile = function(sharedFileId, callback) { 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(); @@ -89,8 +113,28 @@ SteamCommunity.prototype.getSteamSharedFile = function(sharedFileId, callback) { } - // Find resolution if artwork or screenshot - sharedfile.resolution = detailsStatsObj["Size"] || null; + // Find resolution if artwork or screenshot. Guides don't have a resolution and workshop items display it somewhere else + if (sharedfile.type != ESharedFileType.Workshop) { + sharedfile.resolution = detailsStatsObj["Size"] || null; + } else { + let resolutionTag = $(".workshopTagsTitle:contains(\"Resolution:\")").next(); + + sharedfile.resolution = resolutionTag.text() || null; // Keep prop null if this workshop item does not have a resolution + } + + + // Find categories if guide or workshop item + if (sharedfile.type == ESharedFileType.Guide || sharedfile.type == ESharedFileType.Workshop) { + let categoryTag = $(".workshopTagsTitle:contains(\"Category:\")").parent().contents().slice(1).text(); // Find div containing 'Category:' workshopTagsTitle, remove first element 'Category:' and get everything else as text + + sharedfile.categories = categoryTag ? categoryTag.split(", ") : []; // Convert to array if string is not empty (aka no categories have been found) + } + + + // Find tags (there can be multiple) + let tagsTag = $(".workshopTagsTitle:contains(\"Tags:\")").next().contents(); + + sharedfile.tags = tagsTag.map((i, e) => e.type === 'text' ? $(e).text() : '').get() || []; // Map text to an array - https://stackoverflow.com/a/31543727 // Find uniqueVisitorsCount. We can't use ' || null' here as Number("0") casts to false @@ -124,24 +168,6 @@ SteamCommunity.prototype.getSteamSharedFile = function(sharedFileId, callback) { 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) || $(".breadcrumbs").get(0).children[1]; // Some artworks only have one breadcrumb like "username's Artwork" so let's check that as a backup - - if (breadcrumb) { // If neither could be found then leave type at null - if (breadcrumb.children[0].data.includes("Screenshot")) { - sharedfile.type = ESharedFileType.Screenshot; - } - - if (breadcrumb.children[0].data.includes("Artwork")) { - sharedfile.type = ESharedFileType.Artwork; - } - - if (breadcrumb.children[0].data.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"]; @@ -167,7 +193,7 @@ SteamCommunity.prototype.getSteamSharedFile = function(sharedFileId, callback) { * 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 + * @param {{ id: string, type: ESharedFileType, appID: number, owner: SteamID|null, fileSize: string|null, postDate: number, resolution: string|null, category: string[], tags: string[], uniqueVisitorsCount: number, favoritesCount: number, upvoteCount: number|null, guideNumRatings: Number|null, isUpvoted: boolean, isDownvoted: boolean }} data */ function CSteamSharedFile(community, data) { /** @@ -181,7 +207,7 @@ function CSteamSharedFile(community, data) { /** * Deletes a comment from this sharedfile's comment section - * @param {string} cid - ID of the comment to delete + * @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) { @@ -198,7 +224,7 @@ CSteamSharedFile.prototype.favorite = function(callback) { /** * Posts a comment to this sharedfile - * @param {string} message - Content of the comment to post + * @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) { @@ -209,7 +235,7 @@ CSteamSharedFile.prototype.comment = function(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) { +CSteamSharedFile.prototype.subscribeComments = function(callback) { this._community.subscribeSharedFileComments(this.owner, this.id, callback); }; @@ -225,6 +251,22 @@ CSteamSharedFile.prototype.unfavorite = function(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) { +CSteamSharedFile.prototype.unsubscribeComments = function(callback) { this._community.unsubscribeSharedFileComments(this.owner, this.id, callback); }; + +/** + * Subscribes to this workshop item + * @param {function} callback - Takes only an Error object/null as the first argument + */ +CSteamSharedFile.prototype.subscribeWorkshop = function(callback) { + this._community.subscribeWorkshopSharedFile(this.id, this.appID, callback); +}; + +/** + * Unsubscribes from this workshop item + * @param {function} callback - Takes only an Error object/null as the first argument + */ +CSteamSharedFile.prototype.unsubscribeWorkshop = function(callback) { + this._community.unsubscribeWorkshopSharedFile(this.id, this.appID, callback); +}; diff --git a/src/libraryPatches/ESharedFileType.js b/src/libraryPatches/ESharedFileType.js new file mode 100644 index 00000000..01f006a6 --- /dev/null +++ b/src/libraryPatches/ESharedFileType.js @@ -0,0 +1,15 @@ +/** + * @enum ESharedFileType + */ +module.exports = { + "Screenshot": 0, + "Artwork": 1, + "Guide": 2, + "Workshop": 3, + + // Value-to-name mapping for convenience + "0": "Screenshot", + "1": "Artwork", + "2": "Guide", + "3": "Workshop" +}; diff --git a/src/libraryPatches/sharedfiles.js b/src/libraryPatches/sharedfiles.js index 6dd5cfcd..072c2e19 100644 --- a/src/libraryPatches/sharedfiles.js +++ b/src/libraryPatches/sharedfiles.js @@ -106,4 +106,50 @@ SteamCommunity.prototype.postSharedFileComment = function(userID, sharedFileId, callback(new Error(body.error)); } }, "steamcommunity"); -}; \ No newline at end of file +}; + +/** + * Subscribes to a workshop item 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.subscribeWorkshopSharedFile = function(sharedFileId, appid, callback) { + this.httpRequestPost({ + "uri": "https://steamcommunity.com/sharedfiles/subscribe", + "form": { + "id": sharedFileId, + "appid": appid, + "sessionid": this.getSessionID() + } + }, function(err, response, body) { + if (!callback) { + return; + } + + callback(err); + }, "steamcommunity"); +}; + +/** + * Unsubscribes from a workshop item 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.unsubscribeWorkshopSharedFile = function(sharedFileId, appid, callback) { + this.httpRequestPost({ + "uri": "https://steamcommunity.com/sharedfiles/unsubscribe", + "form": { + "id": sharedFileId, + "appid": appid, + "sessionid": this.getSessionID() + } + }, function(err, response, body) { + if (!callback) { + return; + } + + callback(err); + }, "steamcommunity"); +}; diff --git a/types/types.d.ts b/types/types.d.ts index 246986ca..d39b8b47 100644 --- a/types/types.d.ts +++ b/types/types.d.ts @@ -1758,7 +1758,7 @@ declare class CSteamSharedFile { * Subscribes to this sharedfile's comment section. Note: Checkbox on webpage does not update * @param callback - Takes only an Error object/null as the first argument */ - subscribe(callback: (...params: any[]) => any): void; + subscribeComments(callback: (...params: any[]) => any): void; /** * Unfavorites this sharedfile * @param callback - Takes only an Error object/null as the first argument @@ -1768,7 +1768,17 @@ declare class CSteamSharedFile { * Unsubscribes from this sharedfile's comment section. Note: Checkbox on webpage does not update * @param callback - Takes only an Error object/null as the first argument */ - unsubscribe(callback: (...params: any[]) => any): void; + unsubscribeComments(callback: (...params: any[]) => any): void; + /** + * Subscribes to this workshop item + * @param callback - Takes only an Error object/null as the first argument + */ + subscribeWorkshop(callback: (...params: any[]) => any): void; + /** + * Unsubscribes from this workshop item + * @param callback - Takes only an Error object/null as the first argument + */ + unsubscribeWorkshop(callback: (...params: any[]) => any): void; } /**