From b654a3435073b7885dec058447ebf3b5ac21e4c7 Mon Sep 17 00:00:00 2001 From: 3urobeat <35304405+3urobeat@users.noreply.github.com> Date: Thu, 15 Feb 2024 21:45:35 +0100 Subject: [PATCH 1/9] feat(Reviews): Init --- classes/CSteamReviews.js | 16 ++++++++++++++++ components/reviews.js | 1 + index.js | 2 ++ 3 files changed, 19 insertions(+) create mode 100644 classes/CSteamReviews.js create mode 100644 components/reviews.js diff --git a/classes/CSteamReviews.js b/classes/CSteamReviews.js new file mode 100644 index 0000000..125265f --- /dev/null +++ b/classes/CSteamReviews.js @@ -0,0 +1,16 @@ +const Cheerio = require('cheerio'); +const SteamID = require('steamid'); +const StdLib = require('@doctormckay/stdlib'); + +const SteamCommunity = require('../index.js'); +const Helpers = require('../components/helpers.js'); + + +SteamCommunity.prototype.getSteamReview = function(userID, appID, callback) { + +}; + + +function CSteamReview(community, data) { + +}; diff --git a/components/reviews.js b/components/reviews.js new file mode 100644 index 0000000..f86a43a --- /dev/null +++ b/components/reviews.js @@ -0,0 +1 @@ +const SteamCommunity = require('../index.js'); diff --git a/index.js b/index.js index 4915bb3..04b560a 100644 --- a/index.js +++ b/index.js @@ -487,6 +487,7 @@ SteamCommunity.prototype._resolveVanityURL = async function(url) { require('./components/http.js'); require('./components/profile.js'); +require('./components/reviews.js'); require('./components/market.js'); require('./components/groups.js'); require('./components/users.js'); @@ -498,6 +499,7 @@ require('./components/help.js'); require('./classes/CMarketItem.js'); require('./classes/CMarketSearchResult.js'); require('./classes/CSteamGroup.js'); +require('./classes/CSteamReviews.js'); require('./classes/CSteamSharedFile.js'); require('./classes/CSteamUser.js'); From 65ab3f12e4bd241fa9609f262c402d593785d2f0 Mon Sep 17 00:00:00 2001 From: 3urobeat <35304405+3urobeat@users.noreply.github.com> Date: Fri, 16 Feb 2024 11:10:20 +0100 Subject: [PATCH 2/9] feat(Reviews): Add review data scraping --- classes/CSteamReviews.js | 120 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/classes/CSteamReviews.js b/classes/CSteamReviews.js index 125265f..3d1d663 100644 --- a/classes/CSteamReviews.js +++ b/classes/CSteamReviews.js @@ -7,10 +7,128 @@ const Helpers = require('../components/helpers.js'); SteamCommunity.prototype.getSteamReview = function(userID, appID, callback) { + if (typeof userID !== 'string' && !Helpers.isSteamID(userID)) { + throw new Error('userID parameter should be a user URL string or a SteamID object'); + } + if (typeof userID === 'object' && (userID.universe != SteamID.Universe.PUBLIC || userID.type != SteamID.Type.INDIVIDUAL)) { + throw new Error('SteamID must stand for an individual account in the public universe'); + } + + if (typeof userID === 'string') { + userID = new SteamID(userID); + } + + + // Construct object holding all the data we can scrape + let review = { + steamID: userID, + appID: appID, + postedDate: null, + updatedDate: null, + recommended: null, + content: null, + commentsAmount: null, + comments: [], + recentPlaytimeHours: null, + totalPlaytimeHours: null, + playtimeHoursAtReview: null, + votesHelpful: null, + votesFunny: null + }; + + + // Get DOM of review + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + let result = await this.httpRequest({ + method: 'GET', + url: `https://steamcommunity.com/profiles/${userID.getSteamID64()}/recommended/${appID}?l=en`, + source: 'steamcommunity', + followRedirect: true // This setting is important: Steam redirects /profiles/ links to /id/ if user has a vanity set + }); + + + try { + + // Load output into cheerio to make parsing easier + let $ = Cheerio.load(result.textBody); + + + // Find postedDate & updatedDate and convert to timestamp + let posted = $('.recommendation_date').text().split('\n'); + + posted.forEach((e) => { + e = e.trim(); + + if (e.startsWith('Posted')) { + review.postedDate = Helpers.decodeSteamTime(e.replace('Posted: ', '')); + } + if (e.startsWith('Updated')) { + review.updatedDate = Helpers.decodeSteamTime(e.replace('Updated: ', '')); + } + }); + + // Find out if user recommended the game or not + review.recommended = $('.ratingSummary').text().trim() == 'Recommended'; + + // Get content + review.content = $('.review_area_content > #ReviewText').find('br').replaceWith('\n').end().text().trim(); // Preserve line breaks in text + + // Get comments data if any exist + let commentThread = $('.commentthread_area').children(); + + if (commentThread.length > 0) { + // Get amount of comments reported by Steam + review.commentsAmount = Number(commentThread.children('.commentthread_count').children('.commentthread_count_label').children().first().text()); + + // Get content and author of each comment + commentThread.children('.commentthread_comments').children().each(async (i, e) => { + let comment = $(e).children('.commentthread_comment_content'); // The whole comment + + let author = comment.children('.commentthread_comment_author'); // The author part of the comment - contains profile link and date + let commentEmoji = comment.children('.commentthread_comment_text').find('img'); // Emojis in the comment text + + review.comments.push({ + index: i, + id: comment.children('.commentthread_comment_text').attr('id').replace('content_', ''), + authorLink: author.children('.commentthread_author_link').attr('href'), + postedDate: Helpers.decodeSteamTime(author.children('.commentthread_comment_timestamp').text()), + content: commentEmoji.replaceWith(commentEmoji.attr('alt')).end().find('br').replaceWith('\n').end().text().trim() // Preserve emojis by using alt text and line breaks in text + }); + }); + } + + // Get recent playtime. Format: recentPlaytime / totalPlaytime (playtimeAtReview) + let playtimeStr = $('.ratingSummaryHeader > .playTime').text().trim().split('/'); + + review.recentPlaytimeHours = Number(playtimeStr[0].trim().split(' ')[0]); + review.totalPlaytimeHours = Number(playtimeStr[1].trim().split(' ')[0]); + + if (playtimeStr[1].includes('at review time')) { // Some reviews don't contain info about playtime at the time of review + review.playtimeHoursAtReview = Number(playtimeStr[1].trim().split('(')[1].split(' ')[0]); + } + + // Get votes + let ratings = $('.ratingBar').find('br').replaceWith('\n').end().text().trim().split('\n') + + let helpfulStr = ratings.find((e) => e.includes('helpful')); + let funnyStr = ratings.find((e) => e.includes('funny')); + + if (helpfulStr) { + review.votesHelpful = Number(helpfulStr.split(' ')[0]); + } + + if (funnyStr) { + review.votesFunny = Number(funnyStr.split(' ')[0]); + } + + } catch (err) { + callback(err, null); + } + }); }; function CSteamReview(community, data) { -}; +} From ae1b69fa6b3fcd3404c0315af72895f10b5725a6 Mon Sep 17 00:00:00 2001 From: 3urobeat <35304405+3urobeat@users.noreply.github.com> Date: Fri, 16 Feb 2024 12:13:55 +0100 Subject: [PATCH 3/9] feat(Reviews): Create CSteamReview object and assign data --- classes/CSteamReviews.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/classes/CSteamReviews.js b/classes/CSteamReviews.js index 3d1d663..9567cce 100644 --- a/classes/CSteamReviews.js +++ b/classes/CSteamReviews.js @@ -33,8 +33,8 @@ SteamCommunity.prototype.getSteamReview = function(userID, appID, callback) { recentPlaytimeHours: null, totalPlaytimeHours: null, playtimeHoursAtReview: null, - votesHelpful: null, - votesFunny: null + votesHelpful: 0, + votesFunny: 0 }; @@ -109,7 +109,7 @@ SteamCommunity.prototype.getSteamReview = function(userID, appID, callback) { } // Get votes - let ratings = $('.ratingBar').find('br').replaceWith('\n').end().text().trim().split('\n') + let ratings = $('.ratingBar').find('br').replaceWith('\n').end().text().trim().split('\n'); let helpfulStr = ratings.find((e) => e.includes('helpful')); let funnyStr = ratings.find((e) => e.includes('funny')); @@ -122,13 +122,21 @@ SteamCommunity.prototype.getSteamReview = function(userID, appID, callback) { review.votesFunny = Number(funnyStr.split(' ')[0]); } + resolve(new CSteamReview(this, review)); + } catch (err) { - callback(err, null); + reject(err); } }); }; function CSteamReview(community, data) { + /** + * @type {SteamCommunity} + */ + this._community = community; + // Clone all the data we received + Object.assign(this, data); } From f4aa433e43e765baf20838405912efdc57d82fe7 Mon Sep 17 00:00:00 2001 From: 3urobeat <35304405+3urobeat@users.noreply.github.com> Date: Fri, 16 Feb 2024 12:14:12 +0100 Subject: [PATCH 4/9] feat(Reviews): Add JsDoc type documentation --- classes/CSteamReviews.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/classes/CSteamReviews.js b/classes/CSteamReviews.js index 9567cce..01d73b1 100644 --- a/classes/CSteamReviews.js +++ b/classes/CSteamReviews.js @@ -6,6 +6,31 @@ const SteamCommunity = require('../index.js'); const Helpers = require('../components/helpers.js'); +/** + * @typedef Review + * @type {object} + * @property {SteamID} steamID SteamID object of the review author + * @property {string} appID AppID of the associated game + * @property {Date} postedDate Date of when the review was posted initially + * @property {Date} [updatedDate] Date of when the review was last updated. Remains `null` if review was never updated + * @property {boolean} recommended True if the author recommends the game, false otherwise. + * @property {string} content Text content of the review + * @property {number} [commentsAmount] Amount of comments reported by Steam. Remains `null` if coments are disabled + * @property {Array.<{ index: number, id: string, authorLink: string, postedDate: Date, content: string }>} [comments] Array of comments left on the review + * @property {number} recentPlaytimeHours Amount of hours the author played this game for in the last 2 weeks + * @property {number} totalPlaytimeHours Amount of hours the author played this game for in total + * @property {number} [playtimeHoursAtReview] Amount of hours the author played this game for at the point of review. Remains `null` if Steam does not provide this information. + * @property {number} votesHelpful Amount of 'Review is helpful' votes + * @property {number} votesFunny Amount of 'Review is funny' votes + */ + +/** + * Scrape a review's DOM to get all available information + * @param {string | SteamID} userID - SteamID object or steamID64 of the review author + * @param {string} appID - AppID of the associated game + * @param {function(Error, CSteamReview)} [callback] - First argument is null/Error, second is object containing all available information + * @returns {Promise.} Resolves with CSteamReview object + */ SteamCommunity.prototype.getSteamReview = function(userID, appID, callback) { if (typeof userID !== 'string' && !Helpers.isSteamID(userID)) { throw new Error('userID parameter should be a user URL string or a SteamID object'); @@ -131,6 +156,12 @@ SteamCommunity.prototype.getSteamReview = function(userID, appID, callback) { }; +/** + * Constructor - Creates a new CSteamReview object + * @class + * @param {SteamCommunity} community - Current SteamCommunity instance + * @param {Review} data - Review data collected by the scraper + */ function CSteamReview(community, data) { /** * @type {SteamCommunity} From db06b335dd60e199bc67adddc550fb97b2367f6c Mon Sep 17 00:00:00 2001 From: 3urobeat <35304405+3urobeat@users.noreply.github.com> Date: Fri, 16 Feb 2024 13:10:18 +0100 Subject: [PATCH 5/9] feat(Reviews): Add isEarlyAccess prop --- classes/CSteamReviews.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/classes/CSteamReviews.js b/classes/CSteamReviews.js index 01d73b1..4b8bc1e 100644 --- a/classes/CSteamReviews.js +++ b/classes/CSteamReviews.js @@ -14,6 +14,7 @@ const Helpers = require('../components/helpers.js'); * @property {Date} postedDate Date of when the review was posted initially * @property {Date} [updatedDate] Date of when the review was last updated. Remains `null` if review was never updated * @property {boolean} recommended True if the author recommends the game, false otherwise. + * @property {boolean} isEarlyAccess True if the review is an early access review * @property {string} content Text content of the review * @property {number} [commentsAmount] Amount of comments reported by Steam. Remains `null` if coments are disabled * @property {Array.<{ index: number, id: string, authorLink: string, postedDate: Date, content: string }>} [comments] Array of comments left on the review @@ -52,6 +53,7 @@ SteamCommunity.prototype.getSteamReview = function(userID, appID, callback) { postedDate: null, updatedDate: null, recommended: null, + isEarlyAccess: false, content: null, commentsAmount: null, comments: [], @@ -96,6 +98,9 @@ SteamCommunity.prototype.getSteamReview = function(userID, appID, callback) { // Find out if user recommended the game or not review.recommended = $('.ratingSummary').text().trim() == 'Recommended'; + // Find out if review is an early access review + review.isEarlyAccess = $('.early_access_review').length > 0; + // Get content review.content = $('.review_area_content > #ReviewText').find('br').replaceWith('\n').end().text().trim(); // Preserve line breaks in text From 3adc18096830e2512d6fe1f1d3cb08e11f7c7f13 Mon Sep 17 00:00:00 2001 From: 3urobeat <35304405+3urobeat@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:38:15 +0100 Subject: [PATCH 6/9] feat(Reviews): Add reviewID prop needed for vote interactions --- classes/CSteamReviews.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/classes/CSteamReviews.js b/classes/CSteamReviews.js index 4b8bc1e..a06e7dc 100644 --- a/classes/CSteamReviews.js +++ b/classes/CSteamReviews.js @@ -9,6 +9,7 @@ const Helpers = require('../components/helpers.js'); /** * @typedef Review * @type {object} + * @property {string} [reviewID] ID of review, used for voting & reporting. Remains `null` if it is your review or you are not logged in as the buttons are not presented then. * @property {SteamID} steamID SteamID object of the review author * @property {string} appID AppID of the associated game * @property {Date} postedDate Date of when the review was posted initially @@ -48,6 +49,7 @@ SteamCommunity.prototype.getSteamReview = function(userID, appID, callback) { // Construct object holding all the data we can scrape let review = { + reviewID: null, steamID: userID, appID: appID, postedDate: null, @@ -81,6 +83,9 @@ SteamCommunity.prototype.getSteamReview = function(userID, appID, callback) { let $ = Cheerio.load(result.textBody); + // Find reviewID which is needed for upvoting, downvoting, etc. + review.reviewID = $('.review_rate_bar').children('span').attr('id').replace('RecommendationVoteUpBtn', ''); + // Find postedDate & updatedDate and convert to timestamp let posted = $('.recommendation_date').text().split('\n'); From 2add616d5d415f396bbfde8763b0ef5af8dae78d Mon Sep 17 00:00:00 2001 From: 3urobeat <35304405+3urobeat@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:19:26 +0100 Subject: [PATCH 7/9] feat(Reviews): Add methods for commenting, voting & subscribing --- components/reviews.js | 195 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) diff --git a/components/reviews.js b/components/reviews.js index f86a43a..9df3d88 100644 --- a/components/reviews.js +++ b/components/reviews.js @@ -1 +1,196 @@ +const SteamID = require('steamid'); +const StdLib = require('@doctormckay/stdlib'); + const SteamCommunity = require('../index.js'); +const Helpers = require('../components/helpers.js'); + + +/** + * Posts a comment to a review + * @param {String} message - Content of the comment to post + * @param {function} [callback] - Takes only an Error object/null as the first argument + * @return Promise + */ +SteamCommunity.prototype.postReviewComment = function(userID, appID, message, callback) { + if (typeof userID == 'string') { + userID = new SteamID(userID); + } + + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + let res = await this.httpRequest({ + method: 'POST', + url: `https://steamcommunity.com/comment/Recommendation/post/${userID.getSteamID64()}/${appID}/`, + form: { + comment: message, + count: 10, + sessionid: this.getSessionID() + }, + source: 'steamcommunity', + checkCommunityError: true + }); + + if (res.jsonBody && res.jsonBody.success != SteamCommunity.EResult.OK) { + reject(new Error(res.jsonBody.error)); + return; + } + + resolve(); + }); +}; + +SteamCommunity.prototype.deleteReviewComment = function(userID, appID, cid, callback) { + if (typeof userID == 'string') { + userID = new SteamID(userID); + } + + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + let res = await this.httpRequest({ + method: 'POST', + url: `https://steamcommunity.com/comment/Recommendation/delete/${userID.getSteamID64()}/${appID}/`, + form: { + gidcomment: cid, + count: 10, + sessionid: this.getSessionID() + }, + source: 'steamcommunity', + checkCommunityError: true + }); + + if (res.jsonBody && res.jsonBody.success != SteamCommunity.EResult.OK) { + reject(new Error(res.jsonBody.error)); + return; + } + + resolve(); + }); +}; + +SteamCommunity.prototype.subscribeReviewComments = function(userID, appID, callback) { + if (typeof userID == 'string') { + userID = new SteamID(userID); + } + + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + await this.httpRequest({ + method: 'POST', + url: `https://steamcommunity.com/comment/Recommendation/subscribe/${userID.getSteamID64()}/${appID}/`, + form: { + count: 10, + sessionid: this.getSessionID() + }, + source: 'steamcommunity' + }); + + resolve(); + }); +}; + +SteamCommunity.prototype.unsubscribeReviewComments = function(userID, appID, callback) { + if (typeof userID == 'string') { + userID = new SteamID(userID); + } + + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + await this.httpRequest({ + method: 'POST', + url: `https://steamcommunity.com/comment/Recommendation/unsubscribe/${userID.getSteamID64()}/${appID}/`, + form: { + count: 10, + sessionid: this.getSessionID() + }, + source: 'steamcommunity' + }); + + resolve(); + }); +}; + +SteamCommunity.prototype.voteReviewHelpful = function(rid, callback) { + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + let res = await this.httpRequest({ + method: 'POST', + url: `https://steamcommunity.com/userreviews/rate/${rid}`, + form: { + rateup: 'true', + sessionid: this.getSessionID() + }, + source: 'steamcommunity', + checkCommunityError: true + }); + + if (res.jsonBody && res.jsonBody.success != SteamCommunity.EResult.OK) { + reject(Helpers.eresultError(res.jsonBody.success)); + return; + } + + resolve(); + }); +}; + +SteamCommunity.prototype.voteReviewUnhelpful = function(rid, callback) { + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + let res = await this.httpRequest({ + method: 'POST', + url: `https://steamcommunity.com/userreviews/rate/${rid}`, + form: { + rateup: 'false', + sessionid: this.getSessionID() + }, + source: 'steamcommunity', + checkCommunityError: true + }); + + if (res.jsonBody && res.jsonBody.success != SteamCommunity.EResult.OK) { + reject(Helpers.eresultError(res.jsonBody.success)); + return; + } + + resolve(); + }); +}; + +SteamCommunity.prototype.voteReviewFunny = function(rid, callback) { + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + let res = await this.httpRequest({ + method: 'POST', + url: `https://steamcommunity.com/userreviews/votetag/${rid}`, + form: { + tagid: '1', + rateup: 'true', + sessionid: this.getSessionID() + }, + source: 'steamcommunity', + checkCommunityError: true + }); + + if (res.jsonBody && res.jsonBody.success != SteamCommunity.EResult.OK) { + reject(Helpers.eresultError(res.jsonBody.success)); + return; + } + + resolve(); + }); +}; + +SteamCommunity.prototype.voteReviewRemoveFunny = function(rid, callback) { + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + let res = await this.httpRequest({ + method: 'POST', + url: `https://steamcommunity.com/userreviews/votetag/${rid}`, + form: { + tagid: '1', + rateup: 'false', + sessionid: this.getSessionID() + }, + source: 'steamcommunity', + checkCommunityError: true + }); + + if (res.jsonBody && res.jsonBody.success != SteamCommunity.EResult.OK) { + reject(Helpers.eresultError(res.jsonBody.success)); + return; + } + + resolve(); + }); +}; From 15f987a72c2dad19413a0af95f3cba8ed5f1c2e0 Mon Sep 17 00:00:00 2001 From: 3urobeat <35304405+3urobeat@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:20:05 +0100 Subject: [PATCH 8/9] feat(Reviews): Add class methods and fix comment id scraping --- classes/CSteamReviews.js | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/classes/CSteamReviews.js b/classes/CSteamReviews.js index a06e7dc..c98d5ae 100644 --- a/classes/CSteamReviews.js +++ b/classes/CSteamReviews.js @@ -125,7 +125,7 @@ SteamCommunity.prototype.getSteamReview = function(userID, appID, callback) { review.comments.push({ index: i, - id: comment.children('.commentthread_comment_text').attr('id').replace('content_', ''), + id: comment.children('.commentthread_comment_text').attr('id').replace('comment_content_', ''), authorLink: author.children('.commentthread_author_link').attr('href'), postedDate: Helpers.decodeSteamTime(author.children('.commentthread_comment_timestamp').text()), content: commentEmoji.replaceWith(commentEmoji.attr('alt')).end().find('br').replaceWith('\n').end().text().trim() // Preserve emojis by using alt text and line breaks in text @@ -181,3 +181,41 @@ function CSteamReview(community, data) { // Clone all the data we received Object.assign(this, data); } + + +/** + * Posts a comment to this review + * @param {String} message - Content of the comment to post + * @param {function} callback - Takes only an Error object/null as the first argument + */ +CSteamReview.prototype.comment = function(message, callback) { + this._community.postReviewComment(this.steamID.getSteamID64(), this.appID, message, callback); +}; + +CSteamReview.prototype.deleteComment = function(cid, callback) { + this._community.deleteReviewComment(this.steamID.getSteamID64(), this.appID, cid, callback); +}; + +CSteamReview.prototype.subscribe = function(callback) { + this._community.subscribeReviewComments(this.steamID.getSteamID64(), this.appID, callback); +}; + +CSteamReview.prototype.unsubscribe = function(callback) { + this._community.unsubscribeReviewComments(this.steamID.getSteamID64(), this.appID, callback); +}; + +CSteamReview.prototype.voteHelpful = function(callback) { + this._community.voteReviewHelpful(this.reviewID, callback); +}; + +CSteamReview.prototype.voteUnhelpful = function(callback) { + this._community.voteReviewUnhelpful(this.reviewID, callback); +}; + +CSteamReview.prototype.voteFunny = function(callback) { + this._community.voteReviewFunny(this.reviewID, callback); +}; + +CSteamReview.prototype.voteRemoveFunny = function(callback) { + this._community.voteReviewRemoveFunny(this.reviewID, callback); +}; From 61b403063276f7c74d285453c88d5222e08b91b1 Mon Sep 17 00:00:00 2001 From: 3urobeat <35304405+3urobeat@users.noreply.github.com> Date: Sat, 17 Feb 2024 13:45:48 +0100 Subject: [PATCH 9/9] docs(Reviews): Add documentation to all methods --- classes/CSteamReviews.js | 37 +++++++++++++++++++++++++---- components/reviews.js | 50 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 5 deletions(-) diff --git a/classes/CSteamReviews.js b/classes/CSteamReviews.js index c98d5ae..a125a52 100644 --- a/classes/CSteamReviews.js +++ b/classes/CSteamReviews.js @@ -18,7 +18,7 @@ const Helpers = require('../components/helpers.js'); * @property {boolean} isEarlyAccess True if the review is an early access review * @property {string} content Text content of the review * @property {number} [commentsAmount] Amount of comments reported by Steam. Remains `null` if coments are disabled - * @property {Array.<{ index: number, id: string, authorLink: string, postedDate: Date, content: string }>} [comments] Array of comments left on the review + * @property {Array.<{ index: number, id: string, authorLink: string, postedDate: Date, content: string }>} [comments] Array of the last 10 comments left on this review * @property {number} recentPlaytimeHours Amount of hours the author played this game for in the last 2 weeks * @property {number} totalPlaytimeHours Amount of hours the author played this game for in total * @property {number} [playtimeHoursAtReview] Amount of hours the author played this game for at the point of review. Remains `null` if Steam does not provide this information. @@ -185,37 +185,66 @@ function CSteamReview(community, data) { /** * Posts a comment to this review - * @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 */ CSteamReview.prototype.comment = function(message, callback) { this._community.postReviewComment(this.steamID.getSteamID64(), this.appID, message, callback); }; -CSteamReview.prototype.deleteComment = function(cid, callback) { - this._community.deleteReviewComment(this.steamID.getSteamID64(), this.appID, cid, callback); +/** + * Deletes a comment from this review + * @param {string} gidcomment - ID of the comment to delete + * @param {function} callback - Takes only an Error object/null as the first argument + */ +CSteamReview.prototype.deleteComment = function(gidcomment, callback) { + this._community.deleteReviewComment(this.steamID.getSteamID64(), this.appID, gidcomment, callback); }; +/** + * Subscribes to this review's comment section + * @param {function} callback - Takes only an Error object/null as the first argument + */ CSteamReview.prototype.subscribe = function(callback) { this._community.subscribeReviewComments(this.steamID.getSteamID64(), this.appID, callback); }; +/** + * Unsubscribes from this review's comment section + * @param {function} callback - Takes only an Error object/null as the first argument + */ CSteamReview.prototype.unsubscribe = function(callback) { this._community.unsubscribeReviewComments(this.steamID.getSteamID64(), this.appID, callback); }; +/** + * Votes on this review as helpful + * @param {function} callback - Takes only an Error object/null as the first argument + */ CSteamReview.prototype.voteHelpful = function(callback) { this._community.voteReviewHelpful(this.reviewID, callback); }; +/** + * Votes on this review as unhelpful + * @param {function} callback - Takes only an Error object/null as the first argument + */ CSteamReview.prototype.voteUnhelpful = function(callback) { this._community.voteReviewUnhelpful(this.reviewID, callback); }; +/** + * Votes on this review as funny + * @param {function} callback - Takes only an Error object/null as the first argument + */ CSteamReview.prototype.voteFunny = function(callback) { this._community.voteReviewFunny(this.reviewID, callback); }; +/** + * Removes funny vote from this review + * @param {function} callback - Takes only an Error object/null as the first argument + */ CSteamReview.prototype.voteRemoveFunny = function(callback) { this._community.voteReviewRemoveFunny(this.reviewID, callback); }; diff --git a/components/reviews.js b/components/reviews.js index 9df3d88..ab55195 100644 --- a/components/reviews.js +++ b/components/reviews.js @@ -7,9 +7,11 @@ const Helpers = require('../components/helpers.js'); /** * Posts a comment to a review + * @param {string | SteamID} userID - SteamID object or steamID64 of the review author + * @param {string} appID - AppID of the associated game * @param {String} message - Content of the comment to post * @param {function} [callback] - Takes only an Error object/null as the first argument - * @return Promise + * @return Promise Resolves on success, rejects on failure */ SteamCommunity.prototype.postReviewComment = function(userID, appID, message, callback) { if (typeof userID == 'string') { @@ -38,6 +40,14 @@ SteamCommunity.prototype.postReviewComment = function(userID, appID, message, ca }); }; +/** + * Deletes a comment from a review + * @param {string | SteamID} userID - SteamID object or steamID64 of the review author + * @param {string} appID - AppID of the associated game + * @param {String} message - Content of the comment to post + * @param {function} [callback] - Takes only an Error object/null as the first argument + * @return Promise Resolves on success, rejects on failure + */ SteamCommunity.prototype.deleteReviewComment = function(userID, appID, cid, callback) { if (typeof userID == 'string') { userID = new SteamID(userID); @@ -65,6 +75,13 @@ SteamCommunity.prototype.deleteReviewComment = function(userID, appID, cid, call }); }; +/** + * Subscribes to a review's comment section + * @param {string | SteamID} userID - SteamID object or steamID64 of the review author + * @param {string} appID - AppID of the associated game + * @param {function} [callback] - Takes only an Error object/null as the first argument + * @return Promise Resolves on success, rejects on failure + */ SteamCommunity.prototype.subscribeReviewComments = function(userID, appID, callback) { if (typeof userID == 'string') { userID = new SteamID(userID); @@ -85,6 +102,13 @@ SteamCommunity.prototype.subscribeReviewComments = function(userID, appID, callb }); }; +/** + * Unsubscribes from a review's comment section + * @param {string | SteamID} userID - SteamID object or steamID64 of the review author + * @param {string} appID - AppID of the associated game + * @param {function} [callback] - Takes only an Error object/null as the first argument + * @return Promise Resolves on success, rejects on failure + */ SteamCommunity.prototype.unsubscribeReviewComments = function(userID, appID, callback) { if (typeof userID == 'string') { userID = new SteamID(userID); @@ -105,6 +129,12 @@ SteamCommunity.prototype.unsubscribeReviewComments = function(userID, appID, cal }); }; +/** + * Votes on a review as helpful + * @param {string} rid - ID of the review. You can obtain it through `getSteamReview()` + * @param {function} [callback] - Takes only an Error object/null as the first argument + * @return Promise Resolves on success, rejects on failure + */ SteamCommunity.prototype.voteReviewHelpful = function(rid, callback) { return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { let res = await this.httpRequest({ @@ -127,6 +157,12 @@ SteamCommunity.prototype.voteReviewHelpful = function(rid, callback) { }); }; +/** + * Votes on a review as unhelpful + * @param {string} rid - ID of the review. You can obtain it through `getSteamReview()` + * @param {function} [callback] - Takes only an Error object/null as the first argument + * @return Promise Resolves on success, rejects on failure + */ SteamCommunity.prototype.voteReviewUnhelpful = function(rid, callback) { return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { let res = await this.httpRequest({ @@ -149,6 +185,12 @@ SteamCommunity.prototype.voteReviewUnhelpful = function(rid, callback) { }); }; +/** + * Votes on a review as funny + * @param {string} rid - ID of the review. You can obtain it through `getSteamReview()` + * @param {function} [callback] - Takes only an Error object/null as the first argument + * @return Promise Resolves on success, rejects on failure + */ SteamCommunity.prototype.voteReviewFunny = function(rid, callback) { return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { let res = await this.httpRequest({ @@ -172,6 +214,12 @@ SteamCommunity.prototype.voteReviewFunny = function(rid, callback) { }); }; +/** + * Removes funny vote from a review + * @param {string} rid - ID of the review. You can obtain it through `getSteamReview()` + * @param {function} [callback] - Takes only an Error object/null as the first argument + * @return Promise Resolves on success, rejects on failure + */ SteamCommunity.prototype.voteReviewRemoveFunny = function(rid, callback) { return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { let res = await this.httpRequest({