From 80dbf9d688ba3fb675e9a12573ad6259c5f57d89 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 14 Feb 2019 00:19:20 -0500 Subject: [PATCH 01/68] Start v4 --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index eaa6106a..28785f84 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "steamcommunity", - "version": "3.38.0", + "version": "4.0.0-dev", + "private": true, "description": "Provides an interface for logging into and interacting with the Steam Community website", "keywords": [ "steam", From 729f078a25f103694c0784a45f772ef38fd89531 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 14 Feb 2019 00:22:06 -0500 Subject: [PATCH 02/68] Removed deprecated confirmation checker --- components/confirmations.js | 148 ------------------------------------ 1 file changed, 148 deletions(-) diff --git a/components/confirmations.js b/components/confirmations.js index 66770cd4..04681e1a 100644 --- a/components/confirmations.js +++ b/components/confirmations.js @@ -1,7 +1,6 @@ var SteamCommunity = require('../index.js'); var Cheerio = require('cheerio'); var SteamTotp = require('steam-totp'); -var Async = require('async'); var CConfirmation = require('../classes/CConfirmation.js'); @@ -276,150 +275,3 @@ function request(community, url, key, time, tag, params, json, callback) { callback(null, body); }, "steamcommunity"); } - -// Confirmation checker - -/** - * Start automatically polling our confirmations for new ones. The `confKeyNeeded` event will be emitted when we need a confirmation key, or `newConfirmation` when we get a new confirmation - * @param {int} pollInterval - The interval, in milliseconds, at which we will poll for confirmations. This should probably be at least 10,000 to avoid rate-limits. - * @param {Buffer|string|null} [identitySecret=null] - Your identity_secret. If passed, all confirmations will be automatically accepted and nothing will be emitted. - */ -SteamCommunity.prototype.startConfirmationChecker = function(pollInterval, identitySecret) { - this._confirmationPollInterval = pollInterval; - this._knownConfirmations = this._knownConfirmations || {}; - this._confirmationKeys = this._confirmationKeys || {}; - this._identitySecret = identitySecret; - - if(this._confirmationTimer) { - clearTimeout(this._confirmationTimer); - } - - setTimeout(this.checkConfirmations.bind(this), 500); -}; - -/** - * Stop automatically polling our confirmations. - */ -SteamCommunity.prototype.stopConfirmationChecker = function() { - if(this._confirmationPollInterval) { - delete this._confirmationPollInterval; - } - - if(this._identitySecret) { - delete this._identitySecret; - } - - if(this._confirmationTimer) { - clearTimeout(this._confirmationTimer); - delete this._confirmationTimer; - } -}; - -/** - * Run the confirmation checker right now instead of waiting for the next poll. - * Useful to call right after you send/accept an offer that needs confirmation. - */ -SteamCommunity.prototype.checkConfirmations = function() { - if(this._confirmationTimer) { - clearTimeout(this._confirmationTimer); - delete this._confirmationTimer; - } - - var self = this; - if(!this._confirmationQueue) { - this._confirmationQueue = Async.queue(function(conf, callback) { - // Worker to process new confirmations - if(self._identitySecret) { - // We should accept this - self.emit('debug', "Accepting confirmation #" + conf.id); - var time = Math.floor(Date.now() / 1000); - conf.respond(time, SteamTotp.getConfirmationKey(self._identitySecret, time, "allow"), true, function(err) { - // If there was an error and it wasn't actually accepted, we'll pick it up again - if (!err) self.emit('confirmationAccepted', conf); - delete self._knownConfirmations[conf.id]; - setTimeout(callback, 1000); // Call the callback in 1 second, to make sure the time changes - }); - } else { - self.emit('newConfirmation', conf); - setTimeout(callback, 1000); // Call the callback in 1 second, to make sure the time changes - } - }, 1); - } - - this.emit('debug', 'Checking confirmations'); - - this._confirmationCheckerGetKey('conf', function(err, key) { - if(err) { - resetTimer(); - return; - } - - self.getConfirmations(key.time, key.key, function(err, confirmations) { - if(err) { - self.emit('debug', "Can't check confirmations: " + err.message); - resetTimer(); - return; - } - - var known = self._knownConfirmations; - - var newOnes = confirmations.filter(function(conf) { - return !known[conf.id]; - }); - - if(newOnes.length < 1) { - resetTimer(); - return; // No new ones - } - - // We have new confirmations! - newOnes.forEach(function(conf) { - self._knownConfirmations[conf.id] = conf; // Add it to our list of known confirmations - self._confirmationQueue.push(conf); - }); - - resetTimer(); - }); - }); - - function resetTimer() { - if(self._confirmationPollInterval) { - self._confirmationTimer = setTimeout(self.checkConfirmations.bind(self), self._confirmationPollInterval); - } - } -}; - -SteamCommunity.prototype._confirmationCheckerGetKey = function(tag, callback) { - if(this._identitySecret) { - if(tag == 'details') { - // We don't care about details - callback(new Error("Disabled")); - return; - } - - var time = Math.floor(Date.now() / 1000); - callback(null, {"time": time, "key": SteamTotp.getConfirmationKey(this._identitySecret, time, tag)}); - return; - } - - var existing = this._confirmationKeys[tag]; - var reusable = ['conf', 'details']; - - // See if we already have a key that we can reuse. - if(reusable.indexOf(tag) != -1 && existing && (Date.now() - (existing.time * 1000) < (1000 * 60 * 5))) { - callback(null, existing); - return; - } - - // We need a fresh one - var self = this; - this.emit('confKeyNeeded', tag, function(err, time, key) { - if(err) { - callback(err); - return; - } - - self._confirmationKeys[tag] = {"time": time, "key": key}; - callback(null, {"time": time, "key": key}); - }); -}; From 5a855316f667a8e158aa3e1a52e3a16b4dd5dafb Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 14 Feb 2019 00:22:51 -0500 Subject: [PATCH 03/68] Removed inventoryhistory.js --- components/inventoryhistory.js | 188 --------------------------------- index.js | 1 - package.json | 12 +-- 3 files changed, 6 insertions(+), 195 deletions(-) delete mode 100644 components/inventoryhistory.js diff --git a/components/inventoryhistory.js b/components/inventoryhistory.js deleted file mode 100644 index fd7c522e..00000000 --- a/components/inventoryhistory.js +++ /dev/null @@ -1,188 +0,0 @@ -var SteamCommunity = require('../index.js'); -var CEconItem = require('../classes/CEconItem.js'); -var SteamID = require('steamid'); -var request = require('request'); -var Cheerio = require('cheerio'); -var Async = require('async'); - -/* - * Inventory history in a nutshell. - * - * There are no more page numbers. Now you have to request after_time and optionally after_trade. - * Without "prev" set, you will request 30 trades that were completed FURTHER IN THE PAST than after_time (and optionally after_trade) - * With "prev" set, you will request 30 trades that were completed MORE RECENTLY than after_time (and optionally after_trade) - */ - -/** - * @deprecated Use GetTradeHistory instead: https://lab.xpaw.me/steam_api_documentation.html#IEconService_GetTradeHistory_v1 - * @param {object} options - * @param {function} callback - */ -SteamCommunity.prototype.getInventoryHistory = function(options, callback) { - if (typeof options === 'function') { - callback = options; - options = {}; - } - - options.direction = options.direction || "past"; - - var qs = "?l=english"; - if (options.startTime) { - if (options.startTime instanceof Date) { - options.startTime = Math.floor(options.startTime.getTime() / 1000); - } - - qs += "&after_time=" + options.startTime; - - if (options.startTrade) { - qs += "&after_trade=" + options.startTrade; - } - } - - if (options.direction == "future") { - qs += "&prev=1"; - } - - this._myProfile("inventoryhistory" + qs, null, function(err, response, body) { - if (err) { - callback(err); - return; - } - - var output = {}; - var vanityURLs = []; - - var $ = Cheerio.load(body); - if (!$('.inventory_history_pagingrow').html()) { - callback(new Error("Malformed page: no paging row found")); - return; - } - - // Load the inventory item data - var match2 = body.match(/var g_rgHistoryInventory = (.*);/); - if (!match2) { - callback(new Error("Malformed page: no trade found")); - return; - } - - try { - var historyInventory = JSON.parse(match2[1]); - } catch (ex) { - callback(new Error("Malformed page: no well-formed trade data found")); - return; - } - - var i; - - // See if we've got paging buttons - var $paging = $('.inventory_history_nextbtn .pagebtn:not(.disabled)'); - var href; - for (i = 0; i < $paging.length; i++) { - href = $paging[i].attribs.href; - if (href.match(/prev=1/)) { - output.firstTradeTime = new Date(href.match(/after_time=(\d+)/)[1] * 1000); - output.firstTradeID = href.match(/after_trade=(\d+)/)[1]; - } else { - output.lastTradeTime = new Date(href.match(/after_time=(\d+)/)[1] * 1000); - output.lastTradeID = href.match(/after_trade=(\d+)/)[1]; - } - } - - output.trades = []; - var trades = $('.tradehistoryrow'); - - var item, trade, profileLink, items, j, econItem, timeMatch, time; - for (i = 0; i < trades.length; i++) { - item = $(trades[i]); - trade = {}; - - trade.onHold = !!item.find('span:nth-of-type(2)').text().match(/Trade on Hold/i); - - timeMatch = item.find('.tradehistory_timestamp').html().match(/(\d+):(\d+)(am|pm)/); - if (timeMatch[1] == 12 && timeMatch[3] == 'am') { - timeMatch[1] = 0; - } - - if (timeMatch[1] < 12 && timeMatch[3] == 'pm') { - timeMatch[1] = parseInt(timeMatch[1], 10) + 12; - } - - time = (timeMatch[1] < 10 ? '0' : '') + timeMatch[1] + ':' + timeMatch[2] + ':00'; - - trade.date = new Date(item.find('.tradehistory_date').html() + ' ' + time + ' UTC'); - trade.partnerName = item.find('.tradehistory_event_description a').html(); - trade.partnerSteamID = null; - trade.partnerVanityURL = null; - trade.itemsReceived = []; - trade.itemsGiven = []; - - profileLink = item.find('.tradehistory_event_description a').attr('href'); - if (profileLink.indexOf('/profiles/') != -1) { - trade.partnerSteamID = new SteamID(profileLink.match(/(\d+)$/)[1]); - } else { - trade.partnerVanityURL = profileLink.match(/\/([^\/]+)$/)[1]; - if (options.resolveVanityURLs && vanityURLs.indexOf(trade.partnerVanityURL) == -1) { - vanityURLs.push(trade.partnerVanityURL); - } - } - - items = item.find('.history_item'); - for (j = 0; j < items.length; j++) { - match = body.match(new RegExp("HistoryPageCreateItemHover\\( '" + $(items[j]).attr('id') + "', (\\d+), '(\\d+)', '(\\d+|class_\\d+_instance_\\d+|class_\\d+)', '(\\d+)' \\);")); - econItem = historyInventory[match[1]][match[2]][match[3]]; - - if ($(items[j]).attr('id').indexOf('received') != -1) { - trade.itemsReceived.push(new CEconItem(econItem)); - } else { - trade.itemsGiven.push(new CEconItem(econItem)); - } - } - - output.trades.push(trade); - } - - if (options.resolveVanityURLs) { - Async.map(vanityURLs, resolveVanityURL, function(err, results) { - if (err) { - callback(err); - return; - } - - for (i = 0; i < output.trades.length; i++) { - if (output.trades[i].partnerSteamID || !output.trades[i].partnerVanityURL) { - continue; - } - - // Find the vanity URL - for (j = 0; j < results.length; j++) { - if (results[j].vanityURL == output.trades[i].partnerVanityURL) { - output.trades[i].partnerSteamID = new SteamID(results[j].steamID); - break; - } - } - } - - callback(null, output); - }); - } else { - callback(null, output); - } - }, "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(/(\d+)<\/steamID64>/); - if (!match || !match[1]) { - callback(new Error("Couldn't find Steam ID")); - return; - } - - callback(null, {"vanityURL": vanityURL, "steamID": match[1]}); - }); -} diff --git a/index.js b/index.js index 07efc838..112a7ae4 100644 --- a/index.js +++ b/index.js @@ -478,7 +478,6 @@ require('./components/profile.js'); require('./components/market.js'); require('./components/groups.js'); require('./components/users.js'); -require('./components/inventoryhistory.js'); require('./components/webapi.js'); require('./components/twofactor.js'); require('./components/confirmations.js'); diff --git a/package.json b/package.json index 28785f84..df6e7efb 100644 --- a/package.json +++ b/package.json @@ -22,14 +22,14 @@ "url": "https://github.com/DoctorMcKay/node-steamcommunity.git" }, "dependencies": { - "@doctormckay/stats-reporter": "^1.0.2", - "request": "^2.86.0", + "@doctormckay/stats-reporter": "^1.0.4", + "async": "^2.6.2", + "cheerio": "^0.22.0", "node-bignumber": "^1.2.1", + "request": "^2.86.0", + "steam-totp": "^1.5.0", "steamid": "^1.0.0", - "xml2js": "^0.4.11", - "cheerio": "0.22.0", - "async": "^2.1.4", - "steam-totp": "^1.3.0" + "xml2js": "^0.4.11" }, "engines": { "node": ">=4.0.0" From f99d86aa7355bcce5cc9457174479d66a01898e9 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 14 Feb 2019 00:23:17 -0500 Subject: [PATCH 04/68] Removed async dependency --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index df6e7efb..0bd01e90 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ }, "dependencies": { "@doctormckay/stats-reporter": "^1.0.4", - "async": "^2.6.2", "cheerio": "^0.22.0", "node-bignumber": "^1.2.1", "request": "^2.86.0", From 103e9415014ba2fc84ac748e372c6d74af5347a6 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 14 Feb 2019 00:25:07 -0500 Subject: [PATCH 05/68] Updated dependencies --- package.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0bd01e90..d47519dd 100644 --- a/package.json +++ b/package.json @@ -23,14 +23,15 @@ }, "dependencies": { "@doctormckay/stats-reporter": "^1.0.4", + "@doctormckay/stdlib": "^1.10.0", "cheerio": "^0.22.0", "node-bignumber": "^1.2.1", - "request": "^2.86.0", - "steam-totp": "^1.5.0", - "steamid": "^1.0.0", - "xml2js": "^0.4.11" + "request": "^2.88.0", + "steam-totp": "^2.1.0", + "steamid": "^1.1.0", + "xml2js": "^0.4.19" }, "engines": { - "node": ">=4.0.0" + "node": ">=8.0.0" } } From 0cb0d22433dde230f3f2beaef3e4143384a39ded Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 14 Feb 2019 00:29:21 -0500 Subject: [PATCH 06/68] Cleaned up imports --- classes/CConfirmation.js | 2 +- classes/CMarketItem.js | 5 ++-- classes/CMarketSearchResult.js | 5 ++-- classes/CSteamGroup.js | 11 +++---- classes/CSteamUser.js | 11 +++---- components/chat.js | 55 +++++++++++++++++----------------- components/confirmations.js | 9 +++--- components/groups.js | 22 +++++++------- components/http.js | 2 +- components/market.js | 5 ++-- components/twofactor.js | 7 +++-- components/webapi.js | 4 +-- index.js | 6 ++-- 13 files changed, 78 insertions(+), 66 deletions(-) diff --git a/classes/CConfirmation.js b/classes/CConfirmation.js index 0a450c42..85854742 100644 --- a/classes/CConfirmation.js +++ b/classes/CConfirmation.js @@ -1,4 +1,4 @@ -var SteamCommunity = require('../index.js'); +const SteamCommunity = require('../index.js'); module.exports = CConfirmation; diff --git a/classes/CMarketItem.js b/classes/CMarketItem.js index a23c1b77..b031d341 100644 --- a/classes/CMarketItem.js +++ b/classes/CMarketItem.js @@ -1,5 +1,6 @@ -var SteamCommunity = require('../index.js'); -var Cheerio = require('cheerio'); +const Cheerio = require('cheerio'); + +const SteamCommunity = require('../index.js'); SteamCommunity.prototype.getMarketItem = function(appid, hashName, currency, callback) { if (typeof currency == "function") { diff --git a/classes/CMarketSearchResult.js b/classes/CMarketSearchResult.js index e345b7d9..8d1c8883 100644 --- a/classes/CMarketSearchResult.js +++ b/classes/CMarketSearchResult.js @@ -1,5 +1,6 @@ -var SteamCommunity = require('../index.js'); -var Cheerio = require('cheerio'); +const Cheerio = require('cheerio'); + +const SteamCommunity = require('../index.js'); SteamCommunity.prototype.marketSearch = function(options, callback) { var qs = {}; diff --git a/classes/CSteamGroup.js b/classes/CSteamGroup.js index c18889f2..c2009295 100644 --- a/classes/CSteamGroup.js +++ b/classes/CSteamGroup.js @@ -1,7 +1,8 @@ -var SteamCommunity = require('../index.js'); -var Helpers = require('../components/helpers.js'); -var SteamID = require('steamid'); -var xml2js = require('xml2js'); +const SteamID = require('steamid'); +const XML2JS = require('xml2js'); + +const Helpers = require('../components/helpers.js'); +const SteamCommunity = require('../index.js'); SteamCommunity.prototype.getSteamGroup = function(id, callback) { if(typeof id !== 'string' && !Helpers.isSteamID(id)) { @@ -19,7 +20,7 @@ SteamCommunity.prototype.getSteamGroup = function(id, callback) { return; } - xml2js.parseString(body, function(err, result) { + XML2JS.parseString(body, function(err, result) { if(err) { callback(err); return; diff --git a/classes/CSteamUser.js b/classes/CSteamUser.js index 9ca45644..b38eceac 100644 --- a/classes/CSteamUser.js +++ b/classes/CSteamUser.js @@ -1,7 +1,8 @@ -var SteamCommunity = require('../index.js'); -var Helpers = require('../components/helpers.js'); -var SteamID = require('steamid'); -var xml2js = require('xml2js'); +const SteamID = require('steamid'); +const XML2JS = require('xml2js'); + +const Helpers = require('../components/helpers.js'); +const SteamCommunity = require('../index.js'); SteamCommunity.prototype.getSteamUser = function(id, callback) { if(typeof id !== 'string' && !Helpers.isSteamID(id)) { @@ -19,7 +20,7 @@ SteamCommunity.prototype.getSteamUser = function(id, callback) { return; } - xml2js.parseString(body, function(err, result) { + XML2JS.parseString(body, function(err, result) { if(err || (!result.response && !result.profile)) { callback(err || new Error("No valid response")); return; diff --git a/components/chat.js b/components/chat.js index 21e743af..09750e83 100644 --- a/components/chat.js +++ b/components/chat.js @@ -1,5 +1,6 @@ -var SteamCommunity = require('../index.js'); -var SteamID = require('steamid'); +const SteamID = require('steamid'); + +const SteamCommunity = require('../index.js'); SteamCommunity.ChatState = require('../resources/EChatState.js'); SteamCommunity.PersonaState = require('../resources/EPersonaState.js'); @@ -9,13 +10,13 @@ SteamCommunity.prototype.chatLogon = function(interval, uiMode) { if(this.chatState == SteamCommunity.ChatState.LoggingOn || this.chatState == SteamCommunity.ChatState.LoggedOn) { return; } - + interval = interval || 500; uiMode = uiMode || "web"; - + this.emit('debug', 'Requesting chat WebAPI token'); this.chatState = SteamCommunity.ChatState.LoggingOn; - + var self = this; this.getWebApiOauthToken(function(err, token) { if(err) { @@ -32,7 +33,7 @@ SteamCommunity.prototype.chatLogon = function(interval, uiMode) { self.emit('debug', "Cannot get oauth token: " + err.message); return; } - + self.httpRequestPost({ "uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Logon/v1", "form": { @@ -48,7 +49,7 @@ SteamCommunity.prototype.chatLogon = function(interval, uiMode) { setTimeout(self.chatLogon.bind(self), 5000); return; } - + if(body.error != 'OK') { self.chatState = SteamCommunity.ChatState.LogOnFailed; self.emit('chatLogOnFailed', new Error(body.error), false); @@ -56,7 +57,7 @@ SteamCommunity.prototype.chatLogon = function(interval, uiMode) { setTimeout(self.chatLogon.bind(self), 5000); return; } - + self._chat = { "umqid": body.umqid, "message": body.message, @@ -64,9 +65,9 @@ SteamCommunity.prototype.chatLogon = function(interval, uiMode) { "interval": interval, "uiMode": uiMode }; - + self.chatFriends = {}; - + self.chatState = SteamCommunity.ChatState.LoggedOn; self.emit('chatLoggedOn'); self._chatPoll(); @@ -78,16 +79,16 @@ SteamCommunity.prototype.chatMessage = function(recipient, text, type, callback) if(this.chatState != SteamCommunity.ChatState.LoggedOn) { throw new Error("Chat must be logged on before messages can be sent"); } - + if(typeof recipient === 'string') { recipient = new SteamID(recipient); } - + if(typeof type === 'function') { callback = type; type = 'saytext'; } - + type = type || 'saytext'; var self = this; @@ -105,12 +106,12 @@ SteamCommunity.prototype.chatMessage = function(recipient, text, type, callback) if(!callback) { return; } - + if (err) { callback(err); return; } - + if(body.error != 'OK') { callback(new Error(body.error)); } else { @@ -143,7 +144,7 @@ SteamCommunity.prototype.chatLogoff = function() { SteamCommunity.prototype._chatPoll = function() { this.emit('debug', 'Doing chat poll'); - + var self = this; this.httpRequestPost({ "uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Poll/v1", @@ -161,9 +162,9 @@ SteamCommunity.prototype._chatPoll = function() { if (self.chatState == SteamCommunity.ChatState.Offline) { return; } - + self._chat.timer = setTimeout(self._chatPoll.bind(self), self._chat.interval); - + if(err || response.statusCode != 200) { self.emit('debug', 'Error in chat poll: ' + (err ? err.message : "HTTP error " + response.statusCode)); if (err.message == "Not Logged On") { @@ -172,7 +173,7 @@ SteamCommunity.prototype._chatPoll = function() { return; } - + if(!body || body.error != 'OK') { self.emit('debug', 'Error in chat poll: ' + (body && body.error ? body.error : "Malformed response")); if (body && body.error && body.error == "Not Logged On") { @@ -181,29 +182,29 @@ SteamCommunity.prototype._chatPoll = function() { return; } - + self._chat.message = body.messagelast; - + (body.messages || []).forEach(function(message) { var sender = new SteamID(); sender.universe = SteamID.Universe.PUBLIC; sender.type = SteamID.Type.INDIVIDUAL; sender.instance = SteamID.Instance.DESKTOP; sender.accountid = message.accountid_from; - + switch(message.type) { case 'personastate': self._chatUpdatePersona(sender); break; - + case 'saytext': self.emit('chatMessage', sender, message.text); break; - + case 'typing': self.emit('chatTyping', sender); break; - + default: self.emit('debug', 'Unhandled chat message type: ' + message.type); } @@ -240,7 +241,7 @@ SteamCommunity.prototype._chatUpdatePersona = function(steamID) { }, 2000); return; } - + var persona = { "steamID": steamID, "personaName": body.m_strName, @@ -255,4 +256,4 @@ SteamCommunity.prototype._chatUpdatePersona = function(steamID) { self.emit('chatPersonaState', steamID, persona); self.chatFriends[steamID.getSteamID64()] = persona; }, "steamcommunity"); -}; \ No newline at end of file +}; diff --git a/components/confirmations.js b/components/confirmations.js index 04681e1a..3fa88c64 100644 --- a/components/confirmations.js +++ b/components/confirmations.js @@ -1,8 +1,9 @@ -var SteamCommunity = require('../index.js'); -var Cheerio = require('cheerio'); -var SteamTotp = require('steam-totp'); +const Cheerio = require('cheerio'); +const SteamTotp = require('steam-totp'); -var CConfirmation = require('../classes/CConfirmation.js'); +const SteamCommunity = require('../index.js'); + +const CConfirmation = require('../classes/CConfirmation.js'); /** * Get a list of your account's currently outstanding confirmations. diff --git a/components/groups.js b/components/groups.js index 5bd42b42..d9a3ef70 100644 --- a/components/groups.js +++ b/components/groups.js @@ -1,9 +1,11 @@ -var SteamCommunity = require('../index.js'); -var SteamID = require('steamid'); -var xml2js = require('xml2js'); -var Cheerio = require('cheerio'); -var Helpers = require('./helpers.js'); -var EResult = SteamCommunity.EResult; +const Cheerio = require('cheerio'); +const SteamID = require('steamid'); +const XML2JS = require('xml2js'); + +const Helpers = require('./helpers.js'); +const SteamCommunity = require('../index.js'); + +const EResult = SteamCommunity.EResult; SteamCommunity.prototype.getGroupMembers = function(gid, callback, members, link, addresses, addressIdx) { members = members || []; @@ -46,7 +48,7 @@ SteamCommunity.prototype.getGroupMembers = function(gid, callback, members, link return; } - xml2js.parseString(body, function(err, result) { + XML2JS.parseString(body, function(err, result) { if (err) { callback(err); return; @@ -129,7 +131,7 @@ SteamCommunity.prototype.getAllGroupAnnouncements = function(gid, time, callback return; } - xml2js.parseString(body, function(err, results) { + XML2JS.parseString(body, function(err, results) { if(err) { return callback(err); } @@ -398,7 +400,7 @@ SteamCommunity.prototype.setGroupPlayerOfTheWeek = function(gid, steamID, callba return; } - xml2js.parseString(body, function(err, results) { + XML2JS.parseString(body, function(err, results) { if(err) { callback(err); return; @@ -634,7 +636,7 @@ SteamCommunity.prototype.respondToGroupJoinRequests = function(gid, steamIDs, ap if (typeof gid === 'string') { gid = new SteamID(gid); } - + var rgAccounts = (!Array.isArray(steamIDs) ? [steamIDs] : steamIDs).map(sid => sid.toString()); this.httpRequestPost({ diff --git a/components/http.js b/components/http.js index ebb18468..f8d92f7a 100644 --- a/components/http.js +++ b/components/http.js @@ -1,4 +1,4 @@ -var SteamCommunity = require('../index.js'); +const SteamCommunity = require('../index.js'); SteamCommunity.prototype.httpRequest = function(uri, options, callback, source) { if (typeof uri === 'object') { diff --git a/components/market.js b/components/market.js index ac357251..cb6923ec 100644 --- a/components/market.js +++ b/components/market.js @@ -1,5 +1,6 @@ -var SteamCommunity = require('../index.js'); -var Cheerio = require('cheerio'); +const Cheerio = require('cheerio'); + +const SteamCommunity = require('../index.js'); /** * Get a list of all apps on the market diff --git a/components/twofactor.js b/components/twofactor.js index 651c691a..c02828de 100644 --- a/components/twofactor.js +++ b/components/twofactor.js @@ -1,7 +1,8 @@ -var SteamTotp = require('steam-totp'); -var SteamCommunity = require('../index.js'); +const SteamTotp = require('steam-totp'); -var ETwoFactorTokenType = { +const SteamCommunity = require('../index.js'); + +const ETwoFactorTokenType = { "None": 0, // No token-based two-factor authentication "ValveMobileApp": 1, // Tokens generated using Valve's special charset (5 digits, alphanumeric) "ThirdParty": 2 // Tokens generated using literally everyone else's standard charset (6 digits, numeric). This is disabled. diff --git a/components/webapi.js b/components/webapi.js index dc507fac..ee246c2b 100644 --- a/components/webapi.js +++ b/components/webapi.js @@ -1,4 +1,4 @@ -var SteamCommunity = require('../index.js'); +const SteamCommunity = require('../index.js'); SteamCommunity.prototype.getWebApiKey = function(domain, callback) { var self = this; @@ -14,7 +14,7 @@ SteamCommunity.prototype.getWebApiKey = function(domain, callback) { if(body.match(/

Access Denied<\/h2>/)) { return callback(new Error("Access Denied")); } - + if(body.match(/You must have a validated email address to create a Steam Web API key./)) { return callback(new Error("You must have a validated email address to create a Steam Web API key.")); } diff --git a/index.js b/index.js index 112a7ae4..c2a15b5a 100644 --- a/index.js +++ b/index.js @@ -1,19 +1,21 @@ require('@doctormckay/stats-reporter').setup(require('./package.json')); +const EventEmitter = require('events').EventEmitter; const hex2b64 = require('node-bignumber').hex2b64; const Request = require('request'); const RSA = require('node-bignumber').Key; const SteamID = require('steamid'); +const Util = require('util'); const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"; -require('util').inherits(SteamCommunity, require('events').EventEmitter); +Util.inherits(SteamCommunity, EventEmitter); module.exports = SteamCommunity; SteamCommunity.SteamID = SteamID; SteamCommunity.ConfirmationType = require('./resources/EConfirmationType.js'); -SteamCommunity.EResult = require('./resources/EResult.js') +SteamCommunity.EResult = require('./resources/EResult.js'); function SteamCommunity(options) { From 8e9e3723f29f33601e1eb82e87f508605aa1da07 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 24 Sep 2019 04:23:40 -0400 Subject: [PATCH 07/68] Code cleanup for chat.js --- components/chat.js | 141 ++++++++++++++++++++++----------------------- 1 file changed, 68 insertions(+), 73 deletions(-) diff --git a/components/chat.js b/components/chat.js index adc476ae..d2eceefa 100644 --- a/components/chat.js +++ b/components/chat.js @@ -12,7 +12,7 @@ SteamCommunity.PersonaStateFlag = require('../resources/EPersonaStateFlag.js'); * @param {string} uiMode */ SteamCommunity.prototype.chatLogon = function(interval, uiMode) { - if(this.chatState == SteamCommunity.ChatState.LoggingOn || this.chatState == SteamCommunity.ChatState.LoggedOn) { + if (this.chatState == SteamCommunity.ChatState.LoggingOn || this.chatState == SteamCommunity.ChatState.LoggedOn) { return; } @@ -22,48 +22,47 @@ SteamCommunity.prototype.chatLogon = function(interval, uiMode) { this.emit('debug', 'Requesting chat WebAPI token'); this.chatState = SteamCommunity.ChatState.LoggingOn; - var self = this; - this.getWebApiOauthToken(function(err, token) { - if(err) { - var fatal = err.message.indexOf('not authorized') != -1; + this.getWebApiOauthToken((err, token) => { + if (err) { + let fatal = err.message.indexOf('not authorized') != -1; if (!fatal) { - self.chatState = SteamCommunity.ChatState.LogOnFailed; - setTimeout(self.chatLogon.bind(self), 5000); + this.chatState = SteamCommunity.ChatState.LogOnFailed; + setTimeout(this.chatLogon.bind(this), 5000); } else { - self.chatState = SteamCommunity.ChatState.Offline; + this.chatState = SteamCommunity.ChatState.Offline; } - self.emit('chatLogOnFailed', err, fatal); - self.emit('debug', "Cannot get oauth token: " + err.message); + this.emit('chatLogOnFailed', err, fatal); + this.emit('debug', "Cannot get oauth token: " + err.message); return; } - self.httpRequestPost({ + this.httpRequestPost({ "uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Logon/v1", "form": { "ui_mode": uiMode, "access_token": token }, "json": true - }, function(err, response, body) { - if(err || response.statusCode != 200) { - self.chatState = SteamCommunity.ChatState.LogOnFailed; - self.emit('chatLogOnFailed', err ? err : new Error("HTTP error " + response.statusCode), false); - self.emit('debug', 'Error logging into webchat: ' + (err ? err.message : "HTTP error " + response.statusCode)); - setTimeout(self.chatLogon.bind(self), 5000); + }, (err, response, body) => { + if (err || response.statusCode != 200) { + this.chatState = SteamCommunity.ChatState.LogOnFailed; + this.emit('chatLogOnFailed', err ? err : new Error("HTTP error " + response.statusCode), false); + this.emit('debug', 'Error logging into webchat: ' + (err ? err.message : "HTTP error " + response.statusCode)); + setTimeout(this.chatLogon.bind(this), 5000); return; } - if(body.error != 'OK') { - self.chatState = SteamCommunity.ChatState.LogOnFailed; - self.emit('chatLogOnFailed', new Error(body.error), false); - self.emit('debug', 'Error logging into webchat: ' + body.error); - setTimeout(self.chatLogon.bind(self), 5000); + if (body.error != 'OK') { + this.chatState = SteamCommunity.ChatState.LogOnFailed; + this.emit('chatLogOnFailed', new Error(body.error), false); + this.emit('debug', 'Error logging into webchat: ' + body.error); + setTimeout(this.chatLogon.bind(this), 5000); return; } - self._chat = { + this._chat = { "umqid": body.umqid, "message": body.message, "accessToken": token, @@ -71,11 +70,11 @@ SteamCommunity.prototype.chatLogon = function(interval, uiMode) { "uiMode": uiMode }; - self.chatFriends = {}; + this.chatFriends = {}; - self.chatState = SteamCommunity.ChatState.LoggedOn; - self.emit('chatLoggedOn'); - self._chatPoll(); + this.chatState = SteamCommunity.ChatState.LoggedOn; + this.emit('chatLoggedOn'); + this._chatPoll(); }, "steamcommunity"); }); }; @@ -88,22 +87,21 @@ SteamCommunity.prototype.chatLogon = function(interval, uiMode) { * @param {function} [callback] */ SteamCommunity.prototype.chatMessage = function(recipient, text, type, callback) { - if(this.chatState != SteamCommunity.ChatState.LoggedOn) { + if (this.chatState != SteamCommunity.ChatState.LoggedOn) { throw new Error("Chat must be logged on before messages can be sent"); } - if(typeof recipient === 'string') { + if (typeof recipient === 'string') { recipient = new SteamID(recipient); } - if(typeof type === 'function') { + if (typeof type === 'function') { callback = type; type = 'saytext'; } type = type || 'saytext'; - var self = this; this.httpRequestPost({ "uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Message/v1", "form": { @@ -114,8 +112,8 @@ SteamCommunity.prototype.chatMessage = function(recipient, text, type, callback) "umqid": this._chat.umqid }, "json": true - }, function(err, response, body) { - if(!callback) { + }, (err, response, body) => { + if (!callback) { return; } @@ -124,7 +122,7 @@ SteamCommunity.prototype.chatMessage = function(recipient, text, type, callback) return; } - if(body.error != 'OK') { + if (body.error != 'OK') { callback(new Error(body.error)); } else { callback(null); @@ -136,23 +134,22 @@ SteamCommunity.prototype.chatMessage = function(recipient, text, type, callback) * @deprecated No support for new Steam chat. Use steam-user instead. */ SteamCommunity.prototype.chatLogoff = function() { - var self = this; this.httpRequestPost({ "uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Logoff/v1", "form": { "access_token": this._chat.accessToken, "umqid": this._chat.umqid } - }, function(err, response, body) { - if(err || response.statusCode != 200) { - self.emit('debug', 'Error logging off of chat: ' + (err ? err.message : "HTTP error " + response.statusCode)); - setTimeout(self.chatLogoff.bind(self), 1000); + }, (err, response, body) => { + if (err || response.statusCode != 200) { + this.emit('debug', 'Error logging off of chat: ' + (err ? err.message : "HTTP error " + response.statusCode)); + setTimeout(this.chatLogoff.bind(this), 1000); } else { - self.emit('chatLoggedOff'); - clearTimeout(self._chat.timer); - delete self._chat; - delete self.chatFriends; - self.chatState = SteamCommunity.ChatState.Offline; + this.emit('chatLoggedOff'); + clearTimeout(this._chat.timer); + delete this._chat; + delete this.chatFriends; + this.chatState = SteamCommunity.ChatState.Offline; } }, "steamcommunity"); }; @@ -163,68 +160,67 @@ SteamCommunity.prototype.chatLogoff = function() { SteamCommunity.prototype._chatPoll = function() { this.emit('debug', 'Doing chat poll'); - var self = this; this.httpRequestPost({ "uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Poll/v1", "form": { - "umqid": self._chat.umqid, - "message": self._chat.message, + "umqid": this._chat.umqid, + "message": this._chat.message, "pollid": 1, "sectimeout": 20, "secidletime": 0, "use_accountids": 1, - "access_token": self._chat.accessToken + "access_token": this._chat.accessToken }, "json": true - }, function(err, response, body) { - if (self.chatState == SteamCommunity.ChatState.Offline) { + }, (err, response, body) => { + if (this.chatState == SteamCommunity.ChatState.Offline) { return; } - self._chat.timer = setTimeout(self._chatPoll.bind(self), self._chat.interval); + this._chat.timer = setTimeout(this._chatPoll.bind(this), this._chat.interval); - if(err || response.statusCode != 200) { - self.emit('debug', 'Error in chat poll: ' + (err ? err.message : "HTTP error " + response.statusCode)); + if (err || response.statusCode != 200) { + this.emit('debug', 'Error in chat poll: ' + (err ? err.message : "HTTP error " + response.statusCode)); if (err.message == "Not Logged On") { - self._relogWebChat(); + this._relogWebChat(); } return; } - if(!body || body.error != 'OK') { - self.emit('debug', 'Error in chat poll: ' + (body && body.error ? body.error : "Malformed response")); + if (!body || body.error != 'OK') { + this.emit('debug', 'Error in chat poll: ' + (body && body.error ? body.error : "Malformed response")); if (body && body.error && body.error == "Not Logged On") { - self._relogWebChat(); + this._relogWebChat(); } return; } - self._chat.message = body.messagelast; + this._chat.message = body.messagelast; (body.messages || []).forEach(function(message) { - var sender = new SteamID(); + let sender = new SteamID(); sender.universe = SteamID.Universe.PUBLIC; sender.type = SteamID.Type.INDIVIDUAL; sender.instance = SteamID.Instance.DESKTOP; sender.accountid = message.accountid_from; - switch(message.type) { + switch (message.type) { case 'personastate': - self._chatUpdatePersona(sender); + this._chatUpdatePersona(sender); break; case 'saytext': - self.emit('chatMessage', sender, message.text); + this.emit('chatMessage', sender, message.text); break; case 'typing': - self.emit('chatTyping', sender); + this.emit('chatTyping', sender); break; default: - self.emit('debug', 'Unhandled chat message type: ' + message.type); + this.emit('debug', 'Unhandled chat message type: ' + message.type); } }); }, "steamcommunity"); @@ -250,24 +246,23 @@ SteamCommunity.prototype._chatUpdatePersona = function(steamID) { } this.emit('debug', 'Updating persona data for ' + steamID); - var self = this; this.httpRequest({ "uri": "https://steamcommunity.com/chat/friendstate/" + steamID.accountid, "json": true - }, function(err, response, body) { - if (!self.chatFriends || self.chatState == SteamCommunity.ChatState.Offline) { + }, (err, response, body) => { + if (!this.chatFriends || this.chatState == SteamCommunity.ChatState.Offline) { return; // welp } - if(err || response.statusCode != 200) { - self.emit('debug', 'Chat update persona error: ' + (err ? err.message : "HTTP error " + response.statusCode)); + if (err || response.statusCode != 200) { + this.emit('debug', 'Chat update persona error: ' + (err ? err.message : "HTTP error " + response.statusCode)); setTimeout(function() { - self._chatUpdatePersona(steamID); + this._chatUpdatePersona(steamID); }, 2000); return; } - var persona = { + let persona = { "steamID": steamID, "personaName": body.m_strName, "personaState": body.m_ePersonaState, @@ -278,7 +273,7 @@ SteamCommunity.prototype._chatUpdatePersona = function(steamID) { "inGameName": body.m_strInGameName || null }; - self.emit('chatPersonaState', steamID, persona); - self.chatFriends[steamID.getSteamID64()] = persona; + this.emit('chatPersonaState', steamID, persona); + this.chatFriends[steamID.getSteamID64()] = persona; }, "steamcommunity"); }; From ac8e4a62dede60f3f9e1185e29037004715081ee Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 24 Sep 2019 04:28:05 -0400 Subject: [PATCH 08/68] Code cleanup for confirmations.js --- components/confirmations.js | 131 +++++++++++++++++------------------- 1 file changed, 63 insertions(+), 68 deletions(-) diff --git a/components/confirmations.js b/components/confirmations.js index 3fa88c64..6007f4e6 100644 --- a/components/confirmations.js +++ b/components/confirmations.js @@ -12,23 +12,21 @@ const CConfirmation = require('../classes/CConfirmation.js'); * @param {SteamCommunity~getConfirmations} callback - Called when the list of confirmations is received */ SteamCommunity.prototype.getConfirmations = function(time, key, callback) { - var self = this; - - request(this, "conf", key, time, "conf", null, false, function(err, body) { - if(err) { + request(this, "conf", key, time, "conf", null, false, (err, body) => { + if (err) { if (err.message == "Invalid protocol: steammobile:") { err.message = "Not Logged In"; - self._notifySessionExpired(err); + this._notifySessionExpired(err); } callback(err); return; } - var $ = Cheerio.load(body); - var empty = $('#mobileconf_empty'); - if(empty.length > 0) { - if(!$(empty).hasClass('mobileconf_done')) { + let $ = Cheerio.load(body); + let empty = $('#mobileconf_empty'); + if (empty.length > 0) { + if (!$(empty).hasClass('mobileconf_done')) { // An error occurred callback(new Error(empty.find('div:nth-of-type(2)').text())); } else { @@ -39,18 +37,18 @@ SteamCommunity.prototype.getConfirmations = function(time, key, callback) { } // We have something to confirm - var confirmations = $('#mobileconf_list'); - if(!confirmations) { + let confirmations = $('#mobileconf_list'); + if (!confirmations) { callback(new Error("Malformed response")); return; } - var confs = []; - Array.prototype.forEach.call(confirmations.find('.mobileconf_list_entry'), function(conf) { + let confs = []; + Array.prototype.forEach.call(confirmations.find('.mobileconf_list_entry'), (conf) => { conf = $(conf); - var img = conf.find('.mobileconf_list_entry_icon img'); - confs.push(new CConfirmation(self, { + let img = conf.find('.mobileconf_list_entry_icon img'); + confs.push(new CConfirmation(this, { "id": conf.data('confid'), "type": conf.data('type'), "creator": conf.data('creator'), @@ -69,7 +67,7 @@ SteamCommunity.prototype.getConfirmations = function(time, key, callback) { /** * @callback SteamCommunity~getConfirmations * @param {Error|null} err - An Error object on failure, or null on success - * @param {CConfirmation[]} confirmations - An array of CConfirmation objects + * @param {CConfirmation[]} [confirmations] - An array of CConfirmation objects */ /** @@ -80,20 +78,20 @@ SteamCommunity.prototype.getConfirmations = function(time, key, callback) { * @param {SteamCommunity~getConfirmationOfferID} callback */ SteamCommunity.prototype.getConfirmationOfferID = function(confID, time, key, callback) { - request(this, "details/" + confID, key, time, "details", null, true, function(err, body) { - if(err) { + request(this, "details/" + confID, key, time, "details", null, true, (err, body) => { + if (err) { callback(err); return; } - if(!body.success) { + if (!body.success) { callback(new Error("Cannot load confirmation details")); return; } - var $ = Cheerio.load(body.html); - var offer = $('.tradeoffer'); - if(offer.length < 1) { + let $ = Cheerio.load(body.html); + let offer = $('.tradeoffer'); + if (offer.length < 1) { callback(null, null); return; } @@ -110,7 +108,7 @@ SteamCommunity.prototype.getConfirmationOfferID = function(confID, time, key, ca /** * Confirm or cancel a given confirmation. - * @param {int|int[]} confID - The ID of the confirmation in question, or an array of confirmation IDs + * @param {int|int[]|string|string[]} confID - The ID of the confirmation in question, or an array of confirmation IDs * @param {string|string[]} confKey - The confirmation key associated with the confirmation in question (or an array of them) (not a TOTP key, the `key` property of CConfirmation) * @param {int} time - The unix timestamp with which the following key was generated * @param {string} key - The confirmation key that was generated using the preceding time and the tag "allow" (if accepting) or "cancel" (if not accepting) @@ -122,22 +120,22 @@ SteamCommunity.prototype.respondToConfirmation = function(confID, confKey, time, "op": accept ? "allow" : "cancel", "cid": confID, "ck": confKey - }, true, function(err, body) { - if(!callback) { + }, true, (err, body) => { + if (!callback) { return; } - if(err) { + if (err) { callback(err); return; } - if(body.success) { + if (body.success) { callback(null); return; } - if(body.message) { + if (body.message) { callback(new Error(body.message)); return; } @@ -153,60 +151,59 @@ SteamCommunity.prototype.respondToConfirmation = function(confID, confKey, time, * @param {SteamCommunity~genericErrorCallback} callback */ SteamCommunity.prototype.acceptConfirmationForObject = function(identitySecret, objectID, callback) { - var self = this; this._usedConfTimes = this._usedConfTimes || []; - if (typeof this._timeOffset !== 'undefined') { - // time offset is already known and saved - doConfirmation(); - } else { - SteamTotp.getTimeOffset(function(err, offset) { - if (err) { - callback(err); - return; - } - - self._timeOffset = offset; - doConfirmation(); - - setTimeout(function() { - // Delete the saved time offset after 12 hours because why not - delete self._timeOffset; - }, 1000 * 60 * 60 * 12); - }); - } - - function doConfirmation() { - var offset = self._timeOffset; - var time = SteamTotp.time(offset); - self.getConfirmations(time, SteamTotp.getConfirmationKey(identitySecret, time, "conf"), function(err, confs) { + let doConfirmation = () => { + let offset = this._timeOffset; + let time = SteamTotp.time(offset); + this.getConfirmations(time, SteamTotp.getConfirmationKey(identitySecret, time, "conf"), (err, confs) => { if (err) { callback(err); return; } - var conf = confs.filter(function(conf) { return conf.creator == objectID; }); - if (conf.length == 0) { + let conf = confs.find(conf => conf.creator == objectID); + if (!conf) { callback(new Error("Could not find confirmation for object " + objectID)); return; } - conf = conf[0]; - // make sure we don't reuse the same time - var localOffset = 0; + let localOffset = 0; do { time = SteamTotp.time(offset) + localOffset++; - } while (self._usedConfTimes.indexOf(time) != -1); + } while (this._usedConfTimes.indexOf(time) != -1); - self._usedConfTimes.push(time); - if (self._usedConfTimes.length > 60) { - self._usedConfTimes.splice(0, self._usedConfTimes.length - 60); // we don't need to save more than 60 entries + this._usedConfTimes.push(time); + if (this._usedConfTimes.length > 60) { + this._usedConfTimes.splice(0, this._usedConfTimes.length - 60); // we don't need to save more than 60 entries } conf.respond(time, SteamTotp.getConfirmationKey(identitySecret, time, "allow"), true, callback); }); + }; + + if (typeof this._timeOffset !== 'undefined') { + // time offset is already known and saved + doConfirmation(); + } else { + SteamTotp.getTimeOffset((err, offset) => { + if (err) { + callback(err); + return; + } + + this._timeOffset = offset; + doConfirmation(); + + setTimeout(() => { + // Delete the saved time offset after 12 hours because why not + delete this._timeOffset; + }, 1000 * 60 * 60 * 12); + }); } + + }; /** @@ -218,9 +215,7 @@ SteamCommunity.prototype.acceptConfirmationForObject = function(identitySecret, * @param {function} callback */ SteamCommunity.prototype.acceptAllConfirmations = function(time, confKey, allowKey, callback) { - var self = this; - - this.getConfirmations(time, confKey, function(err, confs) { + this.getConfirmations(time, confKey, (err, confs) => { if (err) { callback(err); return; @@ -231,7 +226,7 @@ SteamCommunity.prototype.acceptAllConfirmations = function(time, confKey, allowK return; } - self.respondToConfirmation(confs.map(function(conf) { return conf.id; }), confs.map(function(conf) { return conf.key; }), time, allowKey, true, function(err) { + this.respondToConfirmation(confs.map(conf => conf.id), confs.map(conf => conf.key), time, allowKey, true, (err) => { if (err) { callback(err); return; @@ -255,7 +250,7 @@ function request(community, url, key, time, tag, params, json, callback) { params.m = "android"; params.tag = tag; - var req = { + let req = { "method": url == 'multiajaxop' ? 'POST' : 'GET', "uri": "https://steamcommunity.com/mobileconf/" + url, "json": !!json @@ -267,7 +262,7 @@ function request(community, url, key, time, tag, params, json, callback) { req.form = params; } - community.httpRequest(req, function(err, response, body) { + community.httpRequest(req, (err, response, body) => { if (err) { callback(err); return; From d7cfc396a72d6ebad5c77fb26df350c88c9f402c Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 24 Sep 2019 04:38:27 -0400 Subject: [PATCH 09/68] Code cleanup for groups.js --- components/chat.js | 10 +- components/confirmations.js | 2 +- components/groups.js | 340 ++++++++++++++++++------------------ 3 files changed, 172 insertions(+), 180 deletions(-) diff --git a/components/chat.js b/components/chat.js index d2eceefa..f409eab7 100644 --- a/components/chat.js +++ b/components/chat.js @@ -75,7 +75,7 @@ SteamCommunity.prototype.chatLogon = function(interval, uiMode) { this.chatState = SteamCommunity.ChatState.LoggedOn; this.emit('chatLoggedOn'); this._chatPoll(); - }, "steamcommunity"); + }, 'steamcommunity'); }); }; @@ -127,7 +127,7 @@ SteamCommunity.prototype.chatMessage = function(recipient, text, type, callback) } else { callback(null); } - }, "steamcommunity"); + }, 'steamcommunity'); }; /** @@ -151,7 +151,7 @@ SteamCommunity.prototype.chatLogoff = function() { delete this.chatFriends; this.chatState = SteamCommunity.ChatState.Offline; } - }, "steamcommunity"); + }, 'steamcommunity'); }; /** @@ -223,7 +223,7 @@ SteamCommunity.prototype._chatPoll = function() { this.emit('debug', 'Unhandled chat message type: ' + message.type); } }); - }, "steamcommunity"); + }, 'steamcommunity'); }; /** @@ -275,5 +275,5 @@ SteamCommunity.prototype._chatUpdatePersona = function(steamID) { this.emit('chatPersonaState', steamID, persona); this.chatFriends[steamID.getSteamID64()] = persona; - }, "steamcommunity"); + }, 'steamcommunity'); }; diff --git a/components/confirmations.js b/components/confirmations.js index 6007f4e6..e3cd09a0 100644 --- a/components/confirmations.js +++ b/components/confirmations.js @@ -269,5 +269,5 @@ function request(community, url, key, time, tag, params, json, callback) { } callback(null, body); - }, "steamcommunity"); + }, 'steamcommunity'); } diff --git a/components/groups.js b/components/groups.js index ac127948..156e7327 100644 --- a/components/groups.js +++ b/components/groups.js @@ -11,14 +11,15 @@ SteamCommunity.prototype.getGroupMembers = function(gid, callback, members, link members = members || []; if (!link) { - if (typeof gid !== 'string') { + if (typeof gid != 'string') { // It's a SteamID object - link = "http://steamcommunity.com/gid/" + gid.toString() + "/memberslistxml/?xml=1"; + link = "https://steamcommunity.com/gid/" + gid.toString() + "/memberslistxml/?xml=1"; } else { try { - var sid = new SteamID(gid); + // new SteamID could throw which is why we have this funky-looking try/catch set up here + let sid = new SteamID(gid); if (sid.type == SteamID.Type.CLAN && sid.isValid()) { - link = "http://steamcommunity.com/gid/" + sid.getSteamID64() + "/memberslistxml/?xml=1"; + link = "https://steamcommunity.com/gid/" + sid.getSteamID64() + "/memberslistxml/?xml=1"; } else { throw new Error("Doesn't particularly matter what this message is"); } @@ -30,42 +31,39 @@ SteamCommunity.prototype.getGroupMembers = function(gid, callback, members, link addressIdx = addressIdx || 0; - var options = {}; + let options = {}; options.uri = link; - if(addresses) { - if(addressIdx >= addresses.length) { + if (addresses) { + if (addressIdx >= addresses.length) { addressIdx = 0; } options.localAddress = addresses[addressIdx]; } - var self = this; - this.httpRequest(options, function(err, response, body) { + this.httpRequest(options, (err, response, body) => { if (err) { callback(err); return; } - XML2JS.parseString(body, function(err, result) { + XML2JS.parseString(body, (err, result) => { if (err) { callback(err); return; } - members = members.concat(result.memberList.members[0].steamID64.map(function(steamID) { - return new SteamID(steamID); - })); + members = members.concat(result.memberList.members[0].steamID64.map(sid => new SteamID(sid))); if (result.memberList.nextPageLink) { addressIdx++; - self.getGroupMembers(gid, callback, members, result.memberList.nextPageLink[0], addresses, addressIdx); + this.getGroupMembers(gid, callback, members, result.memberList.nextPageLink[0], addresses, addressIdx); } else { callback(null, members); } }); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.getGroupMembersEx = function(gid, addresses, callback) { @@ -73,38 +71,36 @@ SteamCommunity.prototype.getGroupMembersEx = function(gid, addresses, callback) }; SteamCommunity.prototype.joinGroup = function(gid, callback) { - if(typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } - var self = this; this.httpRequestPost({ "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64(), "form": { "action": "join", "sessionID": this.getSessionID() } - }, function(err, response, body) { - if(!callback) { + }, (err, response, body) => { + if (!callback) { return; } callback(err || null); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.leaveGroup = function(gid, callback) { - if(typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } - var self = this; this._myProfile("home_process", { "sessionID": this.getSessionID(), "action": "leaveGroup", "groupId": gid.getSteamID64() - }, function(err, response, body) { - if(!callback) { + }, (err, response, body) => { + if (!callback) { return; } @@ -113,57 +109,53 @@ SteamCommunity.prototype.leaveGroup = function(gid, callback) { }; SteamCommunity.prototype.getAllGroupAnnouncements = function(gid, time, callback) { - if(typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } - if(typeof time === 'function') { + if (typeof time == 'function') { callback = time; time = new Date(0); // The beginnig of time... } - var self = this; this.httpRequest({ "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/rss/" - }, function(err, response, body) { + }, (err, response, body) => { if (err) { callback(err); return; } - XML2JS.parseString(body, function(err, results) { - if(err) { + XML2JS.parseString(body, (err, results) => { + if (err) { return callback(err); } - if(!results.rss.channel[0].item) { + if (!results.rss.channel[0].item) { return callback(null, []); } - var announcements = results.rss.channel[0].item.map(function(announcement) { - var splitLink = announcement.link[0].split('/'); + let announcements = results.rss.channel[0].item.map((announcement) => { + let splitLink = announcement.link[0].split('/'); return { headline: announcement.title[0], - content: announcement.description[0], - date: new Date(announcement.pubDate[0]), - author: (typeof announcement.author === 'undefined') ? null : announcement.author[0], - aid: splitLink[splitLink.length - 1] + content: announcement.description[0], + date: new Date(announcement.pubDate[0]), + author: (typeof announcement.author === 'undefined') ? null : announcement.author[0], + aid: splitLink[splitLink.length - 1] } - }).filter(function(announcement) { - return (announcement.date > time); - }); + }).filter(announcement => announcement.date > time); return callback(null, announcements); }); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content, callback) { - if(typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } - var self = this; this.httpRequestPost({ "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/announcements", "form": { @@ -174,23 +166,21 @@ SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content "languages[0][headline]": headline, "languages[0][body]": content } - }, function(err, response, body) { - if(!callback) { + }, (err, response, body) => { + if (!callback) { return; } callback(err || null); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.editGroupAnnouncement = function(gid, aid, headline, content, callback) { - if(typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } - var self = this; - - var submitData = { + let submitData = { "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/announcements", "form": { "sessionID": this.getSessionID(), @@ -204,66 +194,71 @@ SteamCommunity.prototype.editGroupAnnouncement = function(gid, aid, headline, co } }; - this.httpRequestPost(submitData, function(err, response, body) { - if(!callback) { + this.httpRequestPost(submitData, (err, response, body) => { + if (!callback) { return; } callback(err || null); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.deleteGroupAnnouncement = function(gid, aid, callback) { - if(typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } - var self = this; - - var submitData = { + let submitData = { "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/announcements/delete/" + aid + "?sessionID=" + this.getSessionID() }; - this.httpRequestGet(submitData, function(err, response, body) { - if(!callback) { + this.httpRequestGet(submitData, (err, response, body) => { + if (!callback) { return; } callback(err || null); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.scheduleGroupEvent = function(gid, name, type, description, time, server, callback) { - if(typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } // Event types: ChatEvent - Chat, OtherEvent - A lil somethin somethin, PartyEvent - Party!, MeetingEvent - Important meeting, SpecialCauseEvent - Special cause (charity ball?), MusicAndArtsEvent - Music or Art type thing, SportsEvent - Sporting endeavor, TripEvent - Out of town excursion // Passing a number for type will make it a game event for that appid - if(typeof server === 'function') { - callback = server; - server = {"ip": "", "password": ""}; - } else if(typeof server === 'string') { - server = {"ip": server, "password": ""}; - } else if(typeof server !== 'object') { - server = {"ip": "", "password": ""}; + switch (typeof server) { + case 'function': + callback = server; + server = {ip: '', password: ''}; + break; + + case 'string': + server = {ip: server, password: ''}; + break; + + default: + if (typeof server != object) { + server = {ip: '', password: ''}; + } } - var form = { + let form = { "sessionid": this.getSessionID(), "action": "newEvent", "tzOffset": new Date().getTimezoneOffset() * -60, "name": name, - "type": (typeof type === 'number' || !isNaN(parseInt(type, 10)) ? "GameEvent" : type), - "appID": (typeof type === 'number' || !isNaN(parseInt(type, 10)) ? type : ''), + "type": (typeof type == 'number' || !isNaN(parseInt(type, 10)) ? "GameEvent" : type), + "appID": (typeof type == 'number' || !isNaN(parseInt(type, 10)) ? type : ''), "serverIP": server.ip, "serverPassword": server.password, "notes": description, "eventQuickTime": "now" }; - if(time === null) { + if (time === null) { form.startDate = 'MM/DD/YY'; form.startHour = '12'; form.startMinute = '00'; @@ -277,20 +272,19 @@ SteamCommunity.prototype.scheduleGroupEvent = function(gid, name, type, descript form.timeChoice = 'specific'; } - var self = this; this.httpRequestPost({ "uri": "https://steamcommunity.com/gid/" + gid.toString() + "/eventEdit", - "form": form - }, function(err, response, body) { - if(!callback) { + form + }, (err, response, body) => { + if (!callback) { return; } callback(err || null); - }, "steamcommunity"); + }, 'steamcommunity'); }; -SteamCommunity.prototype.editGroupEvent = function (gid, id, name, type, description, time, server, callback) { +SteamCommunity.prototype.editGroupEvent = function(gid, id, name, type, description, time, server, callback) { if (typeof gid === 'string') { gid = new SteamID(gid); } @@ -298,23 +292,30 @@ SteamCommunity.prototype.editGroupEvent = function (gid, id, name, type, descrip // Event types: ChatEvent - Chat, OtherEvent - A lil somethin somethin, PartyEvent - Party!, MeetingEvent - Important meeting, SpecialCauseEvent - Special cause (charity ball?), MusicAndArtsEvent - Music or Art type thing, SportsEvent - Sporting endeavor, TripEvent - Out of town excursion // Passing a number for type will make it a game event for that appid - if (typeof server === 'function') { - callback = server; - server = {"ip": "", "password": ""}; - } else if (typeof server === 'string') { - server = {"ip": server, "password": ""}; - } else if (typeof server !== 'object') { - server = {"ip": "", "password": ""}; + switch (typeof server) { + case 'function': + callback = server; + server = {ip: '', password: ''}; + break; + + case 'string': + server = {ip: server, password: ''}; + break; + + default: + if (typeof server != object) { + server = {ip: '', password: ''}; + } } - var form = { + let form = { "sessionid": this.getSessionID(), "action": "updateEvent", "eventID": id, "tzOffset": new Date().getTimezoneOffset() * -60, "name": name, - "type": (typeof type === 'number' || !isNaN(parseInt(type, 10)) ? "GameEvent" : type), - "appID": (typeof type === 'number' || !isNaN(parseInt(type, 10)) ? type : ''), + "type": (typeof type == 'number' || !isNaN(parseInt(type, 10)) ? "GameEvent" : type), + "appID": (typeof type == 'number' || !isNaN(parseInt(type, 10)) ? type : ''), "serverIP": server.ip, "serverPassword": server.password, "notes": description, @@ -335,53 +336,50 @@ SteamCommunity.prototype.editGroupEvent = function (gid, id, name, type, descrip form.timeChoice = 'specific'; } - var self = this; this.httpRequestPost({ "uri": "https://steamcommunity.com/gid/" + gid.toString() + "/eventEdit", "form": form - }, function(err, response, body) { - if(!callback) { + }, (err, response, body) => { + if (!callback) { return; } callback(err || null); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.deleteGroupEvent = function(gid, id, callback) { - if (typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } - var form = { + let form = { "sessionid": this.getSessionID(), "action": "deleteEvent", "eventID": id }; - var self = this; this.httpRequestPost({ "uri": "https://steamcommunity.com/gid/" + gid.toString() + "/eventEdit", - "form": form - }, function(err, response, body) { - if(!callback) { + form + }, (err, response, body) => { + if (!callback) { return; } callback(err || null); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.setGroupPlayerOfTheWeek = function(gid, steamID, callback) { - if(typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } - if(typeof steamID === 'string') { + if (typeof steamID == 'string') { steamID = new SteamID(steamID); } - var self = this; this.httpRequestPost({ "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/potwEdit", "form": { @@ -390,41 +388,40 @@ SteamCommunity.prototype.setGroupPlayerOfTheWeek = function(gid, steamID, callba "memberId": steamID.getSteam3RenderedID(), "sessionid": this.getSessionID() } - }, function(err, response, body) { - if(!callback) { + }, (err, response, body) => { + if (!callback) { return; } - if(err || response.statusCode != 200) { + if (err || response.statusCode != 200) { callback(err || new Error("HTTP error " + response.statusCode)); return; } - XML2JS.parseString(body, function(err, results) { - if(err) { + XML2JS.parseString(body, (err, results) => { + if (err) { callback(err); return; } - if(results.response.results[0] == 'OK') { + if (results.response.results[0] == 'OK') { callback(null, new SteamID(results.response.oldPOTW[0]), new SteamID(results.response.newPOTW[0])); } else { callback(new Error(results.response.results[0])); } }); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.kickGroupMember = function(gid, steamID, callback) { - if(typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } - if(typeof steamID === 'string') { + if (typeof steamID == 'string') { steamID = new SteamID(steamID); } - var self = this; this.httpRequestPost({ "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/membersManage", "form": { @@ -433,57 +430,56 @@ SteamCommunity.prototype.kickGroupMember = function(gid, steamID, callback) { "memberId": steamID.getSteamID64(), "queryString": "" } - }, function(err, response, body) { - if(!callback) { + }, (err, response, body) => { + if (!callback) { return; } callback(err || null); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.getGroupHistory = function(gid, page, callback) { - if(typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } - if(typeof page === 'function') { + if (typeof page == 'function') { callback = page; page = 1; } - var self = this; - this.httpRequest("https://steamcommunity.com/gid/" + gid.getSteamID64() + "/history?p=" + page, function(err, response, body) { + this.httpRequest("https://steamcommunity.com/gid/" + gid.getSteamID64() + "/history?p=" + page, (err, response, body) => { if (err) { callback(err); return; } - var $ = Cheerio.load(body); - var output = {}; + let $ = Cheerio.load(body); + let output = {}; - var paging = $('.group_paging p').text(); - var match = paging.match(/(\d+) - (\d+) of (\d+)/); + let paging = $('.group_paging p').text(); + let match = paging.match(/(\d+) - (\d+) of (\d+)/); - if(match) { + if (match) { output.first = parseInt(match[1], 10); output.last = parseInt(match[2], 10); output.total = parseInt(match[3], 10); } output.items = []; - var currentYear = (new Date()).getFullYear(); - var lastDate = Date.now(); + let currentYear = (new Date()).getFullYear(); + let lastDate = Date.now(); - Array.prototype.forEach.call($('.historyItem, .historyItemb'), function(item) { - var data = {}; + Array.prototype.forEach.call($('.historyItem, .historyItemb'), (item) => { + let data = {}; - var $item = $(item); + let $item = $(item); data.type = $item.find('.historyShort').text().replace(/ /g, ''); - var users = $item.find('.whiteLink[data-miniprofile]'); - var sid; - if(users[0]) { + let users = $item.find('.whiteLink[data-miniprofile]'); + let sid; + if (users[0]) { sid = new SteamID(); sid.universe = SteamID.Universe.PUBLIC; sid.type = SteamID.Type.INDIVIDUAL; @@ -492,7 +488,7 @@ SteamCommunity.prototype.getGroupHistory = function(gid, page, callback) { data.user = sid; } - if(users[1]) { + if (users[1]) { sid = new SteamID(); sid.universe = SteamID.Universe.PUBLIC; sid.type = SteamID.Type.INDIVIDUAL; @@ -502,14 +498,14 @@ SteamCommunity.prototype.getGroupHistory = function(gid, page, callback) { } // Figure out the date. Of course there's no year, because Valve - var dateParts = $item.find('.historyDate').text().split('@'); - var date = dateParts[0].trim().replace(/(st|nd|th)$/, '').trim() + ', ' + currentYear; - var time = dateParts[1].trim().replace(/(am|pm)/, ' $1'); + let dateParts = $item.find('.historyDate').text().split('@'); + let date = dateParts[0].trim().replace(/(st|nd|th)$/, '').trim() + ', ' + currentYear; + let time = dateParts[1].trim().replace(/(am|pm)/, ' $1'); date = new Date(date + ' ' + time + ' UTC'); // If this date is in the future, or it's later than the previous one, decrement the year - if(date.getTime() > lastDate) { + if (date.getTime() > lastDate) { date.setFullYear(date.getFullYear() - 1); } @@ -519,15 +515,15 @@ SteamCommunity.prototype.getGroupHistory = function(gid, page, callback) { }); callback(null, output); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.getAllGroupComments = function(gid, from, count, callback) { - if (typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } - var options = { + let options = { "uri": "https://steamcommunity.com/comment/Clan/render/" + gid.getSteamID64() + "/-1/", "form": { "start": from, @@ -535,22 +531,20 @@ SteamCommunity.prototype.getAllGroupComments = function(gid, from, count, callba } }; - var self = this; - this.httpRequestPost(options, function(err, response, body) { + this.httpRequestPost(options, (err, response, body) => { if (err) { callback(err); return; } - var comments = []; + let comments = []; - var $ = Cheerio.load(JSON.parse(body).comments_html); + let $ = Cheerio.load(JSON.parse(body).comments_html); - $(".commentthread_comment_content").each(function () { - var comment = {}; - var cachedSelector; + $('.commentthread_comment_content').each(function() { + let comment = {}; - var $selector = $(this).find(".commentthread_author_link"); + let $selector = $(this).find(".commentthread_author_link"); comment.authorName = $($selector).find("bdi").text(); comment.authorId = $($selector).attr("href").replace(/https?:\/\/steamcommunity.com\/(id|profiles)\//, ""); comment.date = Helpers.decodeSteamTime($(this).find(".commentthread_comment_timestamp").text().trim()); @@ -563,19 +557,19 @@ SteamCommunity.prototype.getAllGroupComments = function(gid, from, count, callba }); callback(null, comments); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.deleteGroupComment = function(gid, cid, callback) { - if (typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } - if (typeof cid !== 'string') { + if (typeof cid != 'string') { cid = cid.toString(); } - var options = { + let options = { "uri": "https://steamcommunity.com/comment/Clan/delete/" + gid.getSteamID64() + "/-1/", "form": { "sessionid": this.getSessionID(), @@ -583,22 +577,21 @@ SteamCommunity.prototype.deleteGroupComment = function(gid, cid, callback) { } }; - var self = this; - this.httpRequestPost(options, function(err, response, body) { - if(!callback) { + this.httpRequestPost(options, (err, response, body) => { + if (!callback) { return; } callback(err || null); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.postGroupComment = function(gid, message, callback) { - if (typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } - var options = { + let options = { "uri": "https://steamcommunity.com/comment/Clan/post/" + gid.getSteamID64() + "/-1/", "form": { "comment": message, @@ -607,14 +600,13 @@ SteamCommunity.prototype.postGroupComment = function(gid, message, callback) { } }; - var self = this; - this.httpRequestPost(options, function(err, response, body) { - if(!callback) { + this.httpRequestPost(options, (err, response, body) => { + if (!callback) { return; } callback(err || null); - }, "steamcommunity"); + }, 'steamcommunity'); }; /** @@ -623,7 +615,7 @@ SteamCommunity.prototype.postGroupComment = function(gid, message, callback) { * @param {function} callback - First argument is null/Error, second is array of SteamID objects */ SteamCommunity.prototype.getGroupJoinRequests = function(gid, callback) { - if (typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } @@ -633,20 +625,20 @@ SteamCommunity.prototype.getGroupJoinRequests = function(gid, callback) { return; } - var matches = body.match(/JoinRequests_ApproveDenyUser\(\W*['"](\d+)['"],\W0\W\)/g); + let matches = body.match(/JoinRequests_ApproveDenyUser\(\W*['"](\d+)['"],\W0\W\)/g); if (!matches) { // no pending requests callback(null, []); return; } - var requests = []; - for (var i = 0; i < matches.length; i++) { + let requests = []; + for (let i = 0; i < matches.length; i++) { requests.push(new SteamID("[U:1:" + matches[i].match(/JoinRequests_ApproveDenyUser\(\W*['"](\d+)['"],\W0\W\)/)[1] + "]")); } callback(null, requests); - }, "steamcommunity"); + }, 'steamcommunity'); }; /** @@ -657,11 +649,11 @@ SteamCommunity.prototype.getGroupJoinRequests = function(gid, callback) { * @param {function} callback - Takes only an Error object/null as the first argument */ SteamCommunity.prototype.respondToGroupJoinRequests = function(gid, steamIDs, approve, callback) { - if (typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } - var rgAccounts = (!Array.isArray(steamIDs) ? [steamIDs] : steamIDs).map(sid => sid.toString()); + let rgAccounts = (!Array.isArray(steamIDs) ? [steamIDs] : steamIDs).map(sid => sid.toString()); this.httpRequestPost({ "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/joinRequestsManage", @@ -678,13 +670,13 @@ SteamCommunity.prototype.respondToGroupJoinRequests = function(gid, steamIDs, ap } if (body != EResult.OK) { - var err = new Error(EResult[body] || ("Error " + body)); + let err = new Error(EResult[body] || ("Error " + body)); err.eresult = body; callback(err); } else { callback(null); } - }, "steamcommunity"); + }, 'steamcommunity'); }; /** @@ -694,7 +686,7 @@ SteamCommunity.prototype.respondToGroupJoinRequests = function(gid, steamIDs, ap * @param {function} callback - Takes only an Error object/null as the first argument */ SteamCommunity.prototype.respondToAllGroupJoinRequests = function(gid, approve, callback) { - if (typeof gid === 'string') { + if (typeof gid == 'string') { gid = new SteamID(gid); } @@ -713,11 +705,11 @@ SteamCommunity.prototype.respondToAllGroupJoinRequests = function(gid, approve, } if (body != EResult.OK) { - var err = new Error(EResult[body] || ("Error " + body)); + let err = new Error(EResult[body] || ("Error " + body)); err.eresult = body; callback(err); } else { callback(null); } - }, "steamcommunity"); + }, 'steamcommunity'); }; From 30ebf333b46f2431e8f5f7850886a8310d00614b Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 24 Sep 2019 04:41:11 -0400 Subject: [PATCH 10/68] Code cleanup for helpers.js --- components/helpers.js | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/components/helpers.js b/components/helpers.js index 1da54437..5c3f7057 100644 --- a/components/helpers.js +++ b/components/helpers.js @@ -1,37 +1,34 @@ const EResult = require('../resources/EResult.js'); exports.isSteamID = function(input) { - var keys = Object.keys(input); + let keys = Object.keys(input); if (keys.length != 4) { return false; } // Make sure it has the keys we expect - keys = keys.filter(function(item) { - return ['universe', 'type', 'instance', 'accountid'].indexOf(item) != -1; - }); - - return keys.length == 4; + keys.sort(); + return keys.join(',') == 'accountid,instance,type,universe'; }; exports.decodeSteamTime = function(time) { - var date = new Date(); + let date = new Date(); - if (time.includes("@")) { - var parts = time.split('@'); - if (!parts[0].includes(",")) { + if (time.includes('@')) { + let parts = time.split('@'); + if (!parts[0].includes(',')) { // no year, assume current year - parts[0] += ", " + date.getFullYear(); + parts[0] += ', ' + date.getFullYear(); } - date = new Date(parts.join('@').replace(/(am|pm)/, ' $1') + " UTC"); // add a space so JS can decode it + date = new Date(parts.join('@').replace(/(am|pm)/, ' $1') + ' UTC'); // add a space so JS can decode it } else { // Relative date - var amount = time.replace(/(\d) (minutes|hour|hours) ago/, "$1"); + let amount = time.replace(/(\d) (minutes|hour|hours) ago/, '$1'); - if(time.includes("minutes")) { + if (time.includes('minutes')) { date.setMinutes(date.getMinutes() - amount); - } else if(time.match(/hour|hours/)) { + } else if (time.match(/hour|hours/)) { date.setHours(date.getHours() - amount); } } @@ -50,7 +47,7 @@ exports.eresultError = function(eresult) { return null; } - var err = new Error(EResult[eresult] || ("Error " + eresult)); + let err = new Error(EResult[eresult] || ("Error " + eresult)); err.eresult = eresult; return err; }; From fa253920e33801c7e32fdc10837b7b2fffbf4468 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 24 Sep 2019 04:43:51 -0400 Subject: [PATCH 11/68] Code cleanup for http.js --- components/http.js | 57 +++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/components/http.js b/components/http.js index f8d92f7a..59112118 100644 --- a/components/http.js +++ b/components/http.js @@ -1,12 +1,12 @@ const SteamCommunity = require('../index.js'); SteamCommunity.prototype.httpRequest = function(uri, options, callback, source) { - if (typeof uri === 'object') { + if (typeof uri == 'object') { source = callback; callback = options; options = uri; uri = options.url || options.uri; - } else if (typeof options === 'function') { + } else if (typeof options == 'function') { source = callback; callback = options; options = {}; @@ -19,18 +19,12 @@ SteamCommunity.prototype.httpRequest = function(uri, options, callback, source) delete this._httpRequestConvenienceMethod; } - var requestID = ++this._httpRequestID; - source = source || ""; + let requestID = ++this._httpRequestID; + source = source || ''; - var self = this; - var continued = false; + let continued = false; - if (!this.onPreHttpRequest || !this.onPreHttpRequest(requestID, source, options, continueRequest)) { - // No pre-hook, or the pre-hook doesn't want to delay the request. - continueRequest(null); - } - - function continueRequest(err) { + let continueRequest = (err) => { if (continued) { return; } @@ -45,14 +39,14 @@ SteamCommunity.prototype.httpRequest = function(uri, options, callback, source) return; } - self.request(options, function (err, response, body) { - var hasCallback = !!callback; - var httpError = options.checkHttpError !== false && self._checkHttpError(err, response, callback, body); - var communityError = !options.json && options.checkCommunityError !== false && self._checkCommunityError(body, httpError ? function () {} : callback); // don't fire the callback if hasHttpError did it already - var tradeError = !options.json && options.checkTradeError !== false && self._checkTradeError(body, httpError || communityError ? function () {} : callback); // don't fire the callback if either of the previous already did - var jsonError = options.json && options.checkJsonError !== false && !body ? new Error("Malformed JSON response") : null; + this.request(options, (err, response, body) => { + let hasCallback = !!callback; + let httpError = options.checkHttpError !== false && this._checkHttpError(err, response, callback, body); + let communityError = !options.json && options.checkCommunityError !== false && this._checkCommunityError(body, httpError ? noop : callback); // don't fire the callback if hasHttpError did it already + let tradeError = !options.json && options.checkTradeError !== false && this._checkTradeError(body, httpError || communityError ? noop : callback); // don't fire the callback if either of the previous already did + let jsonError = options.json && options.checkJsonError !== false && !body ? new Error("Malformed JSON response") : null; - self.emit('postHttpRequest', requestID, source, options, httpError || communityError || tradeError || jsonError || null, response, body, { + this.emit('postHttpRequest', requestID, source, options, httpError || communityError || tradeError || jsonError || null, response, body, { "hasCallback": hasCallback, "httpError": httpError, "communityError": communityError, @@ -62,12 +56,17 @@ SteamCommunity.prototype.httpRequest = function(uri, options, callback, source) if (hasCallback && !(httpError || communityError || tradeError)) { if (jsonError) { - callback.call(self, jsonError, response); + callback.call(this, jsonError, response); } else { - callback.apply(self, arguments); + callback.apply(this, arguments); } } }); + }; + + if (!this.onPreHttpRequest || !this.onPreHttpRequest(requestID, source, options, continueRequest)) { + // No pre-hook, or the pre-hook doesn't want to delay the request. + continueRequest(null); } }; @@ -98,7 +97,7 @@ SteamCommunity.prototype._checkHttpError = function(err, response, callback, bod return err; } - if (response.statusCode == 403 && typeof response.body === 'string' && response.body.match(/
Enter your PIN below to exit Family View.<\/div>/)) { + if (response.statusCode == 403 && typeof response.body == 'string' && response.body.match(/
Enter your PIN below to exit Family View.<\/div>/)) { err = new Error("Family View Restricted"); callback(err, response, body); return err; @@ -115,16 +114,16 @@ SteamCommunity.prototype._checkHttpError = function(err, response, callback, bod }; SteamCommunity.prototype._checkCommunityError = function(html, callback) { - var err; + let err; - if(typeof html === 'string' && html.match(/

Sorry!<\/h1>/)) { - var match = html.match(/

(.+)<\/h3>/); + if (typeof html == 'string' && html.match(/

Sorry!<\/h1>/)) { + let match = html.match(/

(.+)<\/h3>/); err = new Error(match ? match[1] : "Unknown error occurred"); callback(err); return err; } - if (typeof html === 'string' && html.match(/g_steamID = false;/) && html.match(/

Sign In<\/h1>/)) { + if (typeof html == 'string' && html.match(/g_steamID = false;/) && html.match(/

Sign In<\/h1>/)) { err = new Error("Not Logged In"); callback(err); this._notifySessionExpired(err); @@ -139,12 +138,14 @@ SteamCommunity.prototype._checkTradeError = function(html, callback) { return false; } - var match = html.match(/
\s*([^<]+)\s*<\/div>/); + let match = html.match(/
\s*([^<]+)\s*<\/div>/); if (match) { - var err = new Error(match[1].trim()); + let err = new Error(match[1].trim()); callback(err); return err; } return false; }; + +function noop() {} From 247d804c7782ff9f488fb01c1e35b6470022de5b Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 22 Jul 2021 03:44:40 -0400 Subject: [PATCH 12/68] Cleaned up market.js --- components/helpers.js | 7 ++- components/market.js | 136 +++++++++++++++++++----------------------- 2 files changed, 66 insertions(+), 77 deletions(-) diff --git a/components/helpers.js b/components/helpers.js index 5c3f7057..0dd0e6cf 100644 --- a/components/helpers.js +++ b/components/helpers.js @@ -39,15 +39,16 @@ exports.decodeSteamTime = function(time) { /** * Get an Error object for a particular EResult * @param {int} eresult + * @param {string} [message] - If eresult is a failure code and message exists, this message will be used in the Error object instead * @returns {null|Error} */ -exports.eresultError = function(eresult) { - if (eresult == EResult.OK) { +exports.eresultError = function(eresult, message) { + if (!eresult || eresult == EResult.OK) { // no error return null; } - let err = new Error(EResult[eresult] || ("Error " + eresult)); + let err = new Error(message || EResult[eresult] || ("Error " + eresult)); err.eresult = eresult; return err; }; diff --git a/components/market.js b/components/market.js index a56c56e4..15b16329 100644 --- a/components/market.js +++ b/components/market.js @@ -8,8 +8,7 @@ const Helpers = require('./helpers.js'); * @param {function} callback - First argument is null|Error, second is an object of appid => name */ SteamCommunity.prototype.getMarketApps = function(callback) { - var self = this; - this.httpRequest('https://steamcommunity.com/market/', function (err, response, body) { + this.httpRequest('https://steamcommunity.com/market/', (err, response, body) => { if (err) { callback(err); return; @@ -19,17 +18,17 @@ SteamCommunity.prototype.getMarketApps = function(callback) { if ($('.market_search_game_button_group')) { let apps = {}; $('.market_search_game_button_group a.game_button').each(function (i, element) { - var e = Cheerio.load(element); - var name = e('.game_button_game_name').text().trim(); - var url = element.attribs.href; - var appid = url.substr(url.indexOf('=') + 1); + let e = Cheerio.load(element); + let name = e('.game_button_game_name').text().trim(); + let url = element.attribs.href; + let appid = url.substr(url.indexOf('=') + 1); apps[appid] = name; }); callback(null, apps); } else { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); } - }, "steamcommunity"); + }, 'steamcommunity'); }; /** @@ -40,34 +39,32 @@ SteamCommunity.prototype.getMarketApps = function(callback) { */ SteamCommunity.prototype.getGemValue = function(appid, assetid, callback) { this._myProfile({ - "endpoint": "ajaxgetgoovalue/", - "qs": { - "sessionid": this.getSessionID(), - "appid": appid, - "contextid": 6, - "assetid": assetid + endpoint: 'ajaxgetgoovalue/', + qs: { + sessionid: this.getSessionID(), + appid: appid, + contextid: 6, + assetid: assetid }, - "checkHttpError": false, - "json": true + checkHttpError: false, + json: true }, null, (err, res, body) => { if (err) { callback(err); return; } - if (body.success && body.success != SteamCommunity.EResult.OK) { - let err = new Error(body.message || SteamCommunity.EResult[body.success]); - err.eresult = err.code = body.success; - callback(err); - return; + let err2 = Helpers.eresultError(body.success, body.message); + if (err2) { + return callback(err2); } if (!body.goo_value || !body.strTitle) { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); return; } - callback(null, {"promptTitle": body.strTitle, "gemValue": parseInt(body.goo_value, 10)}); + callback(null, {promptTitle: body.strTitle, gemValue: parseInt(body.goo_value, 10)}); }); }; @@ -80,34 +77,32 @@ SteamCommunity.prototype.getGemValue = function(appid, assetid, callback) { */ SteamCommunity.prototype.turnItemIntoGems = function(appid, assetid, expectedGemsValue, callback) { this._myProfile({ - "endpoint": "ajaxgrindintogoo/", - "json": true, - "checkHttpError": false + endpoint: 'ajaxgrindintogoo/', + json: true, + checkHttpError: false }, { - "appid": appid, - "contextid": 6, - "assetid": assetid, - "goo_value_expected": expectedGemsValue, - "sessionid": this.getSessionID() + appid: appid, + contextid: 6, + assetid: assetid, + goo_value_expected: expectedGemsValue, + sessionid: this.getSessionID() }, (err, res, body) => { if (err) { callback(err); return; } - if (body.success && body.success != SteamCommunity.EResult.OK) { - let err = new Error(body.message || SteamCommunity.EResult[body.success]); - err.eresult = err.code = body.success; - callback(err); - return; + let err2 = Helpers.eresultError(body.success, body.message); + if (err2) { + return callback(err2); } - if (!body['goo_value_received '] || !body.goo_value_total) { // lol valve - callback(new Error("Malformed response")); + if (!body['goo_value_received '] || !body.goo_value_total) { // lol valve, that trailing space is real + callback(new Error('Malformed response')); return; } - callback(null, {"gemsReceived": parseInt(body['goo_value_received '], 10), "totalGems": parseInt(body.goo_value_total, 10)}); + callback(null, {gemsReceived: parseInt(body['goo_value_received '], 10), totalGems: parseInt(body.goo_value_total, 10)}); }) }; @@ -119,28 +114,26 @@ SteamCommunity.prototype.turnItemIntoGems = function(appid, assetid, expectedGem */ SteamCommunity.prototype.openBoosterPack = function(appid, assetid, callback) { this._myProfile({ - "endpoint": "ajaxunpackbooster/", - "json": true, - "checkHttpError": false + endpoint: 'ajaxunpackbooster/', + json: true, + checkHttpError: false }, { - "appid": appid, - "communityitemid": assetid, - "sessionid": this.getSessionID() + appid: appid, + communityitemid: assetid, + sessionid: this.getSessionID() }, (err, res, body) => { if (err) { callback(err); return; } - if (body.success && body.success != SteamCommunity.EResult.OK) { - let err = new Error(body.message || SteamCommunity.EResult[body.success]); - err.eresult = err.code = body.success; - callback(err); - return; + let err2 = Helpers.eresultError(body.success, body.message); + if (err2) { + return callback(err2); } if (!body.rgItems) { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); return; } @@ -155,33 +148,31 @@ SteamCommunity.prototype.openBoosterPack = function(appid, assetid, callback) { */ SteamCommunity.prototype.getGiftDetails = function(giftID, callback) { this.httpRequestPost({ - "uri": "https://steamcommunity.com/gifts/" + giftID + "/validateunpack", - "form": { - "sessionid": this.getSessionID() + uri: `https://steamcommunity.com/gifts/${giftID}/validateunpack`, + form: { + sessionid: this.getSessionID() }, - "json": true + json: true }, (err, res, body) => { if (err) { callback(err); return; } - if (body.success && body.success != SteamCommunity.EResult.OK) { - let err = new Error(body.message || SteamCommunity.EResult[body.success]); - err.eresult = err.code = body.success; - callback(err); - return; + let err2 = Helpers.eresultError(body.success, body.message); + if (err2) { + return callback(err2); } if (!body.packageid || !body.gift_name) { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); return; } callback(null, { - "giftName": body.gift_name, - "packageID": parseInt(body.packageid, 10), - "owned": body.owned + giftName: body.gift_name, + packageID: parseInt(body.packageid, 10), + owned: body.owned }); }); }; @@ -193,23 +184,20 @@ SteamCommunity.prototype.getGiftDetails = function(giftID, callback) { */ SteamCommunity.prototype.redeemGift = function(giftID, callback) { this.httpRequestPost({ - "uri": "https://steamcommunity.com/gifts/" + giftID + "/unpack", - "form": { - "sessionid": this.getSessionID() + uri: `https://steamcommunity.com/gifts/${giftID}/unpack`, + form: { + sessionid: this.getSessionID() }, - "json": true + json: true }, (err, res, body) => { if (err) { callback(err); return; } - if (body.success && body.success != SteamCommunity.EResult.OK) { - - let err = new Error(body.message || SteamCommunity.EResult[body.success]); - err.eresult = err.code = body.success; - callback(err); - return; + let err2 = Helpers.eresultError(body.success, body.message); + if (err2) { + return callback(err2); } callback(null); From c550b380fc408990b5ea1fdbaeb40e0bad6f71b6 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 22 Jul 2021 03:54:38 -0400 Subject: [PATCH 13/68] Cleaned up profile.js --- components/helpers.js | 2 +- components/profile.js | 257 ++++++++++++++++++++---------------------- 2 files changed, 124 insertions(+), 135 deletions(-) diff --git a/components/helpers.js b/components/helpers.js index 0dd0e6cf..e493b625 100644 --- a/components/helpers.js +++ b/components/helpers.js @@ -43,7 +43,7 @@ exports.decodeSteamTime = function(time) { * @returns {null|Error} */ exports.eresultError = function(eresult, message) { - if (!eresult || eresult == EResult.OK) { + if (eresult == EResult.OK) { // no error return null; } diff --git a/components/profile.js b/components/profile.js index 32b14ac8..67b15b37 100644 --- a/components/profile.js +++ b/components/profile.js @@ -6,35 +6,42 @@ const Helpers = require('./helpers.js'); const SteamCommunity = require('../index.js'); SteamCommunity.PrivacyState = { - "Private": 1, - "FriendsOnly": 2, - "Public": 3 + Private: 1, + FriendsOnly: 2, + Public: 3 }; -var CommentPrivacyState = { - "1": 2, // private - "2": 0, // friends only - "3": 1 // anyone +const CommentPrivacyState = { + '1': 2, // private + '2': 0, // friends only + '3': 1 // anyone }; +/** + * Creates a profile page if you don't already have one. + * @param {function} callback + */ SteamCommunity.prototype.setupProfile = function(callback) { - var self = this; - this._myProfile("edit?welcomed=1", null, function(err, response, body) { - if(!callback) { + this._myProfile('edit?welcomed=1', null, (err, response, body) => { + if (!callback) { return; } - if(err || response.statusCode != 200) { - callback(err || new Error("HTTP error " + response.statusCode)); + if (err || response.statusCode != 200) { + callback(err || new Error('HTTP error ' + response.statusCode)); } else { callback(null); } }); }; +/** + * Edits your profile details. + * @param {object} settings + * @param {function} callback + */ SteamCommunity.prototype.editProfile = function(settings, callback) { - var self = this; - this._myProfile('edit/info', null, function(err, response, body) { + this._myProfile('edit/info', null, (err, response, body) => { if (err || response.statusCode != 200) { if (callback) { callback(err || new Error('HTTP error ' + response.statusCode)); @@ -43,8 +50,8 @@ SteamCommunity.prototype.editProfile = function(settings, callback) { return; } - var $ = Cheerio.load(body); - var existingSettings = $('#profile_edit_config').data('profile-edit'); + let $ = Cheerio.load(body); + let existingSettings = $('#profile_edit_config').data('profile-edit'); if (!existingSettings || !existingSettings.strPersonaName) { if (callback) { callback(new Error('Malformed response')); @@ -53,7 +60,7 @@ SteamCommunity.prototype.editProfile = function(settings, callback) { return; } - var values = { + let values = { sessionID: self.getSessionID(), type: 'profileSave', weblink_1_title: '', @@ -72,7 +79,7 @@ SteamCommunity.prototype.editProfile = function(settings, callback) { json: 1 }; - for (var i in settings) { + for (let i in settings) { if(!settings.hasOwnProperty(i)) { continue; } @@ -131,9 +138,9 @@ SteamCommunity.prototype.editProfile = function(settings, callback) { } } - self._myProfile('edit', values, function(err, response, body) { + this._myProfile('edit', values, (err, response, body) => { if (settings.customURL) { - delete self._profileURL; + delete this._profileURL; } if (!callback) { @@ -146,10 +153,10 @@ SteamCommunity.prototype.editProfile = function(settings, callback) { } try { - var json = JSON.parse(body); - if (!json.success || json.success != 1) { - callback(new Error(json.errmsg || 'Request was not successful')); - return; + let json = JSON.parse(body); + let err2 = Helpers.eresultError(json.success, json.errmsg); + if (err2) { + return callback(err2); } callback(null); @@ -170,8 +177,8 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) { return; } - var $ = Cheerio.load(body); - var existingSettings = $('#profile_edit_config').data('profile-edit'); + let $ = Cheerio.load(body); + let existingSettings = $('#profile_edit_config').data('profile-edit'); if (!existingSettings || !existingSettings.Privacy) { if (callback) { callback(new Error('Malformed response')); @@ -182,10 +189,10 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) { // PrivacySettings => {PrivacyProfile, PrivacyInventory, PrivacyInventoryGifts, PrivacyOwnedGames, PrivacyPlaytime} // eCommentPermission - var privacy = existingSettings.Privacy.PrivacySettings; - var commentPermission = existingSettings.Privacy.eCommentPermission; + let privacy = existingSettings.Privacy.PrivacySettings; + let commentPermission = existingSettings.Privacy.eCommentPermission; - for (var i in settings) { + for (let i in settings) { if (!settings.hasOwnProperty(i)) { continue; } @@ -230,7 +237,7 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) { Privacy: JSON.stringify(privacy), eCommentPermission: commentPermission } - }, null, function(err, response, body) { + }, null, (err, response, body) => { if (err || response.statusCode != 200) { if (callback) { callback(err || new Error('HTTP error ' + response.statusCode)); @@ -239,11 +246,9 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) { return; } - if (body.success != 1) { - if (callback) { - callback(new Error(body.success ? 'Error ' + body.success : 'Request was not successful')); - } - + let err2 = Helpers.eresultError(body.success); + if (err2) { + callback && callback(err2); return; } @@ -262,71 +267,27 @@ SteamCommunity.prototype.uploadAvatar = function(image, format, callback) { // are we logged in? if (!this.steamID) { - callback(new Error("Not Logged In")); + callback(new Error('Not Logged In')); return; } - var self = this; - - if(image instanceof Buffer) { - doUpload(image); - } else if(image.match(/^https?:\/\//)) { - this.httpRequestGet({ - "uri": image, - "encoding": null - }, function(err, response, body) { - if(err || response.statusCode != 200) { - if(callback) { - callback(err ? new Error(err.message + " downloading image") : new Error("HTTP error " + response.statusCode + " downloading image")); - } - - return; - } - - if(!format) { - format = response.headers['content-type']; - } - - doUpload(body); - }, "steamcommunity"); - } else { - if(!format) { - format = image.match(/\.([^\.]+)$/); - if(format) { - format = format[1]; - } - } - - FS.readFile(image, function(err, file) { - if(err) { - if(callback) { - callback(err); - } - - return; - } - - doUpload(file); - }) - } - - function doUpload(buffer) { - if(!format) { - if(callback) { - callback(new Error("Unknown image format")); + const doUpload = (buffer) => { + if (!format) { + if (callback) { + callback(new Error('Unknown image format')); } return; } - if(format.match(/^image\//)) { + if (format.match(/^image\//)) { format = format.substring(6); } - var filename = ''; - var contentType = ''; + let filename = ''; + let contentType = ''; - switch(format.toLowerCase()) { + switch (format.toLowerCase()) { case 'jpg': case 'jpeg': filename = 'avatar.jpg'; @@ -344,68 +305,96 @@ SteamCommunity.prototype.uploadAvatar = function(image, format, callback) { break; default: - if(callback) { - callback(new Error("Unknown or invalid image format")); + if (callback) { + callback(new Error('Unknown or invalid image format')); } return; } - self.httpRequestPost({ - "uri": "https://steamcommunity.com/actions/FileUploader", - "formData": { - "MAX_FILE_SIZE": buffer.length, - "type": "player_avatar_image", - "sId": self.steamID.getSteamID64(), - "sessionid": self.getSessionID(), - "doSub": 1, - "json": 1, - "avatar": { - "value": buffer, - "options": { - "filename": filename, - "contentType": contentType + this.httpRequestPost({ + uri: 'https://steamcommunity.com/actions/FileUploader', + formData: { + MAX_FILE_SIZE: buffer.length, + type: 'player_avatar_image', + sId: this.steamID.getSteamID64(), + sessionid: this.getSessionID(), + doSub: 1, + json: 1, + avatar: { + value: buffer, + options: { + filename: filename, + contentType: contentType } } }, - "json": true - }, function(err, response, body) { - if(err) { - if(callback) { - callback(err); - } + json: true + }, (err, response, body) => { + if (err) { + callback && callback(err); + return; + } + if (body && !body.success && body.message) { + callback && callback(new Error(body.message)); return; } - if(body && !body.success && body.message) { - if(callback) { - callback(new Error(body.message)); - } + if (response.statusCode != 200) { + callback && callback(new Error(`HTTP error ${response.statusCode}`)); + return; + } + if(!body || !body.success) { + callback && callback(new Error('Malformed response')); return; } - if(response.statusCode != 200) { - if(callback) { - callback(new Error("HTTP error " + response.statusCode)); + callback && callback(null, body.images.full); + }, 'steamcommunity'); + }; + + if (image instanceof Buffer) { + doUpload(image); + } else if (image.match(/^https?:\/\//)) { + this.httpRequestGet({ + uri: image, + encoding: null + }, (err, response, body) => { + if (err || response.statusCode != 200) { + if (callback) { + callback(new Error(err ? `${err.message} downloading image` : `HTTP error ${response.statusCode} downloading image`)); } return; } - if(!body || !body.success) { - if(callback) { - callback(new Error("Malformed response")); + if (!format) { + format = response.headers['content-type']; + } + + doUpload(body); + }, 'steamcommunity'); + } else { + if (!format) { + format = image.match(/\.([^.]+)$/); + if (format) { + format = format[1]; + } + } + + FS.readFile(image, (err, file) => { + if (err) { + if (callback) { + callback(err); } return; } - if(callback) { - callback(null, body.images.full); - } - }, "steamcommunity"); + doUpload(file); + }) } }; @@ -421,10 +410,10 @@ SteamCommunity.prototype.postProfileStatus = function(statusText, options, callb options = {}; } - this._myProfile("ajaxpostuserstatus/", { - "appid": options.appID || 0, - "sessionid": this.getSessionID(), - "status_text": statusText + this._myProfile('ajaxpostuserstatus/', { + appid: options.appID || 0, + sessionid: this.getSessionID(), + status_text: statusText }, (err, res, body) => { try { body = JSON.parse(body); @@ -433,9 +422,9 @@ SteamCommunity.prototype.postProfileStatus = function(statusText, options, callb return; } - var match = body.blotter_html.match(/id="userstatus_(\d+)_/); + let match = body.blotter_html.match(/id="userstatus_(\d+)_/); if (!match) { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); return; } @@ -452,9 +441,9 @@ SteamCommunity.prototype.postProfileStatus = function(statusText, options, callb * @param {function} [callback] */ SteamCommunity.prototype.deleteProfileStatus = function(postID, callback) { - this._myProfile("ajaxdeleteuserstatus/", { - "sessionid": this.getSessionID(), - "postid": postID + this._myProfile('ajaxdeleteuserstatus/', { + sessionid: this.getSessionID(), + postid: postID }, (err, res, body) => { if (!callback) { return; @@ -463,7 +452,7 @@ SteamCommunity.prototype.deleteProfileStatus = function(postID, callback) { try { body = JSON.parse(body); if (!body.success) { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); return; } From 9a4be453ec1c30fa8ebd037958e5bf895df1a52e Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 22 Jul 2021 03:59:55 -0400 Subject: [PATCH 14/68] Cleaned up twofactor.js --- components/market.js | 2 +- components/profile.js | 2 +- components/twofactor.js | 168 +++++++++++++++++++--------------------- 3 files changed, 83 insertions(+), 89 deletions(-) diff --git a/components/market.js b/components/market.js index 15b16329..25049a45 100644 --- a/components/market.js +++ b/components/market.js @@ -14,7 +14,7 @@ SteamCommunity.prototype.getMarketApps = function(callback) { return; } - var $ = Cheerio.load(body); + let $ = Cheerio.load(body); if ($('.market_search_game_button_group')) { let apps = {}; $('.market_search_game_button_group a.game_button').each(function (i, element) { diff --git a/components/profile.js b/components/profile.js index 67b15b37..793af24e 100644 --- a/components/profile.js +++ b/components/profile.js @@ -61,7 +61,7 @@ SteamCommunity.prototype.editProfile = function(settings, callback) { } let values = { - sessionID: self.getSessionID(), + sessionID: this.getSessionID(), type: 'profileSave', weblink_1_title: '', weblink_1_url: '', diff --git a/components/twofactor.js b/components/twofactor.js index c02828de..490de261 100644 --- a/components/twofactor.js +++ b/components/twofactor.js @@ -1,160 +1,154 @@ const SteamTotp = require('steam-totp'); const SteamCommunity = require('../index.js'); +const Helpers = require('./helpers.js'); const ETwoFactorTokenType = { - "None": 0, // No token-based two-factor authentication - "ValveMobileApp": 1, // Tokens generated using Valve's special charset (5 digits, alphanumeric) - "ThirdParty": 2 // Tokens generated using literally everyone else's standard charset (6 digits, numeric). This is disabled. + None: 0, // No token-based two-factor authentication + ValveMobileApp: 1, // Tokens generated using Valve's special charset (5 digits, alphanumeric) + ThirdParty: 2 // Tokens generated using literally everyone else's standard charset (6 digits, numeric). This is disabled. }; SteamCommunity.prototype.enableTwoFactor = function(callback) { - var self = this; - - this.getWebApiOauthToken(function(err, token) { - if(err) { + this.getWebApiOauthToken((err, token) => { + if (err) { callback(err); return; } - self.httpRequestPost({ - "uri": "https://api.steampowered.com/ITwoFactorService/AddAuthenticator/v1/", - "form": { - "steamid": self.steamID.getSteamID64(), - "access_token": token, - "authenticator_time": Math.floor(Date.now() / 1000), - "authenticator_type": ETwoFactorTokenType.ValveMobileApp, - "device_identifier": SteamTotp.getDeviceID(self.steamID), - "sms_phone_id": "1" + this.httpRequestPost({ + uri: 'https://api.steampowered.com/ITwoFactorService/AddAuthenticator/v1/', + form: { + steamid: this.steamID.getSteamID64(), + access_token: token, + authenticator_time: Math.floor(Date.now() / 1000), + authenticator_type: ETwoFactorTokenType.ValveMobileApp, + device_identifier: SteamTotp.getDeviceID(this.steamID), + sms_phone_id: '1' }, - "json": true - }, function(err, response, body) { + json: true + }, (err, response, body) => { if (err) { callback(err); return; } - if(!body.response) { - callback(new Error("Malformed response")); + if (!body.response) { + callback(new Error('Malformed response')); return; } - if(body.response.status != 1) { - var error = new Error("Error " + body.response.status); - error.eresult = body.response.status; - callback(error); - return; + let err2 = Helpers.eresultError(body.response.status); + if (err2) { + return callback(err2); } callback(null, body.response); - }, "steamcommunity"); + }, 'steamcommunity'); }); }; SteamCommunity.prototype.finalizeTwoFactor = function(secret, activationCode, callback) { - var attemptsLeft = 30; - var diff = 0; - - var self = this; - this.getWebApiOauthToken(function(err, token) { - if(err) { - callback(err); - return; - } - - SteamTotp.getTimeOffset(function(err, offset, latency) { - if (err) { - callback(err); - return; - } - - diff = offset; - finalize(token); - }); - }); - - function finalize(token) { - var code = SteamTotp.generateAuthCode(secret, diff); - - self.httpRequestPost({ - "uri": "https://api.steampowered.com/ITwoFactorService/FinalizeAddAuthenticator/v1/", - "form": { - "steamid": self.steamID.getSteamID64(), - "access_token": token, - "authenticator_code": code, - "authenticator_time": Math.floor(Date.now() / 1000), - "activation_code": activationCode + let attemptsLeft = 30; + let diff = 0; + + const finalize = (token) => { + let code = SteamTotp.generateAuthCode(secret, diff); + + this.httpRequestPost({ + uri: 'https://api.steampowered.com/ITwoFactorService/FinalizeAddAuthenticator/v1/', + form: { + steamid: this.steamID.getSteamID64(), + access_token: token, + authenticator_code: code, + authenticator_time: Math.floor(Date.now() / 1000), + activation_code: activationCode }, - "json": true - }, function(err, response, body) { + json: true + }, (err, response, body) => { if (err) { callback(err); return; } - if(!body.response) { - callback(new Error("Malformed response")); + if (!body.response) { + callback(new Error('Malformed response')); return; } body = body.response; - if(body.server_time) { + if (body.server_time) { diff = body.server_time - Math.floor(Date.now() / 1000); } - if(body.status == 89) { - callback(new Error("Invalid activation code")); - } else if(body.want_more) { + if (body.status == SteamCommunity.EResult.TwoFactorActivationCodeMismatch) { + callback(new Error('Invalid activation code')); + } else if (body.want_more) { attemptsLeft--; diff += 30; finalize(token); - } else if(!body.success) { - callback(new Error("Error " + body.status)); + } else if (!body.success) { + callback(Helpers.eresultError(body.status)); } else { callback(null); } - }, "steamcommunity"); + }, 'steamcommunity'); } + + this.getWebApiOauthToken((err, token) => { + if (err) { + callback(err); + return; + } + + SteamTotp.getTimeOffset((err, offset, latency) => { + if (err) { + callback(err); + return; + } + + diff = offset; + finalize(token); + }); + }); }; SteamCommunity.prototype.disableTwoFactor = function(revocationCode, callback) { - var self = this; - - this.getWebApiOauthToken(function(err, token) { - if(err) { + this.getWebApiOauthToken((err, token) => { + if (err) { callback(err); return; } - self.httpRequestPost({ - "uri": "https://api.steampowered.com/ITwoFactorService/RemoveAuthenticator/v1/", - "form": { - "steamid": self.steamID.getSteamID64(), - "access_token": token, - "revocation_code": revocationCode, - "steamguard_scheme": 1 + this.httpRequestPost({ + uri: 'https://api.steampowered.com/ITwoFactorService/RemoveAuthenticator/v1/', + form: { + steamid: this.steamID.getSteamID64(), + access_token: token, + revocation_code: revocationCode, + steamguard_scheme: 1 }, - "json": true - }, function(err, response, body) { + json: true + }, (err, response, body) => { if (err) { callback(err); return; } - if(!body.response) { - callback(new Error("Malformed response")); + if (!body.response) { + callback(new Error('Malformed response')); return; } - if(!body.response.success) { - callback(new Error("Request failed")); + if (!body.response.success) { + callback(new Error('Request failed')); return; } // success = true means it worked callback(null); - }, "steamcommunity"); + }, 'steamcommunity'); }); }; From c69a5451eb8066166f713ae235b409b89d84568c Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 22 Jul 2021 04:02:38 -0400 Subject: [PATCH 15/68] Cleaned up webapi.js --- components/webapi.js | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/components/webapi.js b/components/webapi.js index b13f062f..cdc64e16 100644 --- a/components/webapi.js +++ b/components/webapi.js @@ -1,47 +1,46 @@ const SteamCommunity = require('../index.js'); SteamCommunity.prototype.getWebApiKey = function(domain, callback) { - var self = this; this.httpRequest({ - "uri": "https://steamcommunity.com/dev/apikey?l=english", - "followRedirect": false - }, function(err, response, body) { + uri: 'https://steamcommunity.com/dev/apikey?l=english', + followRedirect: false + }, (err, response, body) => { if (err) { callback(err); return; } - if(body.match(/

Access Denied<\/h2>/)) { - return callback(new Error("Access Denied")); + if (body.includes('

Access Denied

')) { + return callback(new Error('Access Denied')); } - if(body.match(/You must have a validated email address to create a Steam Web API key./)) { - return callback(new Error("You must have a validated email address to create a Steam Web API key.")); + if (body.includes('You must have a validated email address to create a Steam Web API key.')) { + return callback(new Error('You must have a validated email address to create a Steam Web API key.')); } - var match = body.match(/

Key: ([0-9A-F]+)<\/p>/); - if(match) { + let match = body.match(/

Key: ([0-9A-F]+)<\/p>/); + if (match) { // We already have an API key registered callback(null, match[1]); } else { // We need to register a new API key - self.httpRequestPost('https://steamcommunity.com/dev/registerkey?l=english', { - "form": { - "domain": domain, - "agreeToTerms": "agreed", - "sessionid": self.getSessionID(), - "Submit": "Register" + this.httpRequestPost('https://steamcommunity.com/dev/registerkey?l=english', { + form: { + domain, + agreeToTerms: 'agreed', + sessionid: this.getSessionID(), + Submit: 'Register' } - }, function(err, response, body) { + }, (err, response, body) => { if (err) { callback(err); return; } - self.getWebApiKey(domain, callback); - }, "steamcommunity"); + this.getWebApiKey(domain, callback); + }, 'steamcommunity'); } - }, "steamcommunity"); + }, 'steamcommunity'); }; /** From d093466d279b52c3241784dad118cecb2339f01c Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 22 Jul 2021 04:06:59 -0400 Subject: [PATCH 16/68] Removed getWebApiOauthToken --- components/chat.js | 279 ---------------------------------------- components/twofactor.js | 131 +++++++++---------- components/webapi.js | 12 -- index.js | 1 - 4 files changed, 61 insertions(+), 362 deletions(-) delete mode 100644 components/chat.js diff --git a/components/chat.js b/components/chat.js deleted file mode 100644 index f409eab7..00000000 --- a/components/chat.js +++ /dev/null @@ -1,279 +0,0 @@ -const SteamID = require('steamid'); - -const SteamCommunity = require('../index.js'); - -SteamCommunity.ChatState = require('../resources/EChatState.js'); -SteamCommunity.PersonaState = require('../resources/EPersonaState.js'); -SteamCommunity.PersonaStateFlag = require('../resources/EPersonaStateFlag.js'); - -/** - * @deprecated No support for new Steam chat. Use steam-user instead. - * @param {int} interval - * @param {string} uiMode - */ -SteamCommunity.prototype.chatLogon = function(interval, uiMode) { - if (this.chatState == SteamCommunity.ChatState.LoggingOn || this.chatState == SteamCommunity.ChatState.LoggedOn) { - return; - } - - interval = interval || 500; - uiMode = uiMode || "web"; - - this.emit('debug', 'Requesting chat WebAPI token'); - this.chatState = SteamCommunity.ChatState.LoggingOn; - - this.getWebApiOauthToken((err, token) => { - if (err) { - let fatal = err.message.indexOf('not authorized') != -1; - - if (!fatal) { - this.chatState = SteamCommunity.ChatState.LogOnFailed; - setTimeout(this.chatLogon.bind(this), 5000); - } else { - this.chatState = SteamCommunity.ChatState.Offline; - } - - this.emit('chatLogOnFailed', err, fatal); - this.emit('debug', "Cannot get oauth token: " + err.message); - return; - } - - this.httpRequestPost({ - "uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Logon/v1", - "form": { - "ui_mode": uiMode, - "access_token": token - }, - "json": true - }, (err, response, body) => { - if (err || response.statusCode != 200) { - this.chatState = SteamCommunity.ChatState.LogOnFailed; - this.emit('chatLogOnFailed', err ? err : new Error("HTTP error " + response.statusCode), false); - this.emit('debug', 'Error logging into webchat: ' + (err ? err.message : "HTTP error " + response.statusCode)); - setTimeout(this.chatLogon.bind(this), 5000); - return; - } - - if (body.error != 'OK') { - this.chatState = SteamCommunity.ChatState.LogOnFailed; - this.emit('chatLogOnFailed', new Error(body.error), false); - this.emit('debug', 'Error logging into webchat: ' + body.error); - setTimeout(this.chatLogon.bind(this), 5000); - return; - } - - this._chat = { - "umqid": body.umqid, - "message": body.message, - "accessToken": token, - "interval": interval, - "uiMode": uiMode - }; - - this.chatFriends = {}; - - this.chatState = SteamCommunity.ChatState.LoggedOn; - this.emit('chatLoggedOn'); - this._chatPoll(); - }, 'steamcommunity'); - }); -}; - -/** - * @deprecated No support for new Steam chat. Use steam-user instead. - * @param {string|SteamID} recipient - * @param {string} text - * @param {string} [type] - * @param {function} [callback] - */ -SteamCommunity.prototype.chatMessage = function(recipient, text, type, callback) { - if (this.chatState != SteamCommunity.ChatState.LoggedOn) { - throw new Error("Chat must be logged on before messages can be sent"); - } - - if (typeof recipient === 'string') { - recipient = new SteamID(recipient); - } - - if (typeof type === 'function') { - callback = type; - type = 'saytext'; - } - - type = type || 'saytext'; - - this.httpRequestPost({ - "uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Message/v1", - "form": { - "access_token": this._chat.accessToken, - "steamid_dst": recipient.toString(), - "text": text, - "type": type, - "umqid": this._chat.umqid - }, - "json": true - }, (err, response, body) => { - if (!callback) { - return; - } - - if (err) { - callback(err); - return; - } - - if (body.error != 'OK') { - callback(new Error(body.error)); - } else { - callback(null); - } - }, 'steamcommunity'); -}; - -/** - * @deprecated No support for new Steam chat. Use steam-user instead. - */ -SteamCommunity.prototype.chatLogoff = function() { - this.httpRequestPost({ - "uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Logoff/v1", - "form": { - "access_token": this._chat.accessToken, - "umqid": this._chat.umqid - } - }, (err, response, body) => { - if (err || response.statusCode != 200) { - this.emit('debug', 'Error logging off of chat: ' + (err ? err.message : "HTTP error " + response.statusCode)); - setTimeout(this.chatLogoff.bind(this), 1000); - } else { - this.emit('chatLoggedOff'); - clearTimeout(this._chat.timer); - delete this._chat; - delete this.chatFriends; - this.chatState = SteamCommunity.ChatState.Offline; - } - }, 'steamcommunity'); -}; - -/** - * @private - */ -SteamCommunity.prototype._chatPoll = function() { - this.emit('debug', 'Doing chat poll'); - - this.httpRequestPost({ - "uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Poll/v1", - "form": { - "umqid": this._chat.umqid, - "message": this._chat.message, - "pollid": 1, - "sectimeout": 20, - "secidletime": 0, - "use_accountids": 1, - "access_token": this._chat.accessToken - }, - "json": true - }, (err, response, body) => { - if (this.chatState == SteamCommunity.ChatState.Offline) { - return; - } - - this._chat.timer = setTimeout(this._chatPoll.bind(this), this._chat.interval); - - if (err || response.statusCode != 200) { - this.emit('debug', 'Error in chat poll: ' + (err ? err.message : "HTTP error " + response.statusCode)); - if (err.message == "Not Logged On") { - this._relogWebChat(); - } - - return; - } - - if (!body || body.error != 'OK') { - this.emit('debug', 'Error in chat poll: ' + (body && body.error ? body.error : "Malformed response")); - if (body && body.error && body.error == "Not Logged On") { - this._relogWebChat(); - } - - return; - } - - this._chat.message = body.messagelast; - - (body.messages || []).forEach(function(message) { - let sender = new SteamID(); - sender.universe = SteamID.Universe.PUBLIC; - sender.type = SteamID.Type.INDIVIDUAL; - sender.instance = SteamID.Instance.DESKTOP; - sender.accountid = message.accountid_from; - - switch (message.type) { - case 'personastate': - this._chatUpdatePersona(sender); - break; - - case 'saytext': - this.emit('chatMessage', sender, message.text); - break; - - case 'typing': - this.emit('chatTyping', sender); - break; - - default: - this.emit('debug', 'Unhandled chat message type: ' + message.type); - } - }); - }, 'steamcommunity'); -}; - -/** - * @private - */ -SteamCommunity.prototype._relogWebChat = function() { - this.emit('debug', "Relogging web chat"); - clearTimeout(this._chat.timer); - this.chatState = SteamCommunity.ChatState.Offline; - this.chatLogon(this._chat.interval, this._chat.uiMode); -}; - -/** - * @param {SteamID} steamID - * @private - */ -SteamCommunity.prototype._chatUpdatePersona = function(steamID) { - if (!this.chatFriends || this.chatState == SteamCommunity.ChatState.Offline) { - return; // we no longer care - } - - this.emit('debug', 'Updating persona data for ' + steamID); - this.httpRequest({ - "uri": "https://steamcommunity.com/chat/friendstate/" + steamID.accountid, - "json": true - }, (err, response, body) => { - if (!this.chatFriends || this.chatState == SteamCommunity.ChatState.Offline) { - return; // welp - } - - if (err || response.statusCode != 200) { - this.emit('debug', 'Chat update persona error: ' + (err ? err.message : "HTTP error " + response.statusCode)); - setTimeout(function() { - this._chatUpdatePersona(steamID); - }, 2000); - return; - } - - let persona = { - "steamID": steamID, - "personaName": body.m_strName, - "personaState": body.m_ePersonaState, - "personaStateFlags": body.m_nPersonaStateFlags || 0, - "avatarHash": body.m_strAvatarHash, - "inGame": !!body.m_bInGame, - "inGameAppID": body.m_nInGameAppID ? parseInt(body.m_nInGameAppID, 10) : null, - "inGameName": body.m_strInGameName || null - }; - - this.emit('chatPersonaState', steamID, persona); - this.chatFriends[steamID.getSteamID64()] = persona; - }, 'steamcommunity'); -}; diff --git a/components/twofactor.js b/components/twofactor.js index 490de261..0bcbb86b 100644 --- a/components/twofactor.js +++ b/components/twofactor.js @@ -10,56 +10,57 @@ const ETwoFactorTokenType = { }; SteamCommunity.prototype.enableTwoFactor = function(callback) { - this.getWebApiOauthToken((err, token) => { + if (!this.oAuthToken) { + return callback(new Error('enableTwoFactor can only be used when logged on via steamcommunity\'s `login` method without the `disableMobile` option.')); + } + + this.httpRequestPost({ + uri: 'https://api.steampowered.com/ITwoFactorService/AddAuthenticator/v1/', + form: { + steamid: this.steamID.getSteamID64(), + access_token: this.oAuthToken, + authenticator_time: Math.floor(Date.now() / 1000), + authenticator_type: ETwoFactorTokenType.ValveMobileApp, + device_identifier: SteamTotp.getDeviceID(this.steamID), + sms_phone_id: '1' + }, + json: true + }, (err, response, body) => { if (err) { callback(err); return; } - this.httpRequestPost({ - uri: 'https://api.steampowered.com/ITwoFactorService/AddAuthenticator/v1/', - form: { - steamid: this.steamID.getSteamID64(), - access_token: token, - authenticator_time: Math.floor(Date.now() / 1000), - authenticator_type: ETwoFactorTokenType.ValveMobileApp, - device_identifier: SteamTotp.getDeviceID(this.steamID), - sms_phone_id: '1' - }, - json: true - }, (err, response, body) => { - if (err) { - callback(err); - return; - } - - if (!body.response) { - callback(new Error('Malformed response')); - return; - } + if (!body.response) { + callback(new Error('Malformed response')); + return; + } - let err2 = Helpers.eresultError(body.response.status); - if (err2) { - return callback(err2); - } + let err2 = Helpers.eresultError(body.response.status); + if (err2) { + return callback(err2); + } - callback(null, body.response); - }, 'steamcommunity'); - }); + callback(null, body.response); + }, 'steamcommunity'); }; SteamCommunity.prototype.finalizeTwoFactor = function(secret, activationCode, callback) { + if (!this.oAuthToken) { + return callback(new Error('finalizeTwoFactor can only be used when logged on via steamcommunity\'s `login` method without the `disableMobile` option.')); + } + let attemptsLeft = 30; let diff = 0; - const finalize = (token) => { + const finalize = () => { let code = SteamTotp.generateAuthCode(secret, diff); this.httpRequestPost({ uri: 'https://api.steampowered.com/ITwoFactorService/FinalizeAddAuthenticator/v1/', form: { steamid: this.steamID.getSteamID64(), - access_token: token, + access_token: this.oAuthToken, authenticator_code: code, authenticator_time: Math.floor(Date.now() / 1000), activation_code: activationCode @@ -88,7 +89,7 @@ SteamCommunity.prototype.finalizeTwoFactor = function(secret, activationCode, ca attemptsLeft--; diff += 30; - finalize(token); + finalize(); } else if (!body.success) { callback(Helpers.eresultError(body.status)); } else { @@ -97,58 +98,48 @@ SteamCommunity.prototype.finalizeTwoFactor = function(secret, activationCode, ca }, 'steamcommunity'); } - this.getWebApiOauthToken((err, token) => { + SteamTotp.getTimeOffset((err, offset, latency) => { if (err) { callback(err); return; } - SteamTotp.getTimeOffset((err, offset, latency) => { - if (err) { - callback(err); - return; - } - - diff = offset; - finalize(token); - }); + diff = offset; + finalize(); }); }; SteamCommunity.prototype.disableTwoFactor = function(revocationCode, callback) { - this.getWebApiOauthToken((err, token) => { + if (!this.oAuthToken) { + return callback(new Error('disableTwoFactor can only be used when logged on via steamcommunity\'s `login` method without the `disableMobile` option.')); + } + + this.httpRequestPost({ + uri: 'https://api.steampowered.com/ITwoFactorService/RemoveAuthenticator/v1/', + form: { + steamid: this.steamID.getSteamID64(), + access_token: this.oAuthToken, + revocation_code: revocationCode, + steamguard_scheme: 1 + }, + json: true + }, (err, response, body) => { if (err) { callback(err); return; } - this.httpRequestPost({ - uri: 'https://api.steampowered.com/ITwoFactorService/RemoveAuthenticator/v1/', - form: { - steamid: this.steamID.getSteamID64(), - access_token: token, - revocation_code: revocationCode, - steamguard_scheme: 1 - }, - json: true - }, (err, response, body) => { - if (err) { - callback(err); - return; - } - - if (!body.response) { - callback(new Error('Malformed response')); - return; - } + if (!body.response) { + callback(new Error('Malformed response')); + return; + } - if (!body.response.success) { - callback(new Error('Request failed')); - return; - } + if (!body.response.success) { + callback(new Error('Request failed')); + return; + } - // success = true means it worked - callback(null); - }, 'steamcommunity'); - }); + // success = true means it worked + callback(null); + }, 'steamcommunity'); }; diff --git a/components/webapi.js b/components/webapi.js index cdc64e16..1cefcf98 100644 --- a/components/webapi.js +++ b/components/webapi.js @@ -42,15 +42,3 @@ SteamCommunity.prototype.getWebApiKey = function(domain, callback) { } }, 'steamcommunity'); }; - -/** - * @deprecated No longer works if not logged in via mobile login. Will be removed in a future release. - * @param {function} callback - */ -SteamCommunity.prototype.getWebApiOauthToken = function(callback) { - if (this.oAuthToken) { - return callback(null, this.oAuthToken); - } - - callback(new Error('This operation requires an OAuth token, which can only be obtained from node-steamcommunity\'s `login` method.')); -}; diff --git a/index.js b/index.js index cb320d32..68872fa5 100644 --- a/index.js +++ b/index.js @@ -570,7 +570,6 @@ SteamCommunity.prototype.getFriendsList = function(callback) { }; require('./components/http.js'); -require('./components/chat.js'); require('./components/profile.js'); require('./components/market.js'); require('./components/groups.js'); From 8ffa0b50d4c4e5fa95ba7d8c1823f7944ca739c6 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 22 Jul 2021 04:10:20 -0400 Subject: [PATCH 17/68] Cleaned up help.js --- components/help.js | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/components/help.js b/components/help.js index 2441b22e..ca82e1d3 100644 --- a/components/help.js +++ b/components/help.js @@ -1,7 +1,5 @@ const SteamCommunity = require('../index.js'); -const Helpers = require('./helpers.js'); - /** * Restore a previously removed steam package from your steam account. * @param {int|string} packageID @@ -9,13 +7,13 @@ const Helpers = require('./helpers.js'); */ SteamCommunity.prototype.restorePackage = function(packageID, callback) { this.httpRequestPost({ - "uri": "https://help.steampowered.com/wizard/AjaxDoPackageRestore", - "form": { - "packageid": packageID, - "sessionid": this.getSessionID('https://help.steampowered.com'), - "wizard_ajax": 1 + uri: 'https://help.steampowered.com/wizard/AjaxDoPackageRestore', + form: { + packageid: packageID, + sessionid: this.getSessionID('https://help.steampowered.com'), + wizard_ajax: 1 }, - "json": true + json: true }, (err, res, body) => { if (!callback) { return; @@ -27,7 +25,7 @@ SteamCommunity.prototype.restorePackage = function(packageID, callback) { } if (!body.success) { - callback(body.errorMsg ? new Error(body.errorMsg) : Helpers.eresultError(body.success)); + callback(new Error(body.errorMsg || 'Unexpected error')); return; } @@ -42,13 +40,13 @@ SteamCommunity.prototype.restorePackage = function(packageID, callback) { */ SteamCommunity.prototype.removePackage = function(packageID, callback) { this.httpRequestPost({ - "uri": "https://help.steampowered.com/wizard/AjaxDoPackageRemove", - "form": { - "packageid": packageID, - "sessionid": this.getSessionID('https://help.steampowered.com'), - "wizard_ajax": 1 + uri: 'https://help.steampowered.com/wizard/AjaxDoPackageRemove', + form: { + packageid: packageID, + sessionid: this.getSessionID('https://help.steampowered.com'), + wizard_ajax: 1 }, - "json": true + json: true }, (err, res, body) => { if (!callback) { return; @@ -60,7 +58,7 @@ SteamCommunity.prototype.removePackage = function(packageID, callback) { } if (!body.success) { - callback(body.errorMsg ? new Error(body.errorMsg) : Helpers.eresultError(body.success)); + callback(new Error(body.errorMsg || 'Unexpected error')); return; } From 2b678bea0640e425eba6292e1ef374f878bdc7c3 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 22 Jul 2021 04:22:29 -0400 Subject: [PATCH 18/68] Some more cleanup --- components/confirmations.js | 56 ++++----- components/groups.js | 226 ++++++++++++++++++------------------ components/http.js | 26 ++--- 3 files changed, 154 insertions(+), 154 deletions(-) diff --git a/components/confirmations.js b/components/confirmations.js index 3262020c..93f48611 100644 --- a/components/confirmations.js +++ b/components/confirmations.js @@ -12,10 +12,10 @@ const CConfirmation = require('../classes/CConfirmation.js'); * @param {SteamCommunity~getConfirmations} callback - Called when the list of confirmations is received */ SteamCommunity.prototype.getConfirmations = function(time, key, callback) { - request(this, "conf", key, time, "conf", null, false, (err, body) => { + request(this, 'conf', key, time, 'conf', null, false, (err, body) => { if (err) { - if (err.message == "Invalid protocol: steammobile:") { - err.message = "Not Logged In"; + if (err.message == 'Invalid protocol: steammobile:') { + err.message = 'Not Logged In'; this._notifySessionExpired(err); } @@ -39,7 +39,7 @@ SteamCommunity.prototype.getConfirmations = function(time, key, callback) { // We have something to confirm let confirmations = $('#mobileconf_list'); if (!confirmations) { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); return; } @@ -49,14 +49,14 @@ SteamCommunity.prototype.getConfirmations = function(time, key, callback) { let img = conf.find('.mobileconf_list_entry_icon img'); confs.push(new CConfirmation(this, { - "id": conf.data('confid'), - "type": conf.data('type'), - "creator": conf.data('creator'), - "key": conf.data('key'), - "title": conf.find('.mobileconf_list_entry_description>div:nth-of-type(1)').text().trim(), - "receiving": conf.find('.mobileconf_list_entry_description>div:nth-of-type(2)').text().trim(), - "time": conf.find('.mobileconf_list_entry_description>div:nth-of-type(3)').text().trim(), - "icon": img.length < 1 ? '' : $(img).attr('src') + id: conf.data('confid'), + type: conf.data('type'), + creator: conf.data('creator'), + key: conf.data('key'), + title: conf.find('.mobileconf_list_entry_description>div:nth-of-type(1)').text().trim(), + receiving: conf.find('.mobileconf_list_entry_description>div:nth-of-type(2)').text().trim(), + time: conf.find('.mobileconf_list_entry_description>div:nth-of-type(3)').text().trim(), + icon: img.length < 1 ? '' : $(img).attr('src') })); }); @@ -78,14 +78,14 @@ SteamCommunity.prototype.getConfirmations = function(time, key, callback) { * @param {SteamCommunity~getConfirmationOfferID} callback */ SteamCommunity.prototype.getConfirmationOfferID = function(confID, time, key, callback) { - request(this, "details/" + confID, key, time, "details", null, true, (err, body) => { + request(this, 'details/' + confID, key, time, 'details', null, true, (err, body) => { if (err) { callback(err); return; } if (!body.success) { - callback(new Error("Cannot load confirmation details")); + callback(new Error('Cannot load confirmation details')); return; } @@ -116,10 +116,10 @@ SteamCommunity.prototype.getConfirmationOfferID = function(confID, time, key, ca * @param {SteamCommunity~genericErrorCallback} callback - Called when the request is complete */ SteamCommunity.prototype.respondToConfirmation = function(confID, confKey, time, key, accept, callback) { - request(this, (confID instanceof Array) ? "multiajaxop" : "ajaxop", key, time, accept ? "allow" : "cancel", { - "op": accept ? "allow" : "cancel", - "cid": confID, - "ck": confKey + request(this, (confID instanceof Array) ? 'multiajaxop' : 'ajaxop', key, time, accept ? 'allow' : 'cancel', { + op: accept ? 'allow' : 'cancel', + cid: confID, + ck: confKey }, true, (err, body) => { if (!callback) { return; @@ -140,7 +140,7 @@ SteamCommunity.prototype.respondToConfirmation = function(confID, confKey, time, return; } - callback(new Error("Could not act on confirmation")); + callback(new Error('Could not act on confirmation')); }); }; @@ -156,7 +156,7 @@ SteamCommunity.prototype.acceptConfirmationForObject = function(identitySecret, let doConfirmation = () => { let offset = this._timeOffset; let time = SteamTotp.time(offset); - this.getConfirmations(time, SteamTotp.getConfirmationKey(identitySecret, time, "conf"), (err, confs) => { + this.getConfirmations(time, SteamTotp.getConfirmationKey(identitySecret, time, 'conf'), (err, confs) => { if (err) { callback(err); return; @@ -164,7 +164,7 @@ SteamCommunity.prototype.acceptConfirmationForObject = function(identitySecret, let conf = confs.find(conf => conf.creator == objectID); if (!conf) { - callback(new Error("Could not find confirmation for object " + objectID)); + callback(new Error('Could not find confirmation for object ' + objectID)); return; } @@ -179,7 +179,7 @@ SteamCommunity.prototype.acceptConfirmationForObject = function(identitySecret, this._usedConfTimes.splice(0, this._usedConfTimes.length - 60); // we don't need to save more than 60 entries } - conf.respond(time, SteamTotp.getConfirmationKey(identitySecret, time, "allow"), true, callback); + conf.respond(time, SteamTotp.getConfirmationKey(identitySecret, time, 'allow'), true, callback); }); }; @@ -239,7 +239,7 @@ SteamCommunity.prototype.acceptAllConfirmations = function(time, confKey, allowK function request(community, url, key, time, tag, params, json, callback) { if (!community.steamID) { - throw new Error("Must be logged in before trying to do anything with confirmations"); + throw new Error('Must be logged in before trying to do anything with confirmations'); } params = params || {}; @@ -247,16 +247,16 @@ function request(community, url, key, time, tag, params, json, callback) { params.a = community.steamID.getSteamID64(); params.k = key; params.t = time; - params.m = "android"; + params.m = 'android'; params.tag = tag; let req = { - "method": url == 'multiajaxop' ? 'POST' : 'GET', - "uri": "https://steamcommunity.com/mobileconf/" + url, - "json": !!json + method: url == 'multiajaxop' ? 'POST' : 'GET', + uri: 'https://steamcommunity.com/mobileconf/' + url, + json: !!json }; - if (req.method == "GET") { + if (req.method == 'GET') { req.qs = params; } else { req.form = params; diff --git a/components/groups.js b/components/groups.js index 410d1a60..6a1c1ba0 100644 --- a/components/groups.js +++ b/components/groups.js @@ -13,18 +13,18 @@ SteamCommunity.prototype.getGroupMembers = function(gid, callback, members, link if (!link) { if (typeof gid != 'string') { // It's a SteamID object - link = "https://steamcommunity.com/gid/" + gid.toString() + "/memberslistxml/?xml=1"; + link = `https://steamcommunity.com/gid/${gid.toString()}/memberslistxml/?xml=1`; } else { try { // new SteamID could throw which is why we have this funky-looking try/catch set up here let sid = new SteamID(gid); if (sid.type == SteamID.Type.CLAN && sid.isValid()) { - link = "https://steamcommunity.com/gid/" + sid.getSteamID64() + "/memberslistxml/?xml=1"; + link = `https://steamcommunity.com/gid/${sid.getSteamID64()}/memberslistxml/?xml=1`; } else { - throw new Error("Doesn't particularly matter what this message is"); + throw new Error('Doesn\'t particularly matter what this message is'); } } catch (e) { - link = "http://steamcommunity.com/groups/" + gid + "/memberslistxml/?xml=1"; + link = `http://steamcommunity.com/groups/${gid}/memberslistxml/?xml=1`; } } } @@ -76,10 +76,10 @@ SteamCommunity.prototype.joinGroup = function(gid, callback) { } this.httpRequestPost({ - "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64(), - "form": { - "action": "join", - "sessionID": this.getSessionID() + uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}`, + form: { + action: 'join', + sessionID: this.getSessionID() } }, (err, response, body) => { if (!callback) { @@ -95,10 +95,10 @@ SteamCommunity.prototype.leaveGroup = function(gid, callback) { gid = new SteamID(gid); } - this._myProfile("home_process", { - "sessionID": this.getSessionID(), - "action": "leaveGroup", - "groupId": gid.getSteamID64() + this._myProfile('home_process', { + sessionID: this.getSessionID(), + action: 'leaveGroup', + groupId: gid.getSteamID64() }, (err, response, body) => { if (!callback) { return; @@ -119,7 +119,7 @@ SteamCommunity.prototype.getAllGroupAnnouncements = function(gid, time, callback } this.httpRequest({ - "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/rss/" + uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/rss/` }, (err, response, body) => { if (err) { callback(err); @@ -162,23 +162,23 @@ SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content } let form = { - "sessionID": this.getSessionID(), - "action": "post", - "headline": headline, - "body": content, - "languages[0][headline]": headline, - "languages[0][body]": content + sessionID: this.getSessionID(), + action: 'post', + headline: headline, + body: content, + 'languages[0][headline]': headline, + 'languages[0][body]': content }; if (hidden) { - form.is_hidden = "is_hidden" + form.is_hidden = 'is_hidden' } this.httpRequestPost({ - "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/announcements", + uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/announcements`, form }, (err, response, body) => { - if(!callback) { + if (!callback) { return; } @@ -192,16 +192,16 @@ SteamCommunity.prototype.editGroupAnnouncement = function(gid, aid, headline, co } let submitData = { - "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/announcements", - "form": { - "sessionID": this.getSessionID(), - "gid": aid, - "action": "update", - "headline": headline, - "body": content, - "languages[0][headline]": headline, - "languages[0][body]": content, - "languages[0][updated]": 1 + uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/announcements`, + form: { + sessionID: this.getSessionID(), + gid: aid, + action: 'update', + headline: headline, + body: content, + 'languages[0][headline]': headline, + 'languages[0][body]': content, + 'languages[0][updated]': 1 } }; @@ -220,7 +220,7 @@ SteamCommunity.prototype.deleteGroupAnnouncement = function(gid, aid, callback) } let submitData = { - "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/announcements/delete/" + aid + "?sessionID=" + this.getSessionID() + uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/announcements/delete/${aid}?sessionID=${this.getSessionID()}` }; this.httpRequestGet(submitData, (err, response, body) => { @@ -257,16 +257,16 @@ SteamCommunity.prototype.scheduleGroupEvent = function(gid, name, type, descript } let form = { - "sessionid": this.getSessionID(), - "action": "newEvent", - "tzOffset": new Date().getTimezoneOffset() * -60, - "name": name, - "type": (typeof type == 'number' || !isNaN(parseInt(type, 10)) ? "GameEvent" : type), - "appID": (typeof type == 'number' || !isNaN(parseInt(type, 10)) ? type : ''), - "serverIP": server.ip, - "serverPassword": server.password, - "notes": description, - "eventQuickTime": "now" + sessionid: this.getSessionID(), + action: 'newEvent', + tzOffset: new Date().getTimezoneOffset() * -60, + name: name, + type: (typeof type == 'number' || !isNaN(parseInt(type, 10)) ? 'GameEvent' : type), + appID: (typeof type == 'number' || !isNaN(parseInt(type, 10)) ? type : ''), + serverIP: server.ip, + serverPassword: server.password, + notes: description, + eventQuickTime: 'now' }; if (time === null) { @@ -284,7 +284,7 @@ SteamCommunity.prototype.scheduleGroupEvent = function(gid, name, type, descript } this.httpRequestPost({ - "uri": "https://steamcommunity.com/gid/" + gid.toString() + "/eventEdit", + uri: `https://steamcommunity.com/gid/${gid.toString()}/eventEdit`, form }, (err, response, body) => { if (!callback) { @@ -320,17 +320,17 @@ SteamCommunity.prototype.editGroupEvent = function(gid, id, name, type, descript } let form = { - "sessionid": this.getSessionID(), - "action": "updateEvent", - "eventID": id, - "tzOffset": new Date().getTimezoneOffset() * -60, - "name": name, - "type": (typeof type == 'number' || !isNaN(parseInt(type, 10)) ? "GameEvent" : type), - "appID": (typeof type == 'number' || !isNaN(parseInt(type, 10)) ? type : ''), - "serverIP": server.ip, - "serverPassword": server.password, - "notes": description, - "eventQuickTime": "now" + sessionid: this.getSessionID(), + action: 'updateEvent', + eventID: id, + tzOffset: new Date().getTimezoneOffset() * -60, + name: name, + type: (typeof type == 'number' || !isNaN(parseInt(type, 10)) ? 'GameEvent' : type), + appID: (typeof type == 'number' || !isNaN(parseInt(type, 10)) ? type : ''), + serverIP: server.ip, + serverPassword: server.password, + notes: description, + eventQuickTime: 'now' }; if (time === null) { @@ -348,8 +348,8 @@ SteamCommunity.prototype.editGroupEvent = function(gid, id, name, type, descript } this.httpRequestPost({ - "uri": "https://steamcommunity.com/gid/" + gid.toString() + "/eventEdit", - "form": form + uri: `https://steamcommunity.com/gid/${gid.toString()}/eventEdit`, + form }, (err, response, body) => { if (!callback) { return; @@ -365,13 +365,13 @@ SteamCommunity.prototype.deleteGroupEvent = function(gid, id, callback) { } let form = { - "sessionid": this.getSessionID(), - "action": "deleteEvent", - "eventID": id + sessionid: this.getSessionID(), + action: 'deleteEvent', + eventID: id }; this.httpRequestPost({ - "uri": "https://steamcommunity.com/gid/" + gid.toString() + "/eventEdit", + uri: `https://steamcommunity.com/gid/${gid.toString()}/eventEdit`, form }, (err, response, body) => { if (!callback) { @@ -392,12 +392,12 @@ SteamCommunity.prototype.setGroupPlayerOfTheWeek = function(gid, steamID, callba } this.httpRequestPost({ - "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/potwEdit", - "form": { - "xml": 1, - "action": "potw", - "memberId": steamID.getSteam3RenderedID(), - "sessionid": this.getSessionID() + uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/potwEdit`, + form: { + xml: 1, + action: 'potw', + memberId: steamID.getSteam3RenderedID(), + sessionid: this.getSessionID() } }, (err, response, body) => { if (!callback) { @@ -405,7 +405,7 @@ SteamCommunity.prototype.setGroupPlayerOfTheWeek = function(gid, steamID, callba } if (err || response.statusCode != 200) { - callback(err || new Error("HTTP error " + response.statusCode)); + callback(err || new Error(`HTTP error ${response.statusCode}`)); return; } @@ -434,12 +434,12 @@ SteamCommunity.prototype.kickGroupMember = function(gid, steamID, callback) { } this.httpRequestPost({ - "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/membersManage", - "form": { - "sessionID": this.getSessionID(), - "action": "kick", - "memberId": steamID.getSteamID64(), - "queryString": "" + uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/membersManage`, + form: { + sessionID: this.getSessionID(), + action: 'kick', + memberId: steamID.getSteamID64(), + queryString: '' } }, (err, response, body) => { if (!callback) { @@ -460,7 +460,7 @@ SteamCommunity.prototype.getGroupHistory = function(gid, page, callback) { page = 1; } - this.httpRequest("https://steamcommunity.com/gid/" + gid.getSteamID64() + "/history?p=" + page, (err, response, body) => { + this.httpRequest(`https://steamcommunity.com/gid/${gid.getSteamID64()}/history?p=${page}`, (err, response, body) => { if (err) { callback(err); return; @@ -535,10 +535,10 @@ SteamCommunity.prototype.getAllGroupComments = function(gid, from, count, callba } let options = { - "uri": "https://steamcommunity.com/comment/Clan/render/" + gid.getSteamID64() + "/-1/", - "form": { - "start": from, - "count": count + uri: `https://steamcommunity.com/comment/Clan/render/${gid.getSteamID64()}/-1/`, + form: { + start: from, + count } }; @@ -555,13 +555,13 @@ SteamCommunity.prototype.getAllGroupComments = function(gid, from, count, callba $('.commentthread_comment_content').each(function() { let comment = {}; - let $selector = $(this).find(".commentthread_author_link"); - comment.authorName = $($selector).find("bdi").text(); - comment.authorId = $($selector).attr("href").replace(/https?:\/\/steamcommunity.com\/(id|profiles)\//, ""); - comment.date = Helpers.decodeSteamTime($(this).find(".commentthread_comment_timestamp").text().trim()); + let $selector = $(this).find('.commentthread_author_link'); + comment.authorName = $($selector).find('bdi').text(); + comment.authorId = $($selector).attr('href').replace(/https?:\/\/steamcommunity.com\/(id|profiles)\//, ''); + comment.date = Helpers.decodeSteamTime($(this).find('.commentthread_comment_timestamp').text().trim()); - $selector = $(this).find(".commentthread_comment_text"); - comment.commentId = $($selector).attr("id").replace("comment_content_", ""); + $selector = $(this).find('.commentthread_comment_text'); + comment.commentId = $($selector).attr('id').replace('comment_content_', ''); comment.text = $($selector).html().trim(); comments.push(comment); @@ -581,10 +581,10 @@ SteamCommunity.prototype.deleteGroupComment = function(gid, cid, callback) { } let options = { - "uri": "https://steamcommunity.com/comment/Clan/delete/" + gid.getSteamID64() + "/-1/", - "form": { - "sessionid": this.getSessionID(), - "gidcomment": cid + uri: `https://steamcommunity.com/comment/Clan/delete/${gid.getSteamID64()}/-1/`, + form: { + sessionid: this.getSessionID(), + gidcomment: cid } }; @@ -603,11 +603,11 @@ SteamCommunity.prototype.postGroupComment = function(gid, message, callback) { } let options = { - "uri": "https://steamcommunity.com/comment/Clan/post/" + gid.getSteamID64() + "/-1/", - "form": { - "comment": message, - "count": 6, - "sessionid": this.getSessionID() + uri: `https://steamcommunity.com/comment/Clan/post/${gid.getSteamID64()}/-1/`, + form: { + comment: message, + count: 6, + sessionid: this.getSessionID() } }; @@ -630,9 +630,9 @@ SteamCommunity.prototype.getGroupJoinRequests = function(gid, callback) { gid = new SteamID(gid); } - this.httpRequestGet("https://steamcommunity.com/gid/" + gid.getSteamID64() + "/joinRequestsManage", (err, res, body) => { + this.httpRequestGet(`https://steamcommunity.com/gid/${gid.getSteamID64()}/joinRequestsManage`, (err, res, body) => { if (!body) { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); return; } @@ -645,7 +645,7 @@ SteamCommunity.prototype.getGroupJoinRequests = function(gid, callback) { let requests = []; for (let i = 0; i < matches.length; i++) { - requests.push(new SteamID("[U:1:" + matches[i].match(/JoinRequests_ApproveDenyUser\(\W*['"](\d+)['"],\W0\W\)/)[1] + "]")); + requests.push(new SteamID('[U:1:' + matches[i].match(/JoinRequests_ApproveDenyUser\(\W*['"](\d+)['"],\W0\W\)/)[1] + ']')); } callback(null, requests); @@ -667,21 +667,21 @@ SteamCommunity.prototype.respondToGroupJoinRequests = function(gid, steamIDs, ap let rgAccounts = (!Array.isArray(steamIDs) ? [steamIDs] : steamIDs).map(sid => sid.toString()); this.httpRequestPost({ - "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/joinRequestsManage", - "form": { - "rgAccounts": rgAccounts, - "bapprove": approve ? "1" : "0", - "json": "1", - "sessionID": this.getSessionID() + uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/joinRequestsManage`, + form: { + rgAccounts: rgAccounts, + bapprove: approve ? '1' : '0', + json: '1', + sessionID: this.getSessionID() }, - "json": true + json: true }, (err, res, body) => { if (!callback) { return; } if (body != EResult.OK) { - let err = new Error(EResult[body] || ("Error " + body)); + let err = new Error(EResult[body] || `Error ${body}`); err.eresult = body; callback(err); } else { @@ -702,21 +702,21 @@ SteamCommunity.prototype.respondToAllGroupJoinRequests = function(gid, approve, } this.httpRequestPost({ - "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/joinRequestsManage", - "form": { - "bapprove": approve ? "1" : "0", - "json": "1", - "action": "bulkrespond", - "sessionID": this.getSessionID() + uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/joinRequestsManage`, + form: { + bapprove: approve ? '1' : '0', + json: '1', + action: 'bulkrespond', + sessionID: this.getSessionID() }, - "json": true + json: true }, (err, res, body) => { if (!callback) { return; } if (body != EResult.OK) { - let err = new Error(EResult[body] || ("Error " + body)); + let err = new Error(EResult[body] || `Error ${body}`); err.eresult = body; callback(err); } else { diff --git a/components/http.js b/components/http.js index 59112118..5898f829 100644 --- a/components/http.js +++ b/components/http.js @@ -44,14 +44,14 @@ SteamCommunity.prototype.httpRequest = function(uri, options, callback, source) let httpError = options.checkHttpError !== false && this._checkHttpError(err, response, callback, body); let communityError = !options.json && options.checkCommunityError !== false && this._checkCommunityError(body, httpError ? noop : callback); // don't fire the callback if hasHttpError did it already let tradeError = !options.json && options.checkTradeError !== false && this._checkTradeError(body, httpError || communityError ? noop : callback); // don't fire the callback if either of the previous already did - let jsonError = options.json && options.checkJsonError !== false && !body ? new Error("Malformed JSON response") : null; + let jsonError = options.json && options.checkJsonError !== false && !body ? new Error('Malformed JSON response') : null; this.emit('postHttpRequest', requestID, source, options, httpError || communityError || tradeError || jsonError || null, response, body, { - "hasCallback": hasCallback, - "httpError": httpError, - "communityError": communityError, - "tradeError": tradeError, - "jsonError": jsonError + hasCallback, + httpError, + communityError, + tradeError, + jsonError }); if (hasCallback && !(httpError || communityError || tradeError)) { @@ -71,12 +71,12 @@ SteamCommunity.prototype.httpRequest = function(uri, options, callback, source) }; SteamCommunity.prototype.httpRequestGet = function() { - this._httpRequestConvenienceMethod = "GET"; + this._httpRequestConvenienceMethod = 'GET'; return this.httpRequest.apply(this, arguments); }; SteamCommunity.prototype.httpRequestPost = function() { - this._httpRequestConvenienceMethod = "POST"; + this._httpRequestConvenienceMethod = 'POST'; return this.httpRequest.apply(this, arguments); }; @@ -91,20 +91,20 @@ SteamCommunity.prototype._checkHttpError = function(err, response, callback, bod } if (response.statusCode >= 300 && response.statusCode <= 399 && response.headers.location.indexOf('/login') != -1) { - err = new Error("Not Logged In"); + err = new Error('Not Logged In'); callback(err, response, body); this._notifySessionExpired(err); return err; } if (response.statusCode == 403 && typeof response.body == 'string' && response.body.match(/

Enter your PIN below to exit Family View.<\/div>/)) { - err = new Error("Family View Restricted"); + err = new Error('Family View Restricted'); callback(err, response, body); return err; } if (response.statusCode >= 400) { - err = new Error("HTTP error " + response.statusCode); + err = new Error(`HTTP error ${response.statusCode}`); err.code = response.statusCode; callback(err, response, body); return err; @@ -118,13 +118,13 @@ SteamCommunity.prototype._checkCommunityError = function(html, callback) { if (typeof html == 'string' && html.match(/

Sorry!<\/h1>/)) { let match = html.match(/

(.+)<\/h3>/); - err = new Error(match ? match[1] : "Unknown error occurred"); + err = new Error(match ? match[1] : 'Unknown error occurred'); callback(err); return err; } if (typeof html == 'string' && html.match(/g_steamID = false;/) && html.match(/

Sign In<\/h1>/)) { - err = new Error("Not Logged In"); + err = new Error('Not Logged In'); callback(err); this._notifySessionExpired(err); return err; From 63b0b82531f7a36b5cae87278e978a0fd0406331 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 22 Jul 2021 04:42:48 -0400 Subject: [PATCH 19/68] Cleaned up users.js --- components/users.js | 437 +++++++++++++++++++++----------------------- 1 file changed, 212 insertions(+), 225 deletions(-) diff --git a/components/users.js b/components/users.js index f598a2a9..464e9546 100644 --- a/components/users.js +++ b/components/users.js @@ -9,116 +9,111 @@ const CEconItem = require('../classes/CEconItem.js'); const Helpers = require('./helpers.js'); SteamCommunity.prototype.addFriend = function(userID, callback) { - if(typeof userID === 'string') { + if (typeof userID === 'string') { userID = new SteamID(userID); } - var self = this; this.httpRequestPost({ - "uri": "https://steamcommunity.com/actions/AddFriendAjax", - "form": { - "accept_invite": 0, - "sessionID": this.getSessionID(), - "steamid": userID.toString() + uri: 'https://steamcommunity.com/actions/AddFriendAjax', + form: { + accept_invite: 0, + sessionID: this.getSessionID(), + steamid: userID.toString() }, - "json": true - }, function(err, response, body) { - if(!callback) { + json: true + }, (err, response, body) => { + if (!callback) { return; } if (err) { - callback(err); - return; + return callback(err); } - if(body.success) { + if (body.success) { callback(null); } else { - callback(new Error("Unknown error")); + callback(new Error('Unknown error')); } - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.acceptFriendRequest = function(userID, callback) { - if(typeof userID === 'string') { + if (typeof userID === 'string') { userID = new SteamID(userID); } - var self = this; this.httpRequestPost({ - "uri": "https://steamcommunity.com/actions/AddFriendAjax", - "form": { - "accept_invite": 1, - "sessionID": this.getSessionID(), - "steamid": userID.toString() - } - }, function(err, response, body) { - if(!callback) { + uri: 'https://steamcommunity.com/actions/AddFriendAjax', + form: { + accept_invite: 1, + sessionID: this.getSessionID(), + steamid: userID.toString() + } + }, (err, response, body) => { + if (!callback) { return; } callback(err || null); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.removeFriend = function(userID, callback) { - if(typeof userID === 'string') { + if (typeof userID === 'string') { userID = new SteamID(userID); } - var self = this; this.httpRequestPost({ - "uri": "https://steamcommunity.com/actions/RemoveFriendAjax", - "form": { - "sessionID": this.getSessionID(), - "steamid": userID.toString() + uri: 'https://steamcommunity.com/actions/RemoveFriendAjax', + form: { + sessionID: this.getSessionID(), + steamid: userID.toString() } - }, function(err, response, body) { - if(!callback) { + }, (err, response, body) => { + if (!callback) { return; } callback(err || null); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.blockCommunication = function(userID, callback) { - if(typeof userID === 'string') { + if (typeof userID === 'string') { userID = new SteamID(userID); } - var self = this; this.httpRequestPost({ - "uri": "https://steamcommunity.com/actions/BlockUserAjax", - "form": { - "sessionID": this.getSessionID(), - "steamid": userID.toString() + uri: 'https://steamcommunity.com/actions/BlockUserAjax', + form: { + sessionID: this.getSessionID(), + steamid: userID.toString() } - }, function(err, response, body) { - if(!callback) { + }, (err, response, body) => { + if (!callback) { return; } callback(err || null); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.unblockCommunication = function(userID, callback) { - if(typeof userID === 'string') { + if (typeof userID === 'string') { userID = new SteamID(userID); } - var form = {"action": "unignore"}; + let form = {action: 'unignore'}; form['friends[' + userID.toString() + ']'] = 1; - this._myProfile('friends/blocked/', form, function(err, response, body) { - if(!callback) { + this._myProfile('friends/blocked/', form, (err, response, body) => { + if (!callback) { return; } - if(err || response.statusCode >= 400) { - callback(err || new Error("HTTP error " + response.statusCode)); + if (err || response.statusCode >= 400) { + callback(err || new Error(`HTTP error ${response.statusCode}`)); return; } @@ -127,21 +122,20 @@ SteamCommunity.prototype.unblockCommunication = function(userID, callback) { }; SteamCommunity.prototype.postUserComment = function(userID, message, callback) { - if(typeof userID === 'string') { + if (typeof userID === 'string') { userID = new SteamID(userID); } - var self = this; this.httpRequestPost({ - "uri": "https://steamcommunity.com/comment/Profile/post/" + userID.toString() + "/-1", - "form": { - "comment": message, - "count": 1, - "sessionid": this.getSessionID() + uri: `https://steamcommunity.com/comment/Profile/post/${userID.toString()}/-1`, + form: { + comment: message, + count: 1, + sessionid: this.getSessionID() }, - "json": true - }, function(err, response, body) { - if(!callback) { + json: true + }, (err, response, body) => { + if (!callback) { return; } @@ -150,37 +144,36 @@ SteamCommunity.prototype.postUserComment = function(userID, message, callback) { return; } - if(body.success) { + if (body.success) { const $ = Cheerio.load(body.comments_html); const commentID = $('.commentthread_comment').attr('id').split('_')[1]; callback(null, commentID); - } else if(body.error) { + } else if (body.error) { callback(new Error(body.error)); } else { - callback(new Error("Unknown error")); + callback(new Error('Unknown error')); } - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.deleteUserComment = function(userID, commentID, callback) { - if(typeof userID === 'string') { + if (typeof userID === 'string') { userID = new SteamID(userID); } - var self = this; this.httpRequestPost({ - "uri": "https://steamcommunity.com/comment/Profile/delete/" + userID.toString() + "/-1", - "form": { - "gidcomment": commentID, - "start": 0, - "count": 1, - "sessionid": this.getSessionID(), - "feature2": -1 + uri: `https://steamcommunity.com/comment/Profile/delete/${userID.toString()}/-1`, + form: { + gidcomment: commentID, + start: 0, + count: 1, + sessionid: this.getSessionID(), + feature2: -1 }, - "json": true - }, function(err, response, body) { - if(!callback) { + json: true + }, (err, response, body) => { + if (!callback) { return; } @@ -189,20 +182,20 @@ SteamCommunity.prototype.deleteUserComment = function(userID, commentID, callbac return; } - if(body.success && !body.comments_html.includes(commentID)) { + if (body.success && !body.comments_html.includes(commentID)) { callback(null); - } else if(body.error) { + } else if (body.error) { callback(new Error(body.error)); - } else if(body.comments_html.includes(commentID)) { - callback(new Error("Failed to delete comment")); + } else if (body.comments_html.includes(commentID)) { + callback(new Error('Failed to delete comment')); } else { - callback(new Error("Unknown error")); + callback(new Error('Unknown error')); } - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.getUserComments = function(userID, options, callback) { - if(typeof userID === 'string') { + if (typeof userID === 'string') { userID = new SteamID(userID); } @@ -211,19 +204,19 @@ SteamCommunity.prototype.getUserComments = function(userID, options, callback) { options = {}; } - var form = Object.assign({ - "start": 0, - "count": 0, - "feature2": -1, - "sessionid": this.getSessionID() + let form = Object.assign({ + start: 0, + count: 0, + feature2: -1, + sessionid: this.getSessionID() }, options); this.httpRequestPost({ - "uri": "https://steamcommunity.com/comment/Profile/render/" + userID.toString() + "/-1", - "form": form, - "json": true - }, function(err, response, body) { - if(!callback) { + uri: `https://steamcommunity.com/comment/Profile/render/${userID.toString()}/-1`, + form, + json: true + }, (err, response, body) => { + if (!callback) { return; } @@ -232,52 +225,51 @@ SteamCommunity.prototype.getUserComments = function(userID, options, callback) { return; } - if(body.success) { + if (body.success) { const $ = Cheerio.load(body.comments_html); - const comments = $(".commentthread_comment.responsive_body_text[id]").map((i, elem) => { - var $elem = $(elem), - $commentContent = $elem.find(".commentthread_comment_text"); + const comments = $('.commentthread_comment.responsive_body_text[id]').map((i, elem) => { + let $elem = $(elem), + $commentContent = $elem.find('.commentthread_comment_text'); return { - id: $elem.attr("id").split("_")[1], + id: $elem.attr('id').split('_')[1], author: { - steamID: new SteamID("[U:1:" + $elem.find("[data-miniprofile]").data("miniprofile") + "]"), - name: $elem.find("bdi").text(), - avatar: $elem.find(".playerAvatar img[src]").attr("src"), - state: $elem.find(".playerAvatar").attr("class").split(" ").pop() + steamID: new SteamID('[U:1:' + $elem.find('[data-miniprofile]').data('miniprofile') + ']'), + name: $elem.find('bdi').text(), + avatar: $elem.find('.playerAvatar img[src]').attr('src'), + state: $elem.find('.playerAvatar').attr('class').split(' ').pop() }, - date: new Date($elem.find(".commentthread_comment_timestamp").data("timestamp") * 1000), + date: new Date($elem.find('.commentthread_comment_timestamp').data('timestamp') * 1000), text: $commentContent.text().trim(), html: $commentContent.html().trim() } }).get(); callback(null, comments, body.total_count); - } else if(body.error) { + } else if (body.error) { callback(new Error(body.error)); } else { - callback(new Error("Unknown error")); + callback(new Error('Unknown error')); } - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.inviteUserToGroup = function(userID, groupID, callback) { - if(typeof userID === 'string') { + if (typeof userID === 'string') { userID = new SteamID(userID); } - var self = this; this.httpRequestPost({ - "uri": "https://steamcommunity.com/actions/GroupInvite", - "form": { - "group": groupID.toString(), - "invitee": userID.toString(), - "json": 1, - "sessionID": this.getSessionID(), - "type": "groupInvite" + uri: 'https://steamcommunity.com/actions/GroupInvite', + form: { + group: groupID.toString(), + invitee: userID.toString(), + json: 1, + sessionID: this.getSessionID(), + type: 'groupInvite' }, - "json": true - }, function(err, response, body) { - if(!callback) { + json: true + }, (err, response, body) => { + if (!callback) { return; } @@ -286,14 +278,14 @@ SteamCommunity.prototype.inviteUserToGroup = function(userID, groupID, callback) return; } - if(body.results == 'OK') { + if (body.results == 'OK') { callback(null); - } else if(body.results) { + } else if (body.results) { callback(new Error(body.results)); } else { - callback(new Error("Unknown error")); + callback(new Error('Unknown error')); } - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.getUserAliases = function(userID, callback) { @@ -302,24 +294,24 @@ SteamCommunity.prototype.getUserAliases = function(userID, callback) { } this.httpRequestGet({ - "uri": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/ajaxaliases", - "json": true - }, function(err, response, body) { + uri: `https://steamcommunity.com/profiles/${userID.getSteamID64()}/ajaxaliases`, + json: true + }, (err, response, body) => { if (err) { callback(err); return; } if (typeof body !== 'object') { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); return; } - callback(null, body.map(function(entry) { + callback(null, body.map((entry) => { entry.timechanged = Helpers.decodeSteamTime(entry.timechanged); return entry; })); - }, "steamcommunity"); + }, 'steamcommunity'); }; /** @@ -332,33 +324,33 @@ SteamCommunity.prototype.getUserProfileBackground = function(userID, callback) { userID = new SteamID(userID); } - this.httpRequest("https://steamcommunity.com/profiles/" + userID.getSteamID64(), (err, response, body) => { + this.httpRequest(`https://steamcommunity.com/profiles/${userID.getSteamID64()}`, (err, response, body) => { if (err) { callback(err); return; } - var $ = Cheerio.load(body); + let $ = Cheerio.load(body); - var $privateProfileInfo = $('.profile_private_info'); + let $privateProfileInfo = $('.profile_private_info'); if ($privateProfileInfo.length > 0) { callback(new Error($privateProfileInfo.text().trim())); return; } if ($('body').hasClass('has_profile_background')) { - var backgroundUrl = $('div.profile_background_image_content').css('background-image'); - var matcher = backgroundUrl.match(/\(([^)]+)\)/); + let backgroundUrl = $('div.profile_background_image_content').css('background-image'); + let matcher = backgroundUrl.match(/\(([^)]+)\)/); if (matcher.length != 2 || !matcher[1].length) { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); } else { callback(null, matcher[1]); } } else { callback(null, null); } - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) { @@ -372,44 +364,43 @@ SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) { } if (!userID) { - callback(new Error("No SteamID specified and not logged in")); + callback(new Error('No SteamID specified and not logged in')); return; } - var self = this; - this.httpRequest("https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory/", function(err, response, body) { + this.httpRequest(`https://steamcommunity.com/profiles/${userID.getSteamID64()}/inventory/`, (err, response, body) => { if (err) { callback(err); return; } - var match = body.match(/var g_rgAppContextData = ([^\n]+);\r?\n/); + let match = body.match(/var g_rgAppContextData = ([^\n]+);\r?\n/); if (!match) { - var errorMessage = "Malformed response"; + let errorMessage = 'Malformed response'; - if(body.match(/0 items in their inventory\./)){ + if (body.includes('0 items in their inventory.')) { callback(null, {}); return; - }else if(body.match(/inventory is currently private\./)){ - errorMessage = "Private inventory"; - }else if(body.match(/profile\_private\_info/)){ - errorMessage = "Private profile"; + } else if (body.includes('inventory is currently private.')) { + errorMessage = 'Private inventory'; + } else if (body.includes('profile_private_info')) { + errorMessage = 'Private profile'; } callback(new Error(errorMessage)); return; } - var data; + let data; try { data = JSON.parse(match[1]); } catch(e) { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); return; } callback(null, data); - }, "steamcommunity"); + }, 'steamcommunity'); }; /** @@ -422,27 +413,22 @@ SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) { * @param {function} callback */ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, tradableOnly, callback) { - var self = this; - if (typeof userID === 'string') { userID = new SteamID(userID); } - var endpoint = "/profiles/" + userID.getSteamID64(); - get([], []); - - function get(inventory, currency, start) { - self.httpRequest({ - "uri": "https://steamcommunity.com" + endpoint + "/inventory/json/" + appID + "/" + contextID, - "headers": { - "Referer": "https://steamcommunity.com" + endpoint + "/inventory" + const get = (inventory, currency, start) => { + this.httpRequest({ + uri: `https://steamcommunity.com${endpoint}/inventory/json/${appID}/${contextID}`, + headers: { + Referer: `https://steamcommunity.com${endpoint}/inventory` }, - "qs": { - "start": start, - "trading": tradableOnly ? 1 : undefined + qs: { + start: start, + trading: tradableOnly ? 1 : undefined }, - "json": true - }, function(err, response, body) { + json: true + }, (err, response, body) => { if (err) { callback(err); return; @@ -450,15 +436,15 @@ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, t if (!body || !body.success || !body.rgInventory || !body.rgDescriptions || !body.rgCurrency) { if (body) { - callback(new Error(body.Error || "Malformed response")); + callback(new Error(body.Error || 'Malformed response')); } else { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); } return; } - var i; + let i; for (i in body.rgInventory) { if (!body.rgInventory.hasOwnProperty(i)) { continue; @@ -476,17 +462,20 @@ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, t } if (body.more) { - var match = response.request.uri.href.match(/\/(profiles|id)\/([^\/]+)\//); - if(match) { - endpoint = "/" + match[1] + "/" + match[2]; + let match = response.request.uri.href.match(/\/(profiles|id)\/([^\/]+)\//); + if (match) { + endpoint = `/${match[1]}/${match[2]}`; } get(inventory, currency, body.more_start); } else { callback(null, inventory, currency); } - }, "steamcommunity"); - } + }, 'steamcommunity'); + }; + + let endpoint = `/profiles/${userID.getSteamID64()}`; + get([], []); }; /** @@ -501,52 +490,64 @@ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, t SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, contextID, tradableOnly, language, callback) { if (typeof language === 'function') { callback = language; - language = "english"; + language = 'english'; } if (!userID) { - callback(new Error("The user's SteamID is invalid or missing.")); + callback(new Error('The user\'s SteamID is invalid or missing.')); return; } - var self = this; - if (typeof userID === 'string') { userID = new SteamID(userID); } - var pos = 1; - get([], []); + // A bit of optimization; objects are hash tables so it's more efficient to look up by key than to iterate an array + let quickDescriptionLookup = {}; + + const getDescription = (descriptions, classID, instanceID) => { + let key = classID + '_' + (instanceID || '0'); // instanceID can be undefined, in which case it's 0. + + if (quickDescriptionLookup[key]) { + return quickDescriptionLookup[key]; + } + + for (let i = 0; i < descriptions.length; i++) { + quickDescriptionLookup[descriptions[i].classid + '_' + (descriptions[i].instanceid || '0')] = descriptions[i]; + } + + return quickDescriptionLookup[key]; + }; - function get(inventory, currency, start) { - self.httpRequest({ - "uri": "https://steamcommunity.com/inventory/" + userID.getSteamID64() + "/" + appID + "/" + contextID, - "headers": { - "Referer": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory" + const get = (inventory, currency, start) => { + this.httpRequest({ + uri: `https://steamcommunity.com/inventory/${userID.getSteamID64()}/${appID}/${contextID}`, + headers: { + Referer: `https://steamcommunity.com/profiles/${userID.getSteamID64()}/inventory` }, - "qs": { - "l": language, // Default language - "count": 5000, // Max items per 'page' - "start_assetid": start + qs: { + l: language, // Default language + count: 5000, // Max items per 'page' + start_assetid: start }, - "json": true - }, function(err, response, body) { + json: true + }, (err, response, body) => { if (err) { - if (err.message == "HTTP error 403" && body === null) { + if (err.message == 'HTTP error 403' && body === null) { // 403 with a body of "null" means the inventory/profile is private. - if (self.steamID && userID.getSteamID64() == self.steamID.getSteamID64()) { + if (this.steamID && userID.getSteamID64() == this.steamID.getSteamID64()) { // We can never get private profile error for our own inventory! - self._notifySessionExpired(err); + this._notifySessionExpired(err); } - callback(new Error("This profile is private.")); + callback(new Error('This profile is private.')); return; } - if (err.message == "HTTP error 500" && body && body.error) { + if (err.message == 'HTTP error 500' && body && body.error) { err = new Error(body.error); - var match = body.error.match(/^(.+) \((\d+)\)$/); + let match = body.error.match(/^(.+) \((\d+)\)$/); if (match) { err.message = match[1]; err.eresult = match[2]; @@ -568,16 +569,16 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont if (!body || !body.success || !body.assets || !body.descriptions) { if (body) { // Dunno if the error/Error property even exists on this new endpoint - callback(new Error(body.error || body.Error || "Malformed response")); + callback(new Error(body.error || body.Error || 'Malformed response')); } else { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); } return; } - for (var i = 0; i < body.assets.length; i++) { - var description = getDescription(body.descriptions, body.assets[i].classid, body.assets[i].instanceid); + for (let i = 0; i < body.assets.length; i++) { + let description = getDescription(body.descriptions, body.assets[i].classid, body.assets[i].instanceid); if (!tradableOnly || (description && description.tradable)) { body.assets[i].pos = pos++; @@ -590,25 +591,11 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont } else { callback(null, inventory, currency, body.total_inventory_count); } - }, "steamcommunity"); - } - - // A bit of optimization; objects are hash tables so it's more efficient to look up by key than to iterate an array - var quickDescriptionLookup = {}; - - function getDescription(descriptions, classID, instanceID) { - var key = classID + '_' + (instanceID || '0'); // instanceID can be undefined, in which case it's 0. - - if (quickDescriptionLookup[key]) { - return quickDescriptionLookup[key]; - } - - for (var i = 0; i < descriptions.length; i++) { - quickDescriptionLookup[descriptions[i].classid + '_' + (descriptions[i].instanceid || '0')] = descriptions[i]; - } + }, 'steamcommunity'); + }; - return quickDescriptionLookup[key]; - } + let pos = 1; + get([], []); }; /** @@ -640,7 +627,7 @@ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer, return; } - var imageDetails = null; + let imageDetails = null; try { imageDetails = imageSize(imageContentsBuffer); } catch (ex) { @@ -648,11 +635,11 @@ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer, return; } - var imageHash = Crypto.createHash('sha1'); + let imageHash = Crypto.createHash('sha1'); imageHash.update(imageContentsBuffer); imageHash = imageHash.digest('hex'); - var filename = Date.now() + '_image.' + imageDetails.type; + let filename = Date.now() + '_image.' + imageDetails.type; this.httpRequestPost({ uri: 'https://steamcommunity.com/chat/beginfileupload/?l=english', @@ -673,7 +660,7 @@ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer, }, (err, res, body) => { if (err) { if (body && body.success) { - var err2 = Helpers.eresultError(body.success); + let err2 = Helpers.eresultError(body.success); if (body.message) { err2.message = body.message; } @@ -689,9 +676,9 @@ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer, return; } - var hmac = body.hmac; - var timestamp = body.timestamp; - var startResult = body.result; + let hmac = body.hmac; + let timestamp = body.timestamp; + let startResult = body.result; if (!startResult || !startResult.ugcid || !startResult.url_host || !startResult.request_headers) { callback(new Error('Malformed response')); @@ -699,8 +686,8 @@ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer, } // Okay, now we need to PUT the file to the provided URL - var uploadUrl = (startResult.use_https ? 'https' : 'http') + '://' + startResult.url_host + startResult.url_path; - var headers = {}; + let uploadUrl = (startResult.use_https ? 'https' : 'http') + '://' + startResult.url_host + startResult.url_path; + let headers = {}; startResult.request_headers.forEach((header) => { headers[header.name.toLowerCase()] = header.value; }); From 2adc23f0ac9711df9f3aac77a925e7f61a0a4ad5 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 02:30:41 -0400 Subject: [PATCH 20/68] Added eslint config --- .eslintrc.js | 23 ++++++++++++++ .idea/codeStyles/Project.xml | 33 ++++++++++++++++++-- .idea/codeStyles/codeStyleConfig.xml | 2 +- .idea/inspectionProfiles/Project_Default.xml | 1 + package.json | 3 ++ 5 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 .eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..935b7c37 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,23 @@ +module.exports = { + env: { + commonjs: true, + es2021: true, + node: true + }, + extends: 'eslint:recommended', + parserOptions: { + ecmaVersion: 12 + }, + rules: { + // Use tabs for indentation + indent: ['error', 'tab'], + // Single quotes for strings + quotes: ['warn', 'single'], + // Always require semicolons + semi: ['error', 'always'], + // Don't use 'var' + 'no-var': 'warn', + // Only use quotes in object literal keys as needed + 'quote-props': ['warn', 'as-needed'] + } +}; diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index ea198ab0..a2ad752e 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,17 +1,44 @@ - - + + diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index a55e7a17..79ee123c 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,5 @@ - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 2d5fea61..1c130558 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -4,6 +4,7 @@ + diff --git a/package.json b/package.json index bebf399e..b483c1ea 100644 --- a/package.json +++ b/package.json @@ -33,5 +33,8 @@ }, "engines": { "node": ">=8.0.0" + }, + "devDependencies": { + "eslint": "^7.31.0" } } From 4c59309eb495da4f18cb2e1ae5f8338cad90a256 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 02:40:39 -0400 Subject: [PATCH 21/68] Refactor index.js --- .eslintrc.js | 12 +- index.js | 343 ++++++++++++++++++++-------------------- resources/EChatState.js | 14 -- 3 files changed, 179 insertions(+), 190 deletions(-) delete mode 100644 resources/EChatState.js diff --git a/.eslintrc.js b/.eslintrc.js index 935b7c37..219c4280 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -9,8 +9,8 @@ module.exports = { ecmaVersion: 12 }, rules: { - // Use tabs for indentation - indent: ['error', 'tab'], + // Use tabs for indentation and require 'case' in switch to be indented 1 level (default 0) + indent: ['error', 'tab', {SwitchCase: 1}], // Single quotes for strings quotes: ['warn', 'single'], // Always require semicolons @@ -18,6 +18,12 @@ module.exports = { // Don't use 'var' 'no-var': 'warn', // Only use quotes in object literal keys as needed - 'quote-props': ['warn', 'as-needed'] + 'quote-props': ['warn', 'as-needed'], + // Don't allow trailing spaces after a line + 'no-trailing-spaces': 'warn', + // Require spaces before and after keywords (like "if") + 'keyword-spacing': 'warn', + // Don't allow unused variables, but allow unused function args (e.g. in callbacks) and global vars + 'no-unused-vars': ['error', {vars: 'local', args: 'none'}] } }; diff --git a/index.js b/index.js index 68872fa5..434c7435 100644 --- a/index.js +++ b/index.js @@ -1,13 +1,13 @@ -const EventEmitter = require('events').EventEmitter; -const hex2b64 = require('node-bignumber').hex2b64; +const {EventEmitter} = require('events'); +const {hex2b64} = require('node-bignumber'); const Request = require('request'); -const RSA = require('node-bignumber').Key; +const {Key: RSA} = require('node-bignumber'); const SteamID = require('steamid'); const Util = require('util'); const Helpers = require('./components/helpers.js'); -const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36"; +const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'; Util.inherits(SteamCommunity, EventEmitter); @@ -25,18 +25,17 @@ function SteamCommunity(options) { this._jar = Request.jar(); this._captchaGid = -1; this._httpRequestID = 0; - this.chatState = SteamCommunity.ChatState.Offline; - var defaults = { - "jar": this._jar, - "timeout": options.timeout || 50000, - "gzip": true, - "headers": { - "User-Agent": options.userAgent || USER_AGENT + let defaults = { + jar: this._jar, + timeout: options.timeout || 50000, + gzip: true, + headers: { + 'User-Agent': options.userAgent || USER_AGENT } }; - if (typeof options == "string") { + if (typeof options == 'string') { options = { localAddress: options }; @@ -47,7 +46,7 @@ function SteamCommunity(options) { defaults.localAddress = options.localAddress; } - this.request = options.request || Request.defaults({"forever": true}); // "forever" indicates that we want a keep-alive agent + this.request = options.request || Request.defaults({forever: true}); // "forever" indicates that we want a keep-alive agent this.request = this.request.defaults(defaults); // English @@ -59,41 +58,41 @@ function SteamCommunity(options) { SteamCommunity.prototype.login = function(details, callback) { if (!details.accountName || !details.password) { - throw new Error("Missing either accountName or password to login; both are needed"); + throw new Error('Missing either accountName or password to login; both are needed'); } if (details.steamguard) { - var parts = details.steamguard.split('||'); + let parts = details.steamguard.split('||'); this._setCookie(Request.cookie('steamMachineAuth' + parts[0] + '=' + encodeURIComponent(parts[1])), true); } - var disableMobile = details.disableMobile; + let disableMobile = details.disableMobile; - var self = this; + let self = this; // Delete the cache delete self._profileURL; // headers required to convince steam that we're logging in from a mobile device so that we can get the oAuth data - var mobileHeaders = {}; + let mobileHeaders = {}; if (!disableMobile) { mobileHeaders = { - "X-Requested-With": "com.valvesoftware.android.steam.community", - "Referer": "https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client", - "User-Agent": this._options.mobileUserAgent || details.mobileUserAgent || "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", - "Accept": "text/javascript, text/html, application/xml, text/xml, */*" + 'X-Requested-With': 'com.valvesoftware.android.steam.community', + Referer: 'https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client', + 'User-Agent': this._options.mobileUserAgent || details.mobileUserAgent || 'Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', + Accept: 'text/javascript, text/html, application/xml, text/xml, */*' }; - this._setCookie(Request.cookie("mobileClientVersion=0 (2.1.3)")); - this._setCookie(Request.cookie("mobileClient=android")); + this._setCookie(Request.cookie('mobileClientVersion=0 (2.1.3)')); + this._setCookie(Request.cookie('mobileClient=android')); } else { - mobileHeaders = {"Referer": "https://steamcommunity.com/login"}; + mobileHeaders = {Referer: 'https://steamcommunity.com/login'}; } - this.httpRequestPost("https://steamcommunity.com/login/getrsakey/", { - "form": {"username": details.accountName}, - "headers": mobileHeaders, - "json": true + this.httpRequestPost('https://steamcommunity.com/login/getrsakey/', { + form: {username: details.accountName}, + headers: mobileHeaders, + json: true }, function(err, response, body) { // Remove the mobile cookies if (err) { @@ -104,38 +103,38 @@ SteamCommunity.prototype.login = function(details, callback) { if (!body.publickey_mod || !body.publickey_exp) { deleteMobileCookies(); - callback(new Error("Invalid RSA key received")); + callback(new Error('Invalid RSA key received')); return; } - var key = new RSA(); + let key = new RSA(); key.setPublic(body.publickey_mod, body.publickey_exp); - var formObj = { - "captcha_text": details.captcha || "", - "captchagid": self._captchaGid, - "emailauth": details.authCode || "", - "emailsteamid": "", - "password": hex2b64(key.encrypt(details.password)), - "remember_login": "true", - "rsatimestamp": body.timestamp, - "twofactorcode": details.twoFactorCode || "", - "username": details.accountName, - "loginfriendlyname": "", - "donotcache": Date.now() + let formObj = { + captcha_text: details.captcha || '', + captchagid: self._captchaGid, + emailauth: details.authCode || '', + emailsteamid: '', + password: hex2b64(key.encrypt(details.password)), + remember_login: 'true', + rsatimestamp: body.timestamp, + twofactorcode: details.twoFactorCode || '', + username: details.accountName, + loginfriendlyname: '', + donotcache: Date.now() }; - if(!disableMobile){ - formObj.oauth_client_id = "DE45CD61"; - formObj.oauth_scope = "read_profile write_profile read_client write_client"; - formObj.loginfriendlyname = "#login_emailauth_friendlyname_mobile"; + if (!disableMobile){ + formObj.oauth_client_id = 'DE45CD61'; + formObj.oauth_scope = 'read_profile write_profile read_client write_client'; + formObj.loginfriendlyname = '#login_emailauth_friendlyname_mobile'; } self.httpRequestPost({ - "uri": "https://steamcommunity.com/login/dologin/", - "json": true, - "form": formObj, - "headers": mobileHeaders + uri: 'https://steamcommunity.com/login/dologin/', + json: true, + form: formObj, + headers: mobileHeaders }, function(err, response, body) { deleteMobileCookies(); @@ -144,33 +143,33 @@ SteamCommunity.prototype.login = function(details, callback) { return; } - var error; + let error; if (!body.success && body.emailauth_needed) { // Steam Guard (email) - error = new Error("SteamGuard"); + error = new Error('SteamGuard'); error.emaildomain = body.emaildomain; callback(error); } else if (!body.success && body.requires_twofactor) { // Steam Guard (app) - callback(new Error("SteamGuardMobile")); + callback(new Error('SteamGuardMobile')); } else if (!body.success && body.captcha_needed && body.message.match(/Please verify your humanity/)) { - error = new Error("CAPTCHA"); - error.captchaurl = "https://steamcommunity.com/login/rendercaptcha/?gid=" + body.captcha_gid; + error = new Error('CAPTCHA'); + error.captchaurl = 'https://steamcommunity.com/login/rendercaptcha/?gid=' + body.captcha_gid; self._captchaGid = body.captcha_gid; callback(error); } else if (!body.success) { - callback(new Error(body.message || "Unknown error")); + callback(new Error(body.message || 'Unknown error')); } else if (!disableMobile && !body.oauth) { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); } else { - var sessionID = generateSessionID(); - var oAuth; + let sessionID = generateSessionID(); + let oAuth; self._setCookie(Request.cookie('sessionid=' + sessionID)); - var cookies = self._jar.getCookieString("https://steamcommunity.com").split(';').map(function(cookie) { + let cookies = self._jar.getCookieString('https://steamcommunity.com').split(';').map(function(cookie) { return cookie.trim(); }); @@ -178,11 +177,11 @@ SteamCommunity.prototype.login = function(details, callback) { oAuth = JSON.parse(body.oauth); self.steamID = new SteamID(oAuth.steamid); self.oAuthToken = oAuth.oauth_token; - }else{ - for(var i = 0; i < cookies.length; i++) { - var parts = cookies[i].split('='); - if(parts[0] == 'steamLogin') { - self.steamID = new SteamID(decodeURIComponent(parts[1]).split('||')[0]) + } else { + for (let i = 0; i < cookies.length; i++) { + let parts = cookies[i].split('='); + if (parts[0] == 'steamLogin') { + self.steamID = new SteamID(decodeURIComponent(parts[1]).split('||')[0]); break; } } @@ -191,10 +190,10 @@ SteamCommunity.prototype.login = function(details, callback) { } // Find the Steam Guard cookie - var steamguard = null; - for(var i = 0; i < cookies.length; i++) { - var parts = cookies[i].split('='); - if(parts[0] == 'steamMachineAuth' + self.steamID) { + let steamguard = null; + for (let i = 0; i < cookies.length; i++) { + let parts = cookies[i].split('='); + if (parts[0] == 'steamMachineAuth' + self.steamID) { steamguard = self.steamID.toString() + '||' + decodeURIComponent(parts[1]); break; } @@ -204,11 +203,11 @@ SteamCommunity.prototype.login = function(details, callback) { callback(null, sessionID, cookies, steamguard, disableMobile ? null : oAuth.oauth_token); } - }, "steamcommunity"); - }, "steamcommunity"); + }, 'steamcommunity'); + }, 'steamcommunity'); function deleteMobileCookies() { - var cookie = Request.cookie('mobileClientVersion='); + let cookie = Request.cookie('mobileClientVersion='); cookie.expires = new Date(0); self._setCookie(cookie); @@ -220,27 +219,27 @@ SteamCommunity.prototype.login = function(details, callback) { SteamCommunity.prototype.oAuthLogin = function(steamguard, token, callback) { steamguard = steamguard.split('||'); - var steamID = new SteamID(steamguard[0]); + let steamID = new SteamID(steamguard[0]); - var self = this; + let self = this; this.httpRequestPost({ - "uri": "https://api.steampowered.com/IMobileAuthService/GetWGToken/v1/", - "form": { - "access_token": token + uri: 'https://api.steampowered.com/IMobileAuthService/GetWGToken/v1/', + form: { + access_token: token }, - "json": true + json: true }, function(err, response, body) { if (err) { callback(err); return; } - if(!body.response || !body.response.token || !body.response.token_secure) { - callback(new Error("Malformed response")); + if (!body.response || !body.response.token || !body.response.token_secure) { + callback(new Error('Malformed response')); return; } - var cookies = [ + let cookies = [ 'steamLogin=' + encodeURIComponent(steamID.getSteamID64() + '||' + body.response.token), 'steamLoginSecure=' + encodeURIComponent(steamID.getSteamID64() + '||' + body.response.token_secure), 'steamMachineAuth' + steamID.getSteamID64() + '=' + steamguard[1], @@ -249,7 +248,7 @@ SteamCommunity.prototype.oAuthLogin = function(steamguard, token, callback) { self.setCookies(cookies); callback(null, self.getSessionID(), cookies); - }, "steamcommunity"); + }, 'steamcommunity'); }; /** @@ -258,8 +257,8 @@ SteamCommunity.prototype.oAuthLogin = function(steamguard, token, callback) { */ SteamCommunity.prototype.getClientLogonToken = function(callback) { this.httpRequestGet({ - "uri": "https://steamcommunity.com/chat/clientjstoken", - "json": true + uri: 'https://steamcommunity.com/chat/clientjstoken', + json: true }, (err, res, body) => { if (err || res.statusCode != 200) { callback(err ? err : new Error('HTTP error ' + res.statusCode)); @@ -279,25 +278,25 @@ SteamCommunity.prototype.getClientLogonToken = function(callback) { } callback(null, { - "steamID": new SteamID(body.steamid), - "accountName": body.account_name, - "webLogonToken": body.token + steamID: new SteamID(body.steamid), + accountName: body.account_name, + webLogonToken: body.token }); }); }; SteamCommunity.prototype._setCookie = function(cookie, secure) { - var protocol = secure ? "https" : "http"; + let protocol = secure ? 'https' : 'http'; cookie.secure = !!secure; - this._jar.setCookie(cookie.clone(), protocol + "://steamcommunity.com"); - this._jar.setCookie(cookie.clone(), protocol + "://store.steampowered.com"); - this._jar.setCookie(cookie.clone(), protocol + "://help.steampowered.com"); + this._jar.setCookie(cookie.clone(), protocol + '://steamcommunity.com'); + this._jar.setCookie(cookie.clone(), protocol + '://store.steampowered.com'); + this._jar.setCookie(cookie.clone(), protocol + '://help.steampowered.com'); }; SteamCommunity.prototype.setCookies = function(cookies) { cookies.forEach((cookie) => { - var cookieName = cookie.match(/(.+)=/)[1]; + let cookieName = cookie.match(/(.+)=/)[1]; if (cookieName == 'steamLogin' || cookieName == 'steamLoginSecure') { this.steamID = new SteamID(cookie.match(/=(\d+)/)[1]); } @@ -306,16 +305,16 @@ SteamCommunity.prototype.setCookies = function(cookies) { }); }; -SteamCommunity.prototype.getSessionID = function(host = "http://steamcommunity.com") { - var cookies = this._jar.getCookieString(host).split(';'); - for(var i = 0; i < cookies.length; i++) { - var match = cookies[i].trim().match(/([^=]+)=(.+)/); - if(match[1] == 'sessionid') { +SteamCommunity.prototype.getSessionID = function(host = 'http://steamcommunity.com') { + let cookies = this._jar.getCookieString(host).split(';'); + for (let i = 0; i < cookies.length; i++) { + let match = cookies[i].trim().match(/([^=]+)=(.+)/); + if (match[1] == 'sessionid') { return decodeURIComponent(match[2]); } } - var sessionID = generateSessionID(); + let sessionID = generateSessionID(); this._setCookie(Request.cookie('sessionid=' + sessionID)); return sessionID; }; @@ -325,17 +324,17 @@ function generateSessionID() { } SteamCommunity.prototype.parentalUnlock = function(pin, callback) { - var self = this; - var sessionID = self.getSessionID(); + let self = this; + let sessionID = self.getSessionID(); - this.httpRequestPost("https://steamcommunity.com/parental/ajaxunlock", { - "json": true, - "form": { - "pin": pin, - "sessionid": sessionID + this.httpRequestPost('https://steamcommunity.com/parental/ajaxunlock', { + json: true, + form: { + pin: pin, + sessionid: sessionID } }, function(err, response, body) { - if(!callback) { + if (!callback) { return; } @@ -345,127 +344,125 @@ SteamCommunity.prototype.parentalUnlock = function(pin, callback) { } if (!body || typeof body.success !== 'boolean') { - callback("Invalid response"); + callback('Invalid response'); return; } if (!body.success) { switch (body.eresult) { - case 15: - callback("Incorrect PIN"); + case SteamCommunity.EResult.AccessDenied: + callback('Incorrect PIN'); break; - case 25: - callback("Too many invalid PIN attempts"); + case SteamCommunity.EResult.LimitExceeded: + callback('Too many invalid PIN attempts'); break; default: - callback("Error " + body.eresult); + callback('Error ' + body.eresult); } return; } callback(); - }.bind(this), "steamcommunity"); + }.bind(this), 'steamcommunity'); }; SteamCommunity.prototype.getNotifications = function(callback) { - var self = this; this.httpRequestGet({ - "uri": "https://steamcommunity.com/actions/GetNotificationCounts", - "json": true - }, function(err, response, body) { + uri: 'https://steamcommunity.com/actions/GetNotificationCounts', + json: true + }, (err, response, body) => { if (err) { callback(err); return; } if (!body || !body.notifications) { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); return; } - var notifications = { - "trades": body.notifications[1] || 0, - "gameTurns": body.notifications[2] || 0, - "moderatorMessages": body.notifications[3] || 0, - "comments": body.notifications[4] || 0, - "items": body.notifications[5] || 0, - "invites": body.notifications[6] || 0, + let notifications = { + trades: body.notifications[1] || 0, + gameTurns: body.notifications[2] || 0, + moderatorMessages: body.notifications[3] || 0, + comments: body.notifications[4] || 0, + items: body.notifications[5] || 0, + invites: body.notifications[6] || 0, // dunno about 7 - "gifts": body.notifications[8] || 0, - "chat": body.notifications[9] || 0, - "helpRequestReplies": body.notifications[10] || 0, - "accountAlerts": body.notifications[11] || 0 + gifts: body.notifications[8] || 0, + chat: body.notifications[9] || 0, + helpRequestReplies: body.notifications[10] || 0, + accountAlerts: body.notifications[11] || 0 }; callback(null, notifications); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.resetItemNotifications = function(callback) { - var self = this; - this.httpRequestGet("https://steamcommunity.com/my/inventory", function(err, response, body) { - if(!callback) { + this.httpRequestGet('https://steamcommunity.com/my/inventory', (err, response, body) => { + if (!callback) { return; } callback(err || null); - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.loggedIn = function(callback) { this.httpRequestGet({ - "uri": "https://steamcommunity.com/my", - "followRedirect": false, - "checkHttpError": false - }, function(err, response, body) { - if(err || (response.statusCode != 302 && response.statusCode != 403)) { - callback(err || new Error("HTTP error " + response.statusCode)); + uri: 'https://steamcommunity.com/my', + followRedirect: false, + checkHttpError: false + }, (err, response, body) => { + if (err || (response.statusCode != 302 && response.statusCode != 403)) { + callback(err || new Error('HTTP error ' + response.statusCode)); return; } - if(response.statusCode == 403) { + if (response.statusCode == 403) { callback(null, true, true); return; } - callback(null, !!response.headers.location.match(/steamcommunity\.com(\/(id|profiles)\/[^\/]+)\/?/), false); - }, "steamcommunity"); + callback(null, !!response.headers.location.match(/steamcommunity\.com(\/(id|profiles)\/[^/]+)\/?/), false); + }, 'steamcommunity'); }; SteamCommunity.prototype.getTradeURL = function(callback) { - this._myProfile("tradeoffers/privacy", null, (err, response, body) => { + this._myProfile('tradeoffers/privacy', null, (err, response, body) => { if (err) { callback(err); return; } - var match = body.match(/https?:\/\/(www.)?steamcommunity.com\/tradeoffer\/new\/?\?partner=\d+(&|&)token=([a-zA-Z0-9-_]+)/); + let match = body.match(/https?:\/\/(www.)?steamcommunity.com\/tradeoffer\/new\/?\?partner=\d+(&|&)token=([a-zA-Z0-9-_]+)/); if (match) { - var token = match[3]; + let token = match[3]; callback(null, match[0], token); } else { - callback(new Error("Malformed response")); + callback(new Error('Malformed response')); } - }, "steamcommunity"); + }, 'steamcommunity'); }; SteamCommunity.prototype.changeTradeURL = function(callback) { - this._myProfile("tradeoffers/newtradeurl", {"sessionid": this.getSessionID()}, (err, response, body) => { + this._myProfile('tradeoffers/newtradeurl', {sessionid: this.getSessionID()}, (err, response, body) => { if (!callback) { return; } - if (!body || typeof body !== "string" || body.length < 3 || body.indexOf('"') !== 0) { - callback(new Error("Malformed response")); + if (!body || typeof body !== 'string' || body.length < 3 || body.indexOf('"') !== 0) { + callback(new Error('Malformed response')); return; } - var newToken = body.replace(/"/g, ''); //"t1o2k3e4n" => t1o2k3e4n - callback(null, "https://steamcommunity.com/tradeoffer/new/?partner=" + this.steamID.accountid + "&token=" + newToken, newToken); - }, "steamcommunity"); + let newToken = body.replace(/"/g, ''); //"t1o2k3e4n" => t1o2k3e4n + callback(null, 'https://steamcommunity.com/tradeoffer/new/?partner=' + this.steamID.accountid + '&token=' + newToken, newToken); + }, 'steamcommunity'); }; /** @@ -473,7 +470,7 @@ SteamCommunity.prototype.changeTradeURL = function(callback) { * @param {function} callback */ SteamCommunity.prototype.clearPersonaNameHistory = function(callback) { - this._myProfile("ajaxclearaliashistory/", {"sessionid": this.getSessionID()}, (err, res, body) => { + this._myProfile('ajaxclearaliashistory/', {sessionid: this.getSessionID()}, (err, res, body) => { if (!callback) { return; } @@ -483,33 +480,33 @@ SteamCommunity.prototype.clearPersonaNameHistory = function(callback) { } if (res.statusCode != 200) { - return callback(new Error("HTTP error " + res.statusCode)); + return callback(new Error('HTTP error ' + res.statusCode)); } try { body = JSON.parse(body); callback(Helpers.eresultError(body.success)); } catch (ex) { - return callback(new Error("Malformed response")); + return callback(new Error('Malformed response')); } }); }; SteamCommunity.prototype._myProfile = function(endpoint, form, callback) { - var self = this; + let self = this; if (this._profileURL) { completeRequest(this._profileURL); } else { - this.httpRequest("https://steamcommunity.com/my", {"followRedirect": false}, function(err, response, body) { - if(err || response.statusCode != 302) { - callback(err || "HTTP error " + response.statusCode); + this.httpRequest('https://steamcommunity.com/my', {followRedirect: false}, function(err, response, body) { + if (err || response.statusCode != 302) { + callback(err || 'HTTP error ' + response.statusCode); return; } - var match = response.headers.location.match(/steamcommunity\.com(\/(id|profiles)\/[^\/]+)\/?/); - if(!match) { - callback(new Error("Can't get profile URL")); + let match = response.headers.location.match(/steamcommunity\.com(\/(id|profiles)\/[^/]+)\/?/); + if (!match) { + callback(new Error('Can\'t get profile URL')); return; } @@ -519,22 +516,22 @@ SteamCommunity.prototype._myProfile = function(endpoint, form, callback) { }, 60000).unref(); completeRequest(match[1]); - }, "steamcommunity"); + }, 'steamcommunity'); } function completeRequest(url) { - var options = endpoint.endpoint ? endpoint : {}; - options.uri = "https://steamcommunity.com" + url + "/" + (endpoint.endpoint || endpoint); + let options = endpoint.endpoint ? endpoint : {}; + options.uri = 'https://steamcommunity.com' + url + '/' + (endpoint.endpoint || endpoint); if (form) { - options.method = "POST"; + options.method = 'POST'; options.form = form; options.followAllRedirects = true; } else if (!options.method) { - options.method = "GET"; + options.method = 'GET'; } - self.httpRequest(options, callback, "steamcommunity"); + self.httpRequest(options, callback, 'steamcommunity'); } }; @@ -545,8 +542,8 @@ SteamCommunity.prototype._myProfile = function(endpoint, form, callback) { */ SteamCommunity.prototype.getFriendsList = function(callback) { this.httpRequestGet({ - "uri": "https://steamcommunity.com/textfilter/ajaxgetfriendslist", - "json": true + uri: 'https://steamcommunity.com/textfilter/ajaxgetfriendslist', + json: true }, (err, res, body) => { if (err) { callback(err ? err : new Error('HTTP error ' + res.statusCode)); diff --git a/resources/EChatState.js b/resources/EChatState.js deleted file mode 100644 index 287ee8d4..00000000 --- a/resources/EChatState.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @enum EChatState - */ -module.exports = { - "Offline": 0, - "LoggingOn": 1, - "LogOnFailed": 2, - "LoggedOn": 3, - - "0": "Offline", - "1": "LoggingOn", - "2": "LogOnFailed", - "3": "LoggedOn" -}; From 600a41143b050a67a32956692511c1f149e2b607 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 02:55:56 -0400 Subject: [PATCH 22/68] Refactor all files --- .idea/inspectionProfiles/Project_Default.xml | 1 + classes/CConfirmation.js | 4 +- classes/CEconItem.js | 39 +++---- classes/CMarketItem.js | 113 +++++++++---------- classes/CMarketSearchResult.js | 59 +++++----- classes/CSteamGroup.js | 31 +++-- classes/CSteamUser.js | 60 +++++----- components/groups.js | 8 +- components/help.js | 2 - components/helpers.js | 4 +- components/market.js | 4 +- components/profile.js | 23 ++-- components/twofactor.js | 8 +- components/users.js | 17 +-- examples/edit-group-announcement.js | 80 ++++++------- index.js | 112 +++++++++--------- 16 files changed, 265 insertions(+), 300 deletions(-) diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 1c130558..a69dc013 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -8,5 +8,6 @@ + \ No newline at end of file diff --git a/classes/CConfirmation.js b/classes/CConfirmation.js index 85854742..80172c48 100644 --- a/classes/CConfirmation.js +++ b/classes/CConfirmation.js @@ -3,7 +3,7 @@ const SteamCommunity = require('../index.js'); module.exports = CConfirmation; function CConfirmation(community, data) { - Object.defineProperty(this, "_community", {"value": community}); + Object.defineProperty(this, '_community', {value: community}); this.id = data.id.toString(); this.type = data.type; @@ -19,7 +19,7 @@ function CConfirmation(community, data) { CConfirmation.prototype.getOfferID = function(time, key, callback) { if (this.type && this.creator) { if (this.type != SteamCommunity.ConfirmationType.Trade) { - callback(new Error("Not a trade confirmation")); + callback(new Error('Not a trade confirmation')); return; } diff --git a/classes/CEconItem.js b/classes/CEconItem.js index 4ccfc9b3..49eebd50 100644 --- a/classes/CEconItem.js +++ b/classes/CEconItem.js @@ -1,14 +1,11 @@ module.exports = CEconItem; function CEconItem(item, description, contextID) { - var thing; - for (thing in item) { - if (item.hasOwnProperty(thing)) { - this[thing] = item[thing]; - } + for (let thing in item) { + this[thing] = item[thing]; } - var isCurrency = !!(this.is_currency || this.currency) || typeof this.currencyid !== 'undefined'; // I don't want to put this on the object yet; it's nice to have the ids at the top of printed output + let isCurrency = !!(this.is_currency || this.currency) || typeof this.currencyid !== 'undefined'; // I don't want to put this on the object yet; it's nice to have the ids at the top of printed output if (isCurrency) { this.currencyid = this.id = (this.id || this.currencyid); @@ -27,10 +24,8 @@ function CEconItem(item, description, contextID) { description = description[this.classid + '_' + this.instanceid]; } - for (thing in description) { - if (description.hasOwnProperty(thing)) { - this[thing] = description[thing]; - } + for (let thing in description) { + this[thing] = description[thing]; } } @@ -51,18 +46,18 @@ function CEconItem(item, description, contextID) { if (this.tags) { this.tags = this.tags.map(function(tag) { return { - "internal_name": tag.internal_name, - "name": tag.localized_tag_name || tag.name, - "category": tag.category, - "color": tag.color || "", - "category_name": tag.localized_category_name || tag.category_name + internal_name: tag.internal_name, + name: tag.localized_tag_name || tag.name, + category: tag.category, + color: tag.color || '', + category_name: tag.localized_category_name || tag.category_name }; }); } // Restore market_fee_app, if applicable - var match; - if (this.appid == 753 && this.contextid == 6 && this.market_hash_name && (match = this.market_hash_name.match(/^(\d+)\-/))) { + let match; + if (this.appid == 753 && this.contextid == 6 && this.market_hash_name && (match = this.market_hash_name.match(/^(\d+)-/))) { this.market_fee_app = parseInt(match[1], 10); } @@ -82,7 +77,7 @@ function CEconItem(item, description, contextID) { this.cache_expiration = this.item_expiration; } - if (this.actions === "") { + if (this.actions === '') { this.actions = []; } @@ -94,15 +89,15 @@ function CEconItem(item, description, contextID) { } CEconItem.prototype.getImageURL = function() { - return "https://steamcommunity-a.akamaihd.net/economy/image/" + this.icon_url + "/"; + return 'https://steamcommunity-a.akamaihd.net/economy/image/' + this.icon_url + '/'; }; CEconItem.prototype.getLargeImageURL = function() { - if(!this.icon_url_large) { + if (!this.icon_url_large) { return this.getImageURL(); } - return "https://steamcommunity-a.akamaihd.net/economy/image/" + this.icon_url_large + "/"; + return 'https://steamcommunity-a.akamaihd.net/economy/image/' + this.icon_url_large + '/'; }; CEconItem.prototype.getTag = function(category) { @@ -110,7 +105,7 @@ CEconItem.prototype.getTag = function(category) { return null; } - for (var i = 0; i < this.tags.length; i++) { + for (let i = 0; i < this.tags.length; i++) { if (this.tags[i].category == category) { return this.tags[i]; } diff --git a/classes/CMarketItem.js b/classes/CMarketItem.js index b031d341..c489cb45 100644 --- a/classes/CMarketItem.js +++ b/classes/CMarketItem.js @@ -3,32 +3,33 @@ const Cheerio = require('cheerio'); const SteamCommunity = require('../index.js'); SteamCommunity.prototype.getMarketItem = function(appid, hashName, currency, callback) { - if (typeof currency == "function") { + if (typeof currency == 'function') { callback = currency; currency = 1; } - var self = this; - this.httpRequest("https://steamcommunity.com/market/listings/" + appid + "/" + encodeURIComponent(hashName), function(err, response, body) { + + this.httpRequest('https://steamcommunity.com/market/listings/' + appid + '/' + encodeURIComponent(hashName), (err, response, body) => { if (err) { callback(err); return; } - var $ = Cheerio.load(body); - if($('.market_listing_table_message') && $('.market_listing_table_message').text().trim() == 'There are no listings for this item.') { - callback(new Error("There are no listings for this item.")); + let $ = Cheerio.load(body); + let $listingTableMessage = $('.market_listing_table_message'); + if ($listingTableMessage && $listingTableMessage.text().trim() == 'There are no listings for this item.') { + callback(new Error('There are no listings for this item.')); return; } - var item = new CMarketItem(appid, hashName, self, body, $); + let item = new CMarketItem(appid, hashName, this, body, $); item.updatePrice(currency, function(err) { - if(err) { + if (err) { callback(err); } else { callback(null, item); } }); - }, "steamcommunity"); + }, 'steamcommunity'); }; function CMarketItem(appid, hashName, community, body, $) { @@ -37,38 +38,38 @@ function CMarketItem(appid, hashName, community, body, $) { this._community = community; this._$ = $; - this._country = "US"; - var match = body.match(/var g_strCountryCode = "([^"]+)";/); - if(match) { + this._country = 'US'; + let match = body.match(/var g_strCountryCode = "([^"]+)";/); + if (match) { this._country = match[1]; } - this._language = "english"; + this._language = 'english'; match = body.match(/var g_strLanguage = "([^"]+)";/); - if(match) { + if (match) { this._language = match[1]; } this.commodity = false; match = body.match(/Market_LoadOrderSpread\(\s*(\d+)\s*\);/); - if(match) { + if (match) { this.commodity = true; this.commodityID = parseInt(match[1], 10); } this.medianSalePrices = null; match = body.match(/var line1=([^;]+);/); - if(match) { + if (match) { try { this.medianSalePrices = JSON.parse(match[1]); this.medianSalePrices = this.medianSalePrices.map(function(item) { return { - "hour": new Date(item[0]), - "price": item[1], - "quantity": parseInt(item[2], 10) + hour: new Date(item[0]), + price: item[1], + quantity: parseInt(item[2], 10) }; }); - } catch(e) { + } catch (e) { // ignore } } @@ -101,90 +102,88 @@ CMarketItem.prototype.updatePrice = function (currency, callback) { }; CMarketItem.prototype.updatePriceForCommodity = function(currency, callback) { - if(!this.commodity) { - throw new Error("Cannot update price for non-commodity item"); + if (!this.commodity) { + throw new Error('Cannot update price for non-commodity item'); } - var self = this; this._community.httpRequest({ - "uri": "https://steamcommunity.com/market/itemordershistogram?country=US&language=english¤cy=" + currency + "&item_nameid=" + this.commodityID, - "json": true - }, function(err, response, body) { + uri: 'https://steamcommunity.com/market/itemordershistogram?country=US&language=english¤cy=' + currency + '&item_nameid=' + this.commodityID, + json: true + }, (err, response, body) => { if (err) { callback(err); return; } - if(body.success != 1) { - if(callback) { - callback(new Error("Error " + body.success)); + if (body.success != 1) { + if (callback) { + callback(new Error('Error ' + body.success)); } return; } - var match = (body.sell_order_summary || '').match(/(\d+)<\/span>/); - if(match) { - self.quantity = parseInt(match[1], 10); + let match = (body.sell_order_summary || '').match(/(\d+)<\/span>/); + if (match) { + this.quantity = parseInt(match[1], 10); } - self.buyQuantity = 0; + this.buyQuantity = 0; match = (body.buy_order_summary || '').match(/(\d+)<\/span>/); - if(match) { - self.buyQuantity = parseInt(match[1], 10); + if (match) { + this.buyQuantity = parseInt(match[1], 10); } - self.lowestPrice = parseInt(body.lowest_sell_order, 10); - self.highestBuyOrder = parseInt(body.highest_buy_order, 10); + this.lowestPrice = parseInt(body.lowest_sell_order, 10); + this.highestBuyOrder = parseInt(body.highest_buy_order, 10); // TODO: The tables? - if(callback) { + if (callback) { callback(null); } - }, "steamcommunity"); + }, 'steamcommunity'); }; CMarketItem.prototype.updatePriceForNonCommodity = function (currency, callback) { - if(this.commodity) { - throw new Error("Cannot update price for commodity item"); + if (this.commodity) { + throw new Error('Cannot update price for commodity item'); } - var self = this; this._community.httpRequest({ - "uri": "https://steamcommunity.com/market/listings/" + - this._appid + "/" + + uri: 'https://steamcommunity.com/market/listings/' + + this._appid + '/' + encodeURIComponent(this._hashName) + - "/render/?query=&start=0&count=10&country=US&language=english¤cy=" + currency, - "json": true - }, function(err, response, body) { + '/render/?query=&start=0&count=10&country=US&language=english¤cy=' + currency, + json: true + }, (err, response, body) => { if (err) { callback(err); return; } if (body.success != 1) { - callback && callback(new Error("Error " + body.success)); + callback && callback(new Error('Error ' + body.success)); return; } - var match = body.total_count; + let match = body.total_count; if (match) { - self.quantity = parseInt(match, 10); + this.quantity = parseInt(match, 10); } - var lowestPrice; - var $ = Cheerio.load(body.results_html); - match = $(".market_listing_price.market_listing_price_with_fee"); + let lowestPrice; + let $ = Cheerio.load(body.results_html); + match = $('.market_listing_price.market_listing_price_with_fee'); if (match) { - for (var i = 0; i < match.length; i++) { - lowestPrice = parseFloat($(match[i]).text().replace(",", ".").replace(/[^\d.]/g, '')); + for (let i = 0; i < match.length; i++) { + lowestPrice = parseFloat($(match[i]).text().replace(',', '.').replace(/[^\d.]/g, '')); if (!isNaN(lowestPrice)) { - self.lowestPrice = lowestPrice; + this.lowestPrice = lowestPrice; break; } } } callback && callback(null); - }, "steamcommunity"); + }, 'steamcommunity'); }; diff --git a/classes/CMarketSearchResult.js b/classes/CMarketSearchResult.js index 8d1c8883..3affa66b 100644 --- a/classes/CMarketSearchResult.js +++ b/classes/CMarketSearchResult.js @@ -3,18 +3,18 @@ const Cheerio = require('cheerio'); const SteamCommunity = require('../index.js'); SteamCommunity.prototype.marketSearch = function(options, callback) { - var qs = {}; + let qs = {}; - if(typeof options === 'string') { + if (typeof options === 'string') { qs.query = options; } else { qs.query = options.query || ''; qs.appid = options.appid; qs.search_descriptions = options.searchDescriptions ? 1 : 0; - if(qs.appid) { - for(var i in options) { - if(['query', 'appid', 'searchDescriptions'].indexOf(i) != -1) { + if (qs.appid) { + for (let i in options) { + if (['query', 'appid', 'searchDescriptions'].indexOf(i) != -1) { continue; } @@ -29,62 +29,61 @@ SteamCommunity.prototype.marketSearch = function(options, callback) { qs.sort_column = 'price'; qs.sort_dir = 'asc'; - var self = this; - var results = []; - performSearch(); - - function performSearch() { - self.httpRequest({ - "uri": "https://steamcommunity.com/market/search/render/", - "qs": qs, - "headers": { - "referer": "https://steamcommunity.com/market/search" + let results = []; + const performSearch = () => { + this.httpRequest({ + uri: 'https://steamcommunity.com/market/search/render/', + qs: qs, + headers: { + referer: 'https://steamcommunity.com/market/search' }, - "json": true + json: true }, function(err, response, body) { if (err) { callback(err); return; } - if(!body.success) { - callback(new Error("Success is not true")); + if (!body.success) { + callback(new Error('Success is not true')); return; } - if(!body.results_html) { - callback(new Error("No results_html in response")); + if (!body.results_html) { + callback(new Error('No results_html in response')); return; } - var $ = Cheerio.load(body.results_html); - var $errorMsg = $('.market_listing_table_message'); - if($errorMsg.length > 0) { + let $ = Cheerio.load(body.results_html); + let $errorMsg = $('.market_listing_table_message'); + if ($errorMsg.length > 0) { callback(new Error($errorMsg.text())); return; } - var rows = $('.market_listing_row_link'); - for(var i = 0; i < rows.length; i++) { + let rows = $('.market_listing_row_link'); + for (let i = 0; i < rows.length; i++) { results.push(new CMarketSearchResult($(rows[i]))); } - if(body.start + body.pagesize >= body.total_count) { + if (body.start + body.pagesize >= body.total_count) { callback(null, results); } else { qs.start += body.pagesize; performSearch(); } - }, "steamcommunity"); - } + }, 'steamcommunity'); + }; + + performSearch(); }; function CMarketSearchResult(row) { - var match = row.attr('href').match(/\/market\/listings\/(\d+)\/([^\?\/]+)/); + let match = row.attr('href').match(/\/market\/listings\/(\d+)\/([^?/]+)/); this.appid = parseInt(match[1], 10); this.market_hash_name = decodeURIComponent(match[2]); - this.image = ((row.find('.market_listing_item_img').attr('src') || "").match(/^https?:\/\/[^\/]+\/economy\/image\/[^\/]+\//) || [])[0]; + this.image = ((row.find('.market_listing_item_img').attr('src') || '').match(/^https?:\/\/[^/]+\/economy\/image\/[^/]+\//) || [])[0]; this.price = parseInt(row.find('.market_listing_their_price .market_table_value span.normal_price').text().replace(/[^\d]+/g, ''), 10); this.quantity = parseInt(row.find('.market_listing_num_listings_qty').text().replace(/[^\d]+/g, ''), 10); } diff --git a/classes/CSteamGroup.js b/classes/CSteamGroup.js index d93acfc4..b1a3139e 100644 --- a/classes/CSteamGroup.js +++ b/classes/CSteamGroup.js @@ -5,30 +5,29 @@ const Helpers = require('../components/helpers.js'); const SteamCommunity = require('../index.js'); SteamCommunity.prototype.getSteamGroup = function(id, callback) { - if(typeof id !== 'string' && !Helpers.isSteamID(id)) { - throw new Error("id parameter should be a group URL string or a SteamID object"); + if (typeof id !== 'string' && !Helpers.isSteamID(id)) { + throw new Error('id parameter should be a group URL string or a SteamID object'); } - if(typeof id === 'object' && (id.universe != SteamID.Universe.PUBLIC || id.type != SteamID.Type.CLAN)) { - throw new Error("SteamID must stand for a clan account in the public universe"); + if (typeof id === 'object' && (id.universe != SteamID.Universe.PUBLIC || id.type != SteamID.Type.CLAN)) { + throw new Error('SteamID must stand for a clan account in the public universe'); } - var self = this; - this.httpRequest("https://steamcommunity.com/" + (typeof id === 'string' ? "groups/" + id : "gid/" + id.toString()) + "/memberslistxml/?xml=1", function(err, response, body) { + this.httpRequest('https://steamcommunity.com/' + (typeof id === 'string' ? 'groups/' + id : 'gid/' + id.toString()) + '/memberslistxml/?xml=1', (err, response, body) => { if (err) { callback(err); return; } XML2JS.parseString(body, function(err, result) { - if(err) { + if (err) { callback(err); return; } - callback(null, new CSteamGroup(self, result.memberList)); + callback(null, new CSteamGroup(this, result.memberList)); }); - }, "steamcommunity"); + }, 'steamcommunity'); }; function CSteamGroup(community, groupData) { @@ -50,16 +49,16 @@ CSteamGroup.prototype.getAvatarURL = function(size, protocol) { size = size || ''; protocol = protocol || 'http://'; - var url = protocol + "steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/" + this.avatarHash.substring(0, 2) + "/" + this.avatarHash; - if(size == 'full' || size == 'medium') { - return url + "_" + size + ".jpg"; + let url = protocol + 'steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/' + this.avatarHash.substring(0, 2) + '/' + this.avatarHash; + if (size == 'full' || size == 'medium') { + return url + '_' + size + '.jpg'; } else { - return url + ".jpg"; + return url + '.jpg'; } }; CSteamGroup.prototype.getMembers = function(addresses, callback) { - if(typeof addresses === 'function') { + if (typeof addresses === 'function') { callback = addresses; addresses = null; } @@ -84,11 +83,11 @@ CSteamGroup.prototype.postAnnouncement = function(headline, content, hidden, cal }; CSteamGroup.prototype.editAnnouncement = function(annoucementID, headline, content, callback) { - this._community.editGroupAnnouncement(this.steamID, annoucementID, headline, content, callback) + this._community.editGroupAnnouncement(this.steamID, annoucementID, headline, content, callback); }; CSteamGroup.prototype.deleteAnnouncement = function(annoucementID, callback) { - this._community.deleteGroupAnnouncement(this.steamID, annoucementID, callback) + this._community.deleteGroupAnnouncement(this.steamID, annoucementID, callback); }; CSteamGroup.prototype.scheduleEvent = function(name, type, description, time, server, callback) { diff --git a/classes/CSteamUser.js b/classes/CSteamUser.js index 7ee6ce41..5ba68d30 100644 --- a/classes/CSteamUser.js +++ b/classes/CSteamUser.js @@ -5,49 +5,48 @@ const Helpers = require('../components/helpers.js'); const SteamCommunity = require('../index.js'); SteamCommunity.prototype.getSteamUser = function(id, callback) { - if(typeof id !== 'string' && !Helpers.isSteamID(id)) { - throw new Error("id parameter should be a user URL string or a SteamID object"); + if (typeof id !== 'string' && !Helpers.isSteamID(id)) { + throw new Error('id parameter should be a user URL string or a SteamID object'); } - if(typeof id === 'object' && (id.universe != SteamID.Universe.PUBLIC || id.type != SteamID.Type.INDIVIDUAL)) { - throw new Error("SteamID must stand for an individual account in the public universe"); + if (typeof id === 'object' && (id.universe != SteamID.Universe.PUBLIC || id.type != SteamID.Type.INDIVIDUAL)) { + throw new Error('SteamID must stand for an individual account in the public universe'); } - var self = this; - this.httpRequest("http://steamcommunity.com/" + (typeof id === 'string' ? "id/" + id : "profiles/" + id.toString()) + "/?xml=1", function(err, response, body) { + this.httpRequest('http://steamcommunity.com/' + (typeof id === 'string' ? 'id/' + id : 'profiles/' + id.toString()) + '/?xml=1', (err, response, body) => { if (err) { callback(err); return; } XML2JS.parseString(body, function(err, result) { - if(err || (!result.response && !result.profile)) { - callback(err || new Error("No valid response")); + if (err || (!result.response && !result.profile)) { + callback(err || new Error('No valid response')); return; } - if(result.response && result.response.error && result.response.error.length) { + if (result.response && result.response.error && result.response.error.length) { callback(new Error(result.response.error[0])); return; } // Try and find custom URL from redirect - var customurl = null; - if(response.request.redirects && response.request.redirects.length) { - var match = response.request.redirects[0].redirectUri.match(/https?:\/\/steamcommunity\.com\/id\/([^/])+\/\?xml=1/); - if(match) { + let customurl = null; + if (response.request.redirects && response.request.redirects.length) { + let match = response.request.redirects[0].redirectUri.match(/https?:\/\/steamcommunity\.com\/id\/([^/])+\/\?xml=1/); + if (match) { customurl = match[1]; } } - if(!result.profile.steamID64) { - callback(new Error("No valid response")); + if (!result.profile.steamID64) { + callback(new Error('No valid response')); return; } - callback(null, new CSteamUser(self, result.profile, customurl)); + callback(null, new CSteamUser(this, result.profile, customurl)); }); - }, "steamcommunity"); + }, 'steamcommunity'); }; function CSteamUser(community, userData, customurl) { @@ -60,7 +59,7 @@ function CSteamUser(community, userData, customurl) { this.privacyState = processItem('privacyState', 'uncreated'); this.visibilityState = processItem('visibilityState'); this.avatarHash = processItem('avatarIcon', '').match(/([0-9a-f]+)\.[a-z]+$/); - if(this.avatarHash) { + if (this.avatarHash) { this.avatarHash = this.avatarHash[1]; } @@ -69,8 +68,8 @@ function CSteamUser(community, userData, customurl) { this.isLimitedAccount = processItem('isLimitedAccount') == 1; this.customURL = processItem('customURL', customurl); - if(this.visibilityState == 3) { - let memberSinceValue = processItem('memberSince', '0').replace(/(\d{1,2})(st|nd|th)/, "$1"); + if (this.visibilityState == 3) { + let memberSinceValue = processItem('memberSince', '0').replace(/(\d{1,2})(st|nd|th)/, '$1'); if (memberSinceValue.indexOf(',') === -1) { memberSinceValue += ', ' + new Date().getFullYear(); @@ -92,11 +91,10 @@ function CSteamUser(community, userData, customurl) { this.groups = null; this.primaryGroup = null; - var self = this; - if(userData.groups && userData.groups[0] && userData.groups[0].group) { - this.groups = userData.groups[0].group.map(function(group) { - if(group['$'] && group['$'].isPrimary === "1") { - self.primaryGroup = new SteamID(group.groupID64[0]); + if (userData.groups && userData.groups[0] && userData.groups[0].group) { + this.groups = userData.groups[0].group.map((group) => { + if (group['$'] && group['$'].isPrimary === '1') { + this.primaryGroup = new SteamID(group.groupID64[0]); } return new SteamID(group.groupID64[0]); @@ -104,7 +102,7 @@ function CSteamUser(community, userData, customurl) { } function processItem(name, defaultVal) { - if(!userData[name]) { + if (!userData[name]) { return defaultVal; } @@ -116,13 +114,13 @@ CSteamUser.getAvatarURL = function(hash, size, protocol) { size = size || ''; protocol = protocol || 'http://'; - hash = hash || "72f78b4c8cc1f62323f8a33f6d53e27db57c2252"; // The default "?" avatar + hash = hash || '72f78b4c8cc1f62323f8a33f6d53e27db57c2252'; // The default "?" avatar - var url = protocol + "steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/" + hash.substring(0, 2) + "/" + hash; - if(size == 'full' || size == 'medium') { - return url + "_" + size + ".jpg"; + let url = protocol + 'steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/' + hash.substring(0, 2) + '/' + hash; + if (size == 'full' || size == 'medium') { + return url + '_' + size + '.jpg'; } else { - return url + ".jpg"; + return url + '.jpg'; } }; diff --git a/components/groups.js b/components/groups.js index 6a1c1ba0..40d4f045 100644 --- a/components/groups.js +++ b/components/groups.js @@ -143,7 +143,7 @@ SteamCommunity.prototype.getAllGroupAnnouncements = function(gid, time, callback date: new Date(announcement.pubDate[0]), author: (typeof announcement.author === 'undefined') ? null : announcement.author[0], aid: splitLink[splitLink.length - 1] - } + }; }).filter(announcement => announcement.date > time); return callback(null, announcements); @@ -171,7 +171,7 @@ SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content }; if (hidden) { - form.is_hidden = 'is_hidden' + form.is_hidden = 'is_hidden'; } this.httpRequestPost({ @@ -251,7 +251,7 @@ SteamCommunity.prototype.scheduleGroupEvent = function(gid, name, type, descript break; default: - if (typeof server != object) { + if (typeof server != 'object') { server = {ip: '', password: ''}; } } @@ -314,7 +314,7 @@ SteamCommunity.prototype.editGroupEvent = function(gid, id, name, type, descript break; default: - if (typeof server != object) { + if (typeof server != 'object') { server = {ip: '', password: ''}; } } diff --git a/components/help.js b/components/help.js index 881e3617..c0d6a862 100644 --- a/components/help.js +++ b/components/help.js @@ -1,7 +1,5 @@ const SteamCommunity = require('../index.js'); -const Helpers = require('./helpers.js'); - const HELP_SITE_DOMAIN = 'https://help.steampowered.com'; /** diff --git a/components/helpers.js b/components/helpers.js index e493b625..64d1ddcc 100644 --- a/components/helpers.js +++ b/components/helpers.js @@ -38,7 +38,7 @@ exports.decodeSteamTime = function(time) { /** * Get an Error object for a particular EResult - * @param {int} eresult + * @param {int|EResult} eresult * @param {string} [message] - If eresult is a failure code and message exists, this message will be used in the Error object instead * @returns {null|Error} */ @@ -48,7 +48,7 @@ exports.eresultError = function(eresult, message) { return null; } - let err = new Error(message || EResult[eresult] || ("Error " + eresult)); + let err = new Error(message || EResult[eresult] || `Error ${eresult}`); err.eresult = eresult; return err; }; diff --git a/components/market.js b/components/market.js index 25049a45..c786eeb3 100644 --- a/components/market.js +++ b/components/market.js @@ -103,7 +103,7 @@ SteamCommunity.prototype.turnItemIntoGems = function(appid, assetid, expectedGem } callback(null, {gemsReceived: parseInt(body['goo_value_received '], 10), totalGems: parseInt(body.goo_value_total, 10)}); - }) + }); }; /** @@ -138,7 +138,7 @@ SteamCommunity.prototype.openBoosterPack = function(appid, assetid, callback) { } callback(null, body.rgItems); - }) + }); }; /** diff --git a/components/profile.js b/components/profile.js index 793af24e..7f837c17 100644 --- a/components/profile.js +++ b/components/profile.js @@ -1,6 +1,5 @@ const Cheerio = require('cheerio'); const FS = require('fs'); -const SteamID = require('steamid'); const Helpers = require('./helpers.js'); const SteamCommunity = require('../index.js'); @@ -12,9 +11,9 @@ SteamCommunity.PrivacyState = { }; const CommentPrivacyState = { - '1': 2, // private - '2': 0, // friends only - '3': 1 // anyone + 1: 2, // private + 2: 0, // friends only + 3: 1 // anyone }; /** @@ -80,11 +79,7 @@ SteamCommunity.prototype.editProfile = function(settings, callback) { }; for (let i in settings) { - if(!settings.hasOwnProperty(i)) { - continue; - } - - switch(i) { + switch (i) { case 'name': values.personaName = settings[i]; break; @@ -193,10 +188,6 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) { let commentPermission = existingSettings.Privacy.eCommentPermission; for (let i in settings) { - if (!settings.hasOwnProperty(i)) { - continue; - } - switch (i) { case 'profile': privacy.PrivacyProfile = settings[i]; @@ -260,7 +251,7 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) { }; SteamCommunity.prototype.uploadAvatar = function(image, format, callback) { - if(typeof format === 'function') { + if (typeof format === 'function') { callback = format; format = null; } @@ -346,7 +337,7 @@ SteamCommunity.prototype.uploadAvatar = function(image, format, callback) { return; } - if(!body || !body.success) { + if (!body || !body.success) { callback && callback(new Error('Malformed response')); return; } @@ -394,7 +385,7 @@ SteamCommunity.prototype.uploadAvatar = function(image, format, callback) { } doUpload(file); - }) + }); } }; diff --git a/components/twofactor.js b/components/twofactor.js index 0bcbb86b..b6d5d7dc 100644 --- a/components/twofactor.js +++ b/components/twofactor.js @@ -86,7 +86,11 @@ SteamCommunity.prototype.finalizeTwoFactor = function(secret, activationCode, ca if (body.status == SteamCommunity.EResult.TwoFactorActivationCodeMismatch) { callback(new Error('Invalid activation code')); } else if (body.want_more) { - attemptsLeft--; + if (--attemptsLeft <= 0) { + // We made more than 30 attempts, something must be wrong + return callback(Helpers.eresultError(SteamCommunity.EResult.Fail)); + } + diff += 30; finalize(); @@ -96,7 +100,7 @@ SteamCommunity.prototype.finalizeTwoFactor = function(secret, activationCode, ca callback(null); } }, 'steamcommunity'); - } + }; SteamTotp.getTimeOffset((err, offset, latency) => { if (err) { diff --git a/components/users.js b/components/users.js index 464e9546..d8bc2f9c 100644 --- a/components/users.js +++ b/components/users.js @@ -241,7 +241,7 @@ SteamCommunity.prototype.getUserComments = function(userID, options, callback) { date: new Date($elem.find('.commentthread_comment_timestamp').data('timestamp') * 1000), text: $commentContent.text().trim(), html: $commentContent.html().trim() - } + }; }).get(); callback(null, comments, body.total_count); @@ -444,25 +444,16 @@ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, t return; } - let i; - for (i in body.rgInventory) { - if (!body.rgInventory.hasOwnProperty(i)) { - continue; - } - + for (let i in body.rgInventory) { inventory.push(new CEconItem(body.rgInventory[i], body.rgDescriptions, contextID)); } - for (i in body.rgCurrency) { - if (!body.rgCurrency.hasOwnProperty(i)) { - continue; - } - + for (let i in body.rgCurrency) { currency.push(new CEconItem(body.rgInventory[i], body.rgDescriptions, contextID)); } if (body.more) { - let match = response.request.uri.href.match(/\/(profiles|id)\/([^\/]+)\//); + let match = response.request.uri.href.match(/\/(profiles|id)\/([^/]+)\//); if (match) { endpoint = `/${match[1]}/${match[2]}`; } diff --git a/examples/edit-group-announcement.js b/examples/edit-group-announcement.js index 9fe8fd7a..4a20c1fd 100644 --- a/examples/edit-group-announcement.js +++ b/examples/edit-group-announcement.js @@ -1,47 +1,47 @@ -var SteamCommunity = require('../index.js'); -var ReadLine = require('readline'); +let SteamCommunity = require('../index.js'); +let ReadLine = require('readline'); -var community = new SteamCommunity(); -var rl = ReadLine.createInterface({ - "input": process.stdin, - "output": process.stdout +let community = new SteamCommunity(); +let rl = ReadLine.createInterface({ + input: process.stdin, + output: process.stdout }); -rl.question("Username: ", function(accountName) { - rl.question("Password: ", function(password) { +rl.question('Username: ', function(accountName) { + rl.question('Password: ', function(password) { doLogin(accountName, password); }); }); function doLogin(accountName, password, authCode, twoFactorCode, captcha) { community.login({ - "accountName": accountName, - "password": password, - "authCode": authCode, - "twoFactorCode": twoFactorCode, - "captcha": captcha + accountName: accountName, + password: password, + authCode: authCode, + twoFactorCode: twoFactorCode, + captcha: captcha }, function(err, sessionID, cookies, steamguard) { - if(err) { - if(err.message == 'SteamGuardMobile') { - rl.question("Steam Authenticator Code: ", function(code) { + if (err) { + if (err.message == 'SteamGuardMobile') { + rl.question('Steam Authenticator Code: ', function(code) { doLogin(accountName, password, null, code); }); return; } - if(err.message == 'SteamGuard') { - console.log("An email has been sent to your address at " + err.emaildomain); - rl.question("Steam Guard Code: ", function(code) { + if (err.message == 'SteamGuard') { + console.log('An email has been sent to your address at ' + err.emaildomain); + rl.question('Steam Guard Code: ', function(code) { doLogin(accountName, password, code); }); return; } - if(err.message == 'CAPTCHA') { + if (err.message == 'CAPTCHA') { console.log(err.captchaurl); - rl.question("CAPTCHA: ", function(captchaInput) { + rl.question('CAPTCHA: ', function(captchaInput) { doLogin(accountName, password, authCode, twoFactorCode, captchaInput); }); @@ -53,9 +53,9 @@ function doLogin(accountName, password, authCode, twoFactorCode, captcha) { return; } - console.log("Logged on!"); + console.log('Logged on!'); - rl.question("Group ID: ", function(gid) { + rl.question('Group ID: ', function(gid) { community.getSteamGroup(gid, function(err, group) { if (err) { console.log(err); @@ -64,19 +64,19 @@ function doLogin(accountName, password, authCode, twoFactorCode, captcha) { group.getAllAnnouncements(function(err, announcements) { - if(announcements.length === 0) { - return console.log("This group has no announcements"); + if (announcements.length === 0) { + return console.log('This group has no announcements'); } - for (var i = announcements.length - 1; i >= 0; i--) { - console.log("[%s] %s %s: %s", announcements[i].date, announcements[i].aid, announcements[i].author, announcements[i].content); - }; + for (let i = announcements.length - 1; i >= 0; i--) { + console.log('[%s] %s %s: %s', announcements[i].date, announcements[i].aid, announcements[i].author, announcements[i].content); + } - rl.question("Would you like to delete delete or edit an annoucement? (Type edit/delete): ", function(choice) { - rl.question("Annoucement ID: ", function(aid) { - if(choice === 'edit') { - rl.question("New title: ", function(header) { - rl.question("New body: ", function(content) { + rl.question('Would you like to delete delete or edit an annoucement? (Type edit/delete): ', function(choice) { + rl.question('Annoucement ID: ', function(aid) { + if (choice === 'edit') { + rl.question('New title: ', function(header) { + rl.question('New body: ', function(content) { // EW THE PYRAMID! // Try replace this with delete! editAnnouncement(group, aid, header, content); @@ -96,10 +96,10 @@ function doLogin(accountName, password, authCode, twoFactorCode, captcha) { function editAnnouncement(group, aid, header, content) { // Actual community method. group.editAnnouncement(aid, header, content, function(error) { - if(!error) { - console.log("Annoucement edited!"); + if (!error) { + console.log('Annoucement edited!'); } else { - console.log("Unable to edit annoucement! %j", error); + console.log('Unable to edit annoucement! %j', error); process.exit(1); } }); @@ -109,10 +109,10 @@ function deleteAnnouncement(group, aid) { // group.deleteAnnouncement(aid); // Or group.deleteAnnouncement(aid, function(err) { - if(!err) { - console.log("Deleted"); + if (!err) { + console.log('Deleted'); } else { - console.log("Error deleting announcement."); + console.log('Error deleting announcement.'); } - }) + }); } diff --git a/index.js b/index.js index 434c7435..e0e0dd3f 100644 --- a/index.js +++ b/index.js @@ -68,10 +68,8 @@ SteamCommunity.prototype.login = function(details, callback) { let disableMobile = details.disableMobile; - let self = this; - // Delete the cache - delete self._profileURL; + delete this._profileURL; // headers required to convince steam that we're logging in from a mobile device so that we can get the oAuth data let mobileHeaders = {}; @@ -89,11 +87,21 @@ SteamCommunity.prototype.login = function(details, callback) { mobileHeaders = {Referer: 'https://steamcommunity.com/login'}; } + const deleteMobileCookies = () => { + let cookie = Request.cookie('mobileClientVersion='); + cookie.expires = new Date(0); + this._setCookie(cookie); + + cookie = Request.cookie('mobileClient='); + cookie.expires = new Date(0); + this._setCookie(cookie); + }; + this.httpRequestPost('https://steamcommunity.com/login/getrsakey/', { form: {username: details.accountName}, headers: mobileHeaders, json: true - }, function(err, response, body) { + }, (err, response, body) => { // Remove the mobile cookies if (err) { deleteMobileCookies(); @@ -112,7 +120,7 @@ SteamCommunity.prototype.login = function(details, callback) { let formObj = { captcha_text: details.captcha || '', - captchagid: self._captchaGid, + captchagid: this._captchaGid, emailauth: details.authCode || '', emailsteamid: '', password: hex2b64(key.encrypt(details.password)), @@ -130,12 +138,12 @@ SteamCommunity.prototype.login = function(details, callback) { formObj.loginfriendlyname = '#login_emailauth_friendlyname_mobile'; } - self.httpRequestPost({ + this.httpRequestPost({ uri: 'https://steamcommunity.com/login/dologin/', json: true, form: formObj, headers: mobileHeaders - }, function(err, response, body) { + }, (err, response, body) => { deleteMobileCookies(); if (err) { @@ -157,7 +165,7 @@ SteamCommunity.prototype.login = function(details, callback) { error = new Error('CAPTCHA'); error.captchaurl = 'https://steamcommunity.com/login/rendercaptcha/?gid=' + body.captcha_gid; - self._captchaGid = body.captcha_gid; + this._captchaGid = body.captcha_gid; callback(error); } else if (!body.success) { @@ -167,68 +175,53 @@ SteamCommunity.prototype.login = function(details, callback) { } else { let sessionID = generateSessionID(); let oAuth; - self._setCookie(Request.cookie('sessionid=' + sessionID)); + this._setCookie(Request.cookie('sessionid=' + sessionID)); - let cookies = self._jar.getCookieString('https://steamcommunity.com').split(';').map(function(cookie) { - return cookie.trim(); - }); + let cookies = this._jar.getCookieString('https://steamcommunity.com').split(';').map(cookie => cookie.trim()); if (!disableMobile){ oAuth = JSON.parse(body.oauth); - self.steamID = new SteamID(oAuth.steamid); - self.oAuthToken = oAuth.oauth_token; + this.steamID = new SteamID(oAuth.steamid); + this.oAuthToken = oAuth.oauth_token; } else { for (let i = 0; i < cookies.length; i++) { let parts = cookies[i].split('='); if (parts[0] == 'steamLogin') { - self.steamID = new SteamID(decodeURIComponent(parts[1]).split('||')[0]); + this.steamID = new SteamID(decodeURIComponent(parts[1]).split('||')[0]); break; } } - self.oAuthToken = null; + this.oAuthToken = null; } // Find the Steam Guard cookie let steamguard = null; for (let i = 0; i < cookies.length; i++) { let parts = cookies[i].split('='); - if (parts[0] == 'steamMachineAuth' + self.steamID) { - steamguard = self.steamID.toString() + '||' + decodeURIComponent(parts[1]); + if (parts[0] == 'steamMachineAuth' + this.steamID) { + steamguard = this.steamID.toString() + '||' + decodeURIComponent(parts[1]); break; } } - self.setCookies(cookies); + this.setCookies(cookies); callback(null, sessionID, cookies, steamguard, disableMobile ? null : oAuth.oauth_token); } }, 'steamcommunity'); }, 'steamcommunity'); - - function deleteMobileCookies() { - let cookie = Request.cookie('mobileClientVersion='); - cookie.expires = new Date(0); - self._setCookie(cookie); - - cookie = Request.cookie('mobileClient='); - cookie.expires = new Date(0); - self._setCookie(cookie); - } }; SteamCommunity.prototype.oAuthLogin = function(steamguard, token, callback) { steamguard = steamguard.split('||'); let steamID = new SteamID(steamguard[0]); - let self = this; this.httpRequestPost({ uri: 'https://api.steampowered.com/IMobileAuthService/GetWGToken/v1/', - form: { - access_token: token - }, + form: {access_token: token}, json: true - }, function(err, response, body) { + }, (err, response, body) => { if (err) { callback(err); return; @@ -243,11 +236,11 @@ SteamCommunity.prototype.oAuthLogin = function(steamguard, token, callback) { 'steamLogin=' + encodeURIComponent(steamID.getSteamID64() + '||' + body.response.token), 'steamLoginSecure=' + encodeURIComponent(steamID.getSteamID64() + '||' + body.response.token_secure), 'steamMachineAuth' + steamID.getSteamID64() + '=' + steamguard[1], - 'sessionid=' + self.getSessionID() + 'sessionid=' + this.getSessionID() ]; - self.setCookies(cookies); - callback(null, self.getSessionID(), cookies); + this.setCookies(cookies); + callback(null, this.getSessionID(), cookies); }, 'steamcommunity'); }; @@ -324,8 +317,7 @@ function generateSessionID() { } SteamCommunity.prototype.parentalUnlock = function(pin, callback) { - let self = this; - let sessionID = self.getSessionID(); + let sessionID = this.getSessionID(); this.httpRequestPost('https://steamcommunity.com/parental/ajaxunlock', { json: true, @@ -333,7 +325,7 @@ SteamCommunity.prototype.parentalUnlock = function(pin, callback) { pin: pin, sessionid: sessionID } - }, function(err, response, body) { + }, (err, response, body) => { if (!callback) { return; } @@ -366,7 +358,7 @@ SteamCommunity.prototype.parentalUnlock = function(pin, callback) { } callback(); - }.bind(this), 'steamcommunity'); + }, 'steamcommunity'); }; SteamCommunity.prototype.getNotifications = function(callback) { @@ -493,12 +485,25 @@ SteamCommunity.prototype.clearPersonaNameHistory = function(callback) { }; SteamCommunity.prototype._myProfile = function(endpoint, form, callback) { - let self = this; + const completeRequest = (url) => { + let options = endpoint.endpoint ? endpoint : {}; + options.uri = 'https://steamcommunity.com' + url + '/' + (endpoint.endpoint || endpoint); + + if (form) { + options.method = 'POST'; + options.form = form; + options.followAllRedirects = true; + } else if (!options.method) { + options.method = 'GET'; + } + + this.httpRequest(options, callback, 'steamcommunity'); + }; if (this._profileURL) { completeRequest(this._profileURL); } else { - this.httpRequest('https://steamcommunity.com/my', {followRedirect: false}, function(err, response, body) { + this.httpRequest('https://steamcommunity.com/my', {followRedirect: false}, (err, response, body) => { if (err || response.statusCode != 302) { callback(err || 'HTTP error ' + response.statusCode); return; @@ -510,29 +515,14 @@ SteamCommunity.prototype._myProfile = function(endpoint, form, callback) { return; } - self._profileURL = match[1]; - setTimeout(function () { - delete self._profileURL; // delete the cache + this._profileURL = match[1]; + setTimeout(() => { + delete this._profileURL; // delete the cache }, 60000).unref(); completeRequest(match[1]); }, 'steamcommunity'); } - - function completeRequest(url) { - let options = endpoint.endpoint ? endpoint : {}; - options.uri = 'https://steamcommunity.com' + url + '/' + (endpoint.endpoint || endpoint); - - if (form) { - options.method = 'POST'; - options.form = form; - options.followAllRedirects = true; - } else if (!options.method) { - options.method = 'GET'; - } - - self.httpRequest(options, callback, 'steamcommunity'); - } }; /** From 9ec329569f05788413010d79893ca627082a6e71 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 03:05:54 -0400 Subject: [PATCH 23/68] Added eslint action --- .github/workflows/eslint.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/eslint.yml diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml new file mode 100644 index 00000000..7721e8c7 --- /dev/null +++ b/.github/workflows/eslint.yml @@ -0,0 +1,32 @@ +name: ESLint + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [8.x] + + steps: + - uses: actions/checkout@v1 + with: + fetch-depth: 1 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + - name: npm install + working-directory: . + run: npm install --ignore-scripts + + - name: Run ESLint + uses: ninosaurus/eslint-check@v5 + with: + eslint-config-path: "./.eslintrc.js" + custom-directory: "." + repo-token: ${{secrets.GITHUB_TOKEN}} From 8c0994cec31af390e3161152bbff425d2a3e428b Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 03:09:20 -0400 Subject: [PATCH 24/68] Fixed eslint action --- .github/workflows/eslint.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 7721e8c7..0f8fbfbc 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -25,8 +25,4 @@ jobs: run: npm install --ignore-scripts - name: Run ESLint - uses: ninosaurus/eslint-check@v5 - with: - eslint-config-path: "./.eslintrc.js" - custom-directory: "." - repo-token: ${{secrets.GITHUB_TOKEN}} + run: eslint . --ext .js,.jsx,.ts.,tsx From ca887c46bac7699c91e8b8b2ad475f3be65385c2 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 03:10:06 -0400 Subject: [PATCH 25/68] Use npx for eslint --- .github/workflows/eslint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 0f8fbfbc..48981405 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -25,4 +25,4 @@ jobs: run: npm install --ignore-scripts - name: Run ESLint - run: eslint . --ext .js,.jsx,.ts.,tsx + run: npx eslint . --ext .js,.jsx,.ts.,tsx From 59df4dee1d4bfd574ece005a61f06ae73bcef96b Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 03:11:03 -0400 Subject: [PATCH 26/68] eslint requires node 10 --- .github/workflows/eslint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 48981405..5cfdb170 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: - node-version: [8.x] + node-version: [10.x] steps: - uses: actions/checkout@v1 From a4de664759083eecd43bcac4c9c10fdd878467e2 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 03:14:55 -0400 Subject: [PATCH 27/68] Disable eslint on generated files --- package.json | 3 +++ resources/EConfirmationType.js | 2 ++ resources/EFriendRelationship.js | 4 +++- resources/EPersonaState.js | 2 ++ resources/EPersonaStateFlag.js | 2 ++ resources/EResult.js | 2 ++ 6 files changed, 14 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index b483c1ea..ad9a878a 100644 --- a/package.json +++ b/package.json @@ -36,5 +36,8 @@ }, "devDependencies": { "eslint": "^7.31.0" + }, + "scripts": { + "lint": "npx eslint . --ext .js,.jsx,.ts,.tsx" } } diff --git a/resources/EConfirmationType.js b/resources/EConfirmationType.js index 3c924749..ed56667e 100644 --- a/resources/EConfirmationType.js +++ b/resources/EConfirmationType.js @@ -1,3 +1,5 @@ +/* eslint-disable */ + /** * @enum EConfirmationType */ diff --git a/resources/EFriendRelationship.js b/resources/EFriendRelationship.js index ed9ef7a4..f5a5b44a 100644 --- a/resources/EFriendRelationship.js +++ b/resources/EFriendRelationship.js @@ -1,7 +1,9 @@ +/* eslint-disable */ + /** * @enum EFriendRelationship */ - module.exports = { +module.exports = { "None": 0, "Blocked": 1, "RequestRecipient": 2, diff --git a/resources/EPersonaState.js b/resources/EPersonaState.js index d09c90d0..07eabcf7 100644 --- a/resources/EPersonaState.js +++ b/resources/EPersonaState.js @@ -1,3 +1,5 @@ +/* eslint-disable */ + /** * @enum EPersonaState */ diff --git a/resources/EPersonaStateFlag.js b/resources/EPersonaStateFlag.js index eb1f5b1d..7812c32f 100644 --- a/resources/EPersonaStateFlag.js +++ b/resources/EPersonaStateFlag.js @@ -1,3 +1,5 @@ +/* eslint-disable */ + /** * @enum EPersonaStateFlag */ diff --git a/resources/EResult.js b/resources/EResult.js index 6513772d..5824cc83 100644 --- a/resources/EResult.js +++ b/resources/EResult.js @@ -1,3 +1,5 @@ +/* eslint-disable */ + /** * @enum EResult */ From e252efc455f5140495b0b590fc3c8096f8c3bdb5 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 03:15:38 -0400 Subject: [PATCH 28/68] Change all eslint rules to errors so they'll hit on CI --- .eslintrc.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 219c4280..89c5eb34 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -12,17 +12,17 @@ module.exports = { // Use tabs for indentation and require 'case' in switch to be indented 1 level (default 0) indent: ['error', 'tab', {SwitchCase: 1}], // Single quotes for strings - quotes: ['warn', 'single'], + quotes: ['error', 'single'], // Always require semicolons semi: ['error', 'always'], // Don't use 'var' - 'no-var': 'warn', + 'no-var': 'error', // Only use quotes in object literal keys as needed - 'quote-props': ['warn', 'as-needed'], + 'quote-props': ['error', 'as-needed'], // Don't allow trailing spaces after a line - 'no-trailing-spaces': 'warn', + 'no-trailing-spaces': 'error', // Require spaces before and after keywords (like "if") - 'keyword-spacing': 'warn', + 'keyword-spacing': 'error', // Don't allow unused variables, but allow unused function args (e.g. in callbacks) and global vars 'no-unused-vars': ['error', {vars: 'local', args: 'none'}] } From 40ee35ac89960ef568f109b3bc075920823ff854 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 03:15:47 -0400 Subject: [PATCH 29/68] Fixed last eslint error --- components/users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/users.js b/components/users.js index d8bc2f9c..357bcf49 100644 --- a/components/users.js +++ b/components/users.js @@ -394,7 +394,7 @@ SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) { let data; try { data = JSON.parse(match[1]); - } catch(e) { + } catch (e) { callback(new Error('Malformed response')); return; } From 9d47d28a1e5bd0267b876f57146171abb4a441a6 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 03:20:14 -0400 Subject: [PATCH 30/68] Some more refactoring --- classes/CEconItem.js | 16 +++++++--------- classes/CMarketItem.js | 14 ++++++-------- classes/CMarketSearchResult.js | 2 +- classes/CSteamGroup.js | 2 +- classes/CSteamUser.js | 2 +- components/groups.js | 2 ++ 6 files changed, 18 insertions(+), 20 deletions(-) diff --git a/classes/CEconItem.js b/classes/CEconItem.js index 49eebd50..5d7dd9ed 100644 --- a/classes/CEconItem.js +++ b/classes/CEconItem.js @@ -44,15 +44,13 @@ function CEconItem(item, description, contextID) { // Restore old property names of tags if (this.tags) { - this.tags = this.tags.map(function(tag) { - return { - internal_name: tag.internal_name, - name: tag.localized_tag_name || tag.name, - category: tag.category, - color: tag.color || '', - category_name: tag.localized_category_name || tag.category_name - }; - }); + this.tags = this.tags.map((tag) => ({ + internal_name: tag.internal_name, + name: tag.localized_tag_name || tag.name, + category: tag.category, + color: tag.color || '', + category_name: tag.localized_category_name || tag.category_name + })); } // Restore market_fee_app, if applicable diff --git a/classes/CMarketItem.js b/classes/CMarketItem.js index c489cb45..ba75d146 100644 --- a/classes/CMarketItem.js +++ b/classes/CMarketItem.js @@ -22,7 +22,7 @@ SteamCommunity.prototype.getMarketItem = function(appid, hashName, currency, cal } let item = new CMarketItem(appid, hashName, this, body, $); - item.updatePrice(currency, function(err) { + item.updatePrice(currency, (err) => { if (err) { callback(err); } else { @@ -62,13 +62,11 @@ function CMarketItem(appid, hashName, community, body, $) { if (match) { try { this.medianSalePrices = JSON.parse(match[1]); - this.medianSalePrices = this.medianSalePrices.map(function(item) { - return { - hour: new Date(item[0]), - price: item[1], - quantity: parseInt(item[2], 10) - }; - }); + this.medianSalePrices = this.medianSalePrices.map((item) => ({ + hour: new Date(item[0]), + price: item[1], + quantity: parseInt(item[2], 10) + })); } catch (e) { // ignore } diff --git a/classes/CMarketSearchResult.js b/classes/CMarketSearchResult.js index 3affa66b..d0f9e804 100644 --- a/classes/CMarketSearchResult.js +++ b/classes/CMarketSearchResult.js @@ -38,7 +38,7 @@ SteamCommunity.prototype.marketSearch = function(options, callback) { referer: 'https://steamcommunity.com/market/search' }, json: true - }, function(err, response, body) { + }, (err, response, body) => { if (err) { callback(err); return; diff --git a/classes/CSteamGroup.js b/classes/CSteamGroup.js index b1a3139e..6d8d74b1 100644 --- a/classes/CSteamGroup.js +++ b/classes/CSteamGroup.js @@ -19,7 +19,7 @@ SteamCommunity.prototype.getSteamGroup = function(id, callback) { return; } - XML2JS.parseString(body, function(err, result) { + XML2JS.parseString(body, (err, result) => { if (err) { callback(err); return; diff --git a/classes/CSteamUser.js b/classes/CSteamUser.js index 5ba68d30..195f7a3a 100644 --- a/classes/CSteamUser.js +++ b/classes/CSteamUser.js @@ -19,7 +19,7 @@ SteamCommunity.prototype.getSteamUser = function(id, callback) { return; } - XML2JS.parseString(body, function(err, result) { + XML2JS.parseString(body, (err, result) => { if (err || (!result.response && !result.profile)) { callback(err || new Error('No valid response')); return; diff --git a/components/groups.js b/components/groups.js index 40d4f045..74dcf560 100644 --- a/components/groups.js +++ b/components/groups.js @@ -552,6 +552,8 @@ SteamCommunity.prototype.getAllGroupComments = function(gid, from, count, callba let $ = Cheerio.load(JSON.parse(body).comments_html); + // !! DO NOT replace this function with an arrow function. We depend on the `this` context inside the function, + // which is the comment element. $('.commentthread_comment_content').each(function() { let comment = {}; From 89d058fc990ff47cd08ae2dfb9ca404b5a6b7899 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 04:54:27 -0400 Subject: [PATCH 31/68] Update EResult.js --- resources/EResult.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/EResult.js b/resources/EResult.js index 5824cc83..8b3390fe 100644 --- a/resources/EResult.js +++ b/resources/EResult.js @@ -1,4 +1,5 @@ /* eslint-disable */ +// Auto-generated by generate-enums script on Thu Jul 29 2021 04:43:52 GMT-0400 (Eastern Daylight Time) /** * @enum EResult @@ -131,7 +132,8 @@ module.exports = { "DeniedDueToCommunityCooldown": 116, "NoLauncherSpecified": 117, "MustAgreeToSSA": 118, - "ClientNoLongerSupported": 119, + "ClientNoLongerSupported": 119, // obsolete + "LauncherMigrated": 119, // Value-to-name mapping for convenience "0": "Invalid", @@ -252,5 +254,5 @@ module.exports = { "116": "DeniedDueToCommunityCooldown", "117": "NoLauncherSpecified", "118": "MustAgreeToSSA", - "119": "ClientNoLongerSupported", + "119": "LauncherMigrated", }; From 605fb6268f3784c7a7e7198ce85de446c17041cc Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 05:03:07 -0400 Subject: [PATCH 32/68] Skip eslint workflow for internal pull requests Refs: - https://github.community/t/duplicate-checks-on-push-and-pull-request-simultaneous-event/18012/4 - https://github.com/Dart-Code/Dart-Code/commit/612732d5879730608baa9622bf7f5e5b7b51ae65 --- .github/workflows/eslint.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 5cfdb170..9b65324d 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -6,6 +6,9 @@ jobs: lint: runs-on: ubuntu-latest + # Run for external PRs, but not on our own internal PRs as they'll be run by the push to the branch. + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'DoctorMcKay/node-steamcommunity' + strategy: matrix: node-version: [10.x] From 3def387d56e041b015df43d8ddeeaeb878633842 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 05:06:01 -0400 Subject: [PATCH 33/68] Updated @doctormckay/stdlib --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ad9a878a..43b15f73 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "url": "https://github.com/DoctorMcKay/node-steamcommunity.git" }, "dependencies": { - "@doctormckay/stdlib": "^1.10.0", + "@doctormckay/stdlib": "^1.14.1", "cheerio": "0.22.0", "image-size": "^0.8.2", "node-bignumber": "^1.2.1", From 485529e8074a66ae6bbca3cb1ce84c29c42f7435 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 05:06:21 -0400 Subject: [PATCH 34/68] Use npm run lint in workflow --- .github/workflows/eslint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 9b65324d..4e6734d0 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -28,4 +28,4 @@ jobs: run: npm install --ignore-scripts - name: Run ESLint - run: npx eslint . --ext .js,.jsx,.ts.,tsx + run: npm run lint From dfda22cee4b4569846881d33936a5219ab7c7a96 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 05:20:34 -0400 Subject: [PATCH 35/68] Rename ConfirmationType to EConfirmationType for consistency --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index e0e0dd3f..89c18e52 100644 --- a/index.js +++ b/index.js @@ -14,7 +14,7 @@ Util.inherits(SteamCommunity, EventEmitter); module.exports = SteamCommunity; SteamCommunity.SteamID = SteamID; -SteamCommunity.ConfirmationType = require('./resources/EConfirmationType.js'); +SteamCommunity.EConfirmationType = require('./resources/EConfirmationType.js'); SteamCommunity.EResult = require('./resources/EResult.js'); SteamCommunity.EFriendRelationship = require('./resources/EFriendRelationship.js'); From e9d3221ee60c833358dde95f5b1916db8f876495 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 05:47:24 -0400 Subject: [PATCH 36/68] Start replacing request with got --- index.js | 79 ++++++++++++++++++++++++++-------------------------- package.json | 2 ++ 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/index.js b/index.js index 89c18e52..5837fcc9 100644 --- a/index.js +++ b/index.js @@ -1,13 +1,15 @@ const {EventEmitter} = require('events'); +const Got = require('got'); const {hex2b64} = require('node-bignumber'); const Request = require('request'); const {Key: RSA} = require('node-bignumber'); const SteamID = require('steamid'); +const {CookieJar} = require('tough-cookie'); const Util = require('util'); const Helpers = require('./components/helpers.js'); -const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'; +const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36'; Util.inherits(SteamCommunity, EventEmitter); @@ -22,7 +24,7 @@ SteamCommunity.EFriendRelationship = require('./resources/EFriendRelationship.js function SteamCommunity(options) { options = options || {}; - this._jar = Request.jar(); + this._jar = new CookieJar(); this._captchaGid = -1; this._httpRequestID = 0; @@ -35,11 +37,6 @@ function SteamCommunity(options) { } }; - if (typeof options == 'string') { - options = { - localAddress: options - }; - } this._options = options; if (options.localAddress) { @@ -50,10 +47,10 @@ function SteamCommunity(options) { this.request = this.request.defaults(defaults); // English - this._setCookie(Request.cookie('Steam_Language=english')); + this._setCookie('Steam_Language=english'); // UTC - this._setCookie(Request.cookie('timezoneOffset=0,0')); + this._setCookie('timezoneOffset=0,0'); } SteamCommunity.prototype.login = function(details, callback) { @@ -63,7 +60,7 @@ SteamCommunity.prototype.login = function(details, callback) { if (details.steamguard) { let parts = details.steamguard.split('||'); - this._setCookie(Request.cookie('steamMachineAuth' + parts[0] + '=' + encodeURIComponent(parts[1])), true); + this._setCookie(`steamMachineAuth${parts[0]}=${encodeURIComponent(parts[1])}`, true); } let disableMobile = details.disableMobile; @@ -81,20 +78,15 @@ SteamCommunity.prototype.login = function(details, callback) { Accept: 'text/javascript, text/html, application/xml, text/xml, */*' }; - this._setCookie(Request.cookie('mobileClientVersion=0 (2.1.3)')); - this._setCookie(Request.cookie('mobileClient=android')); + this._setCookie('mobileClientVersion=0 (2.1.3)'); + this._setCookie('mobileClient=android'); } else { mobileHeaders = {Referer: 'https://steamcommunity.com/login'}; } const deleteMobileCookies = () => { - let cookie = Request.cookie('mobileClientVersion='); - cookie.expires = new Date(0); - this._setCookie(cookie); - - cookie = Request.cookie('mobileClient='); - cookie.expires = new Date(0); - this._setCookie(cookie); + this._setCookie('mobileClientVersion=; max-age=0'); + this._setCookie('mobileClient=; max-age=0'); }; this.httpRequestPost('https://steamcommunity.com/login/getrsakey/', { @@ -173,11 +165,10 @@ SteamCommunity.prototype.login = function(details, callback) { } else if (!disableMobile && !body.oauth) { callback(new Error('Malformed response')); } else { - let sessionID = generateSessionID(); + let sessionID = this.getSessionID(); let oAuth; - this._setCookie(Request.cookie('sessionid=' + sessionID)); - let cookies = this._jar.getCookieString('https://steamcommunity.com').split(';').map(cookie => cookie.trim()); + let cookies = this._jar.getCookieStringSync('https://steamcommunity.com').split(';').map(cookie => cookie.trim()); if (!disableMobile){ oAuth = JSON.parse(body.oauth); @@ -205,6 +196,7 @@ SteamCommunity.prototype.login = function(details, callback) { } } + // Call setCookies to propagate our cookies to the other domains this.setCookies(cookies); callback(null, sessionID, cookies, steamguard, disableMobile ? null : oAuth.oauth_token); @@ -278,44 +270,51 @@ SteamCommunity.prototype.getClientLogonToken = function(callback) { }); }; +/** + * Sets a single cookie in our cookie jar. + * @param {string} cookie + * @param {boolean} [secure=false] + * @private + */ SteamCommunity.prototype._setCookie = function(cookie, secure) { let protocol = secure ? 'https' : 'http'; - cookie.secure = !!secure; - this._jar.setCookie(cookie.clone(), protocol + '://steamcommunity.com'); - this._jar.setCookie(cookie.clone(), protocol + '://store.steampowered.com'); - this._jar.setCookie(cookie.clone(), protocol + '://help.steampowered.com'); + this._jar.setCookieSync(cookie, `${protocol}://steamcommunity.com`); + this._jar.setCookieSync(cookie, `${protocol}://store.steampowered.com`); + this._jar.setCookieSync(cookie, `${protocol}://help.steampowered.com`); }; +/** + * Set one or more cookies in this SteamCommunity's cookie jar. + * @param {string|string[]} cookies + */ SteamCommunity.prototype.setCookies = function(cookies) { + if (!Array.isArray(cookies)) { + cookies = [cookies]; + } + cookies.forEach((cookie) => { let cookieName = cookie.match(/(.+)=/)[1]; if (cookieName == 'steamLogin' || cookieName == 'steamLoginSecure') { this.steamID = new SteamID(cookie.match(/=(\d+)/)[1]); } - this._setCookie(Request.cookie(cookie), !!(cookieName.match(/^steamMachineAuth/) || cookieName.match(/Secure$/))); + this._setCookie(cookie, !!(cookieName.match(/^steamMachineAuth/) || cookieName.match(/Secure$/))); }); }; -SteamCommunity.prototype.getSessionID = function(host = 'http://steamcommunity.com') { - let cookies = this._jar.getCookieString(host).split(';'); - for (let i = 0; i < cookies.length; i++) { - let match = cookies[i].trim().match(/([^=]+)=(.+)/); - if (match[1] == 'sessionid') { - return decodeURIComponent(match[2]); - } +SteamCommunity.prototype.getSessionID = function() { + let sessionIdCookie = this._jar.getCookiesSync('http://steamcommunity.com').find(cookie => cookie.key == 'sessionid'); + if (sessionIdCookie) { + return sessionIdCookie.value; } - let sessionID = generateSessionID(); - this._setCookie(Request.cookie('sessionid=' + sessionID)); + // Generate a new session id + let sessionID = require('crypto').randomBytes(12).toString('hex'); + this._setCookie(`sessionid=${sessionID}`); return sessionID; }; -function generateSessionID() { - return require('crypto').randomBytes(12).toString('hex'); -} - SteamCommunity.prototype.parentalUnlock = function(pin, callback) { let sessionID = this.getSessionID(); diff --git a/package.json b/package.json index 43b15f73..7a53745f 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,13 @@ "dependencies": { "@doctormckay/stdlib": "^1.14.1", "cheerio": "0.22.0", + "got": "^11.8.2", "image-size": "^0.8.2", "node-bignumber": "^1.2.1", "request": "^2.88.0", "steam-totp": "^2.1.0", "steamid": "^1.1.3", + "tough-cookie": "^4.0.0", "xml2js": "^0.4.22" }, "engines": { From ac19e0867b28532003a33ef5d283d45481eedc6c Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 05:55:12 -0400 Subject: [PATCH 37/68] Start adding callback promises --- index.js | 252 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 129 insertions(+), 123 deletions(-) diff --git a/index.js b/index.js index 5837fcc9..e72f089f 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,7 @@ const Got = require('got'); const {hex2b64} = require('node-bignumber'); const Request = require('request'); const {Key: RSA} = require('node-bignumber'); +const StdLib = require('@doctormckay/stdlib'); const SteamID = require('steamid'); const {CookieJar} = require('tough-cookie'); const Util = require('util'); @@ -58,151 +59,156 @@ SteamCommunity.prototype.login = function(details, callback) { throw new Error('Missing either accountName or password to login; both are needed'); } - if (details.steamguard) { - let parts = details.steamguard.split('||'); - this._setCookie(`steamMachineAuth${parts[0]}=${encodeURIComponent(parts[1])}`, true); - } - - let disableMobile = details.disableMobile; - - // Delete the cache - delete this._profileURL; - - // headers required to convince steam that we're logging in from a mobile device so that we can get the oAuth data - let mobileHeaders = {}; - if (!disableMobile) { - mobileHeaders = { - 'X-Requested-With': 'com.valvesoftware.android.steam.community', - Referer: 'https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client', - 'User-Agent': this._options.mobileUserAgent || details.mobileUserAgent || 'Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', - Accept: 'text/javascript, text/html, application/xml, text/xml, */*' - }; + let callbackArgs = ['sessionID', 'cookies', 'steamguard', 'oauthToken']; + return StdLib.Promises.callbackPromise(callbackArgs, callback, false, (resolve, reject) => { + if (details.steamguard) { + let parts = details.steamguard.split('||'); + this._setCookie(`steamMachineAuth${parts[0]}=${encodeURIComponent(parts[1])}`, true); + } - this._setCookie('mobileClientVersion=0 (2.1.3)'); - this._setCookie('mobileClient=android'); - } else { - mobileHeaders = {Referer: 'https://steamcommunity.com/login'}; - } + let disableMobile = details.disableMobile; - const deleteMobileCookies = () => { - this._setCookie('mobileClientVersion=; max-age=0'); - this._setCookie('mobileClient=; max-age=0'); - }; + // Delete the cache + delete this._profileURL; - this.httpRequestPost('https://steamcommunity.com/login/getrsakey/', { - form: {username: details.accountName}, - headers: mobileHeaders, - json: true - }, (err, response, body) => { - // Remove the mobile cookies - if (err) { - deleteMobileCookies(); - callback(err); - return; - } + // headers required to convince steam that we're logging in from a mobile device so that we can get the oAuth data + let mobileHeaders = {}; + if (!disableMobile) { + mobileHeaders = { + 'X-Requested-With': 'com.valvesoftware.android.steam.community', + Referer: 'https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client', + 'User-Agent': this._options.mobileUserAgent || details.mobileUserAgent || 'Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', + Accept: 'text/javascript, text/html, application/xml, text/xml, */*' + }; - if (!body.publickey_mod || !body.publickey_exp) { - deleteMobileCookies(); - callback(new Error('Invalid RSA key received')); - return; + this._setCookie('mobileClientVersion=0 (2.1.3)'); + this._setCookie('mobileClient=android'); + } else { + mobileHeaders = {Referer: 'https://steamcommunity.com/login'}; } - let key = new RSA(); - key.setPublic(body.publickey_mod, body.publickey_exp); - - let formObj = { - captcha_text: details.captcha || '', - captchagid: this._captchaGid, - emailauth: details.authCode || '', - emailsteamid: '', - password: hex2b64(key.encrypt(details.password)), - remember_login: 'true', - rsatimestamp: body.timestamp, - twofactorcode: details.twoFactorCode || '', - username: details.accountName, - loginfriendlyname: '', - donotcache: Date.now() + const deleteMobileCookies = () => { + this._setCookie('mobileClientVersion=; max-age=0'); + this._setCookie('mobileClient=; max-age=0'); }; - if (!disableMobile){ - formObj.oauth_client_id = 'DE45CD61'; - formObj.oauth_scope = 'read_profile write_profile read_client write_client'; - formObj.loginfriendlyname = '#login_emailauth_friendlyname_mobile'; - } - - this.httpRequestPost({ - uri: 'https://steamcommunity.com/login/dologin/', - json: true, - form: formObj, - headers: mobileHeaders + this.httpRequestPost('https://steamcommunity.com/login/getrsakey/', { + form: {username: details.accountName}, + headers: mobileHeaders, + json: true }, (err, response, body) => { - deleteMobileCookies(); - + // Remove the mobile cookies if (err) { - callback(err); - return; + deleteMobileCookies(); + return reject(err); + } + + if (!body.publickey_mod || !body.publickey_exp) { + deleteMobileCookies(); + return reject(new Error('Invalid RSA key received')); + } + + let key = new RSA(); + key.setPublic(body.publickey_mod, body.publickey_exp); + + let formObj = { + captcha_text: details.captcha || '', + captchagid: this._captchaGid, + emailauth: details.authCode || '', + emailsteamid: '', + password: hex2b64(key.encrypt(details.password)), + remember_login: 'true', + rsatimestamp: body.timestamp, + twofactorcode: details.twoFactorCode || '', + username: details.accountName, + loginfriendlyname: '', + donotcache: Date.now() + }; + + if (!disableMobile) { + formObj.oauth_client_id = 'DE45CD61'; + formObj.oauth_scope = 'read_profile write_profile read_client write_client'; + formObj.loginfriendlyname = '#login_emailauth_friendlyname_mobile'; } - let error; - if (!body.success && body.emailauth_needed) { - // Steam Guard (email) - error = new Error('SteamGuard'); - error.emaildomain = body.emaildomain; - - callback(error); - } else if (!body.success && body.requires_twofactor) { - // Steam Guard (app) - callback(new Error('SteamGuardMobile')); - } else if (!body.success && body.captcha_needed && body.message.match(/Please verify your humanity/)) { - error = new Error('CAPTCHA'); - error.captchaurl = 'https://steamcommunity.com/login/rendercaptcha/?gid=' + body.captcha_gid; - - this._captchaGid = body.captcha_gid; - - callback(error); - } else if (!body.success) { - callback(new Error(body.message || 'Unknown error')); - } else if (!disableMobile && !body.oauth) { - callback(new Error('Malformed response')); - } else { - let sessionID = this.getSessionID(); - let oAuth; - - let cookies = this._jar.getCookieStringSync('https://steamcommunity.com').split(';').map(cookie => cookie.trim()); - - if (!disableMobile){ - oAuth = JSON.parse(body.oauth); - this.steamID = new SteamID(oAuth.steamid); - this.oAuthToken = oAuth.oauth_token; + this.httpRequestPost({ + uri: 'https://steamcommunity.com/login/dologin/', + json: true, + form: formObj, + headers: mobileHeaders + }, (err, response, body) => { + deleteMobileCookies(); + + if (err) { + return reject(err); + } + + let error; + if (!body.success && body.emailauth_needed) { + // Steam Guard (email) + error = new Error('SteamGuard'); + error.emaildomain = body.emaildomain; + + return reject(error); + } else if (!body.success && body.requires_twofactor) { + // Steam Guard (app) + return reject(new Error('SteamGuardMobile')); + } else if (!body.success && body.captcha_needed && body.message.match(/Please verify your humanity/)) { + error = new Error('CAPTCHA'); + error.captchaurl = 'https://steamcommunity.com/login/rendercaptcha/?gid=' + body.captcha_gid; + + this._captchaGid = body.captcha_gid; + + return reject(error); + } else if (!body.success) { + return reject(new Error(body.message || 'Unknown error')); + } else if (!disableMobile && !body.oauth) { + return reject(new Error('Malformed response')); } else { + let sessionID = this.getSessionID(); + let oAuth; + + let cookies = this._jar.getCookieStringSync('https://steamcommunity.com').split(';').map(cookie => cookie.trim()); + + if (!disableMobile) { + oAuth = JSON.parse(body.oauth); + this.steamID = new SteamID(oAuth.steamid); + this.oAuthToken = oAuth.oauth_token; + } else { + for (let i = 0; i < cookies.length; i++) { + let parts = cookies[i].split('='); + if (parts[0] == 'steamLogin') { + this.steamID = new SteamID(decodeURIComponent(parts[1]).split('||')[0]); + break; + } + } + + this.oAuthToken = null; + } + + // Find the Steam Guard cookie + let steamguard = null; for (let i = 0; i < cookies.length; i++) { let parts = cookies[i].split('='); - if (parts[0] == 'steamLogin') { - this.steamID = new SteamID(decodeURIComponent(parts[1]).split('||')[0]); + if (parts[0] == 'steamMachineAuth' + this.steamID) { + steamguard = this.steamID.toString() + '||' + decodeURIComponent(parts[1]); break; } } - this.oAuthToken = null; - } + // Call setCookies to propagate our cookies to the other domains + this.setCookies(cookies); - // Find the Steam Guard cookie - let steamguard = null; - for (let i = 0; i < cookies.length; i++) { - let parts = cookies[i].split('='); - if (parts[0] == 'steamMachineAuth' + this.steamID) { - steamguard = this.steamID.toString() + '||' + decodeURIComponent(parts[1]); - break; - } + return resolve({ + sessionID, + cookies, + steamguard, + oauthToken: disableMobile ? null : oAuth.oauth_token + }); } - - // Call setCookies to propagate our cookies to the other domains - this.setCookies(cookies); - - callback(null, sessionID, cookies, steamguard, disableMobile ? null : oAuth.oauth_token); - } + }, 'steamcommunity'); }, 'steamcommunity'); - }, 'steamcommunity'); + }); }; SteamCommunity.prototype.oAuthLogin = function(steamguard, token, callback) { From 7c6e531dbde1eb4bdb2649c730ffce66f157219a Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 21:52:28 -0400 Subject: [PATCH 38/68] Added rationale for not using eqeqeq --- .eslintrc.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.eslintrc.js b/.eslintrc.js index 89c5eb34..25b4bbf4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -25,5 +25,22 @@ module.exports = { 'keyword-spacing': 'error', // Don't allow unused variables, but allow unused function args (e.g. in callbacks) and global vars 'no-unused-vars': ['error', {vars: 'local', args: 'none'}] + + // We will NOT be using eqeqeq for a few reasons: + // 1. I would have to go through and check every single `==` to make sure that it's not depending on loose equality checks. + // 2. I'm only using ESLint to enforce style, not actual differences in functionality. ==/=== is not merely a style choice. + // Yes, I know that 'no-var' is actually enforcing a difference in functionality, but in practice nobody uses + // (or even knows about) var's hoisting functionality, so at this point it's effectively a style choice. + // 3. A lot of the time, you actually *want* loose equality checks, especially when interacting with a web server + // (as HTTP as no concept of anything but strings). Yes, most of our interaction is JSON, but not all. And even then, + // not all JSON actually serializes numbers as numbers. + // 4. `==` is really nowhere near as dangerous as memes would lead you to believe, if you know what you're doing. + // 5. If the idea behind enforcing `===` is to prevent inexperienced developers from unwittingly introducing bugs + // via loose quality checks, in my opinion it could be just as harmful to instruct a code quality tool to + // naively demand that all `==` become `===`. If a developer were to build code that works, but upon opening + // a pull request they see that ESLint demands they use `===` instead, they might just click "fix" and resubmit, + // expecting the code quality tool to know what it's doing. But it *doesn't* know what it's doing, since it's + // just blindly alerting when it sees `==`. The change in functionality from `==` to `===` could very well + // introduce a bug by itself. } }; From 565132d1bce36ae0b36ce3fa2d2c393a38b2fa56 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 22:03:14 -0400 Subject: [PATCH 39/68] Enforce dot-notation --- .eslintrc.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 25b4bbf4..fde2d930 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -24,7 +24,9 @@ module.exports = { // Require spaces before and after keywords (like "if") 'keyword-spacing': 'error', // Don't allow unused variables, but allow unused function args (e.g. in callbacks) and global vars - 'no-unused-vars': ['error', {vars: 'local', args: 'none'}] + 'no-unused-vars': ['error', {vars: 'local', args: 'none'}], + // Require using dot notation (obj.prop instead of obj['prop']) where possible + 'dot-notation': 'error' // We will NOT be using eqeqeq for a few reasons: // 1. I would have to go through and check every single `==` to make sure that it's not depending on loose equality checks. From e49c5c175ecc36f6972ce5264b5033e300b03287 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 22:04:20 -0400 Subject: [PATCH 40/68] Fix dot-notation violation --- classes/CSteamUser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/CSteamUser.js b/classes/CSteamUser.js index 195f7a3a..c82abcf1 100644 --- a/classes/CSteamUser.js +++ b/classes/CSteamUser.js @@ -93,7 +93,7 @@ function CSteamUser(community, userData, customurl) { if (userData.groups && userData.groups[0] && userData.groups[0].group) { this.groups = userData.groups[0].group.map((group) => { - if (group['$'] && group['$'].isPrimary === '1') { + if (group.$ && group.$.isPrimary === '1') { this.primaryGroup = new SteamID(group.groupID64[0]); } From e2ef03cc3265c0e0e1d5cf2799eb98c1503ac903 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 22:09:06 -0400 Subject: [PATCH 41/68] Disallow spaces before anonymous/named function parens --- .eslintrc.js | 4 +++- classes/CMarketItem.js | 4 ++-- classes/CSteamGroup.js | 2 +- components/market.js | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index fde2d930..5b9db8a9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -26,7 +26,9 @@ module.exports = { // Don't allow unused variables, but allow unused function args (e.g. in callbacks) and global vars 'no-unused-vars': ['error', {vars: 'local', args: 'none'}], // Require using dot notation (obj.prop instead of obj['prop']) where possible - 'dot-notation': 'error' + 'dot-notation': 'error', + // Don't use spaces before parens in anonymous or named functions + 'space-before-function-paren': ['error', {anonymous: 'never', named: 'never', asyncArrow: 'always'}] // We will NOT be using eqeqeq for a few reasons: // 1. I would have to go through and check every single `==` to make sure that it's not depending on loose equality checks. diff --git a/classes/CMarketItem.js b/classes/CMarketItem.js index ba75d146..8dd40c54 100644 --- a/classes/CMarketItem.js +++ b/classes/CMarketItem.js @@ -91,7 +91,7 @@ function CMarketItem(appid, hashName, community, body, $) { // TODO: Buying listings and placing buy orders } -CMarketItem.prototype.updatePrice = function (currency, callback) { +CMarketItem.prototype.updatePrice = function(currency, callback) { if (this.commodity) { this.updatePriceForCommodity(currency, callback); } else { @@ -142,7 +142,7 @@ CMarketItem.prototype.updatePriceForCommodity = function(currency, callback) { }, 'steamcommunity'); }; -CMarketItem.prototype.updatePriceForNonCommodity = function (currency, callback) { +CMarketItem.prototype.updatePriceForNonCommodity = function(currency, callback) { if (this.commodity) { throw new Error('Cannot update price for commodity item'); } diff --git a/classes/CSteamGroup.js b/classes/CSteamGroup.js index 6d8d74b1..edb2a5c7 100644 --- a/classes/CSteamGroup.js +++ b/classes/CSteamGroup.js @@ -98,7 +98,7 @@ CSteamGroup.prototype.editEvent = function(id, name, type, description, time, se this._community.editGroupEvent(this.steamID, id, name, type, description, time, server, callback); }; -CSteamGroup.prototype.deleteEvent = function (id, callback) { +CSteamGroup.prototype.deleteEvent = function(id, callback) { this._community.deleteGroupEvent(this.steamID, id, callback); }; diff --git a/components/market.js b/components/market.js index c786eeb3..7f5b82cc 100644 --- a/components/market.js +++ b/components/market.js @@ -17,7 +17,7 @@ SteamCommunity.prototype.getMarketApps = function(callback) { let $ = Cheerio.load(body); if ($('.market_search_game_button_group')) { let apps = {}; - $('.market_search_game_button_group a.game_button').each(function (i, element) { + $('.market_search_game_button_group a.game_button').each((i, element) => { let e = Cheerio.load(element); let name = e('.game_button_game_name').text().trim(); let url = element.attribs.href; From fd5ed31afac0757c362ff0d1a60c17537f04a236 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 29 Jul 2021 22:55:26 -0400 Subject: [PATCH 42/68] Ignore unused vars starting with underscore --- .eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 5b9db8a9..25ea1eb6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -24,7 +24,7 @@ module.exports = { // Require spaces before and after keywords (like "if") 'keyword-spacing': 'error', // Don't allow unused variables, but allow unused function args (e.g. in callbacks) and global vars - 'no-unused-vars': ['error', {vars: 'local', args: 'none'}], + 'no-unused-vars': ['error', {vars: 'local', args: 'none', varsIgnorePattern: '^_'}], // Require using dot notation (obj.prop instead of obj['prop']) where possible 'dot-notation': 'error', // Don't use spaces before parens in anonymous or named functions From c7a8c676b743210929925a98aaf2127960615f78 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Fri, 30 Jul 2021 00:26:11 -0400 Subject: [PATCH 43/68] Simplify isSteamID --- components/helpers.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/components/helpers.js b/components/helpers.js index 64d1ddcc..725c9ab1 100644 --- a/components/helpers.js +++ b/components/helpers.js @@ -1,14 +1,12 @@ const EResult = require('../resources/EResult.js'); +/** + * Make sure that a provided input is a valid SteamID object. + * @param {object} input + * @returns {boolean} + */ exports.isSteamID = function(input) { - let keys = Object.keys(input); - if (keys.length != 4) { - return false; - } - - // Make sure it has the keys we expect - keys.sort(); - return keys.join(',') == 'accountid,instance,type,universe'; + return ['universe', 'type', 'instance', 'accountid'].every(prop => typeof input[prop] == 'number' || typeof input[prop] == 'bigint'); }; exports.decodeSteamTime = function(time) { From d09f18f05586bae97c583bc3b6bef051e12aa39f Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Fri, 30 Jul 2021 00:27:21 -0400 Subject: [PATCH 44/68] Compare against PrivacyState rather than using a magic number --- classes/CSteamUser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/CSteamUser.js b/classes/CSteamUser.js index c82abcf1..191f26ad 100644 --- a/classes/CSteamUser.js +++ b/classes/CSteamUser.js @@ -68,7 +68,7 @@ function CSteamUser(community, userData, customurl) { this.isLimitedAccount = processItem('isLimitedAccount') == 1; this.customURL = processItem('customURL', customurl); - if (this.visibilityState == 3) { + if (this.visibilityState == SteamCommunity.PrivacyState.Public) { let memberSinceValue = processItem('memberSince', '0').replace(/(\d{1,2})(st|nd|th)/, '$1'); if (memberSinceValue.indexOf(',') === -1) { From ad095605b6d4629cb84d41990b53f8c0e5e54dff Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Fri, 30 Jul 2021 00:39:46 -0400 Subject: [PATCH 45/68] Clean up comment parsing code a little --- components/groups.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/components/groups.js b/components/groups.js index 74dcf560..1eac14f3 100644 --- a/components/groups.js +++ b/components/groups.js @@ -552,19 +552,18 @@ SteamCommunity.prototype.getAllGroupComments = function(gid, from, count, callba let $ = Cheerio.load(JSON.parse(body).comments_html); - // !! DO NOT replace this function with an arrow function. We depend on the `this` context inside the function, - // which is the comment element. - $('.commentthread_comment_content').each(function() { + $('.commentthread_comment_content').each((i, element) => { let comment = {}; - let $selector = $(this).find('.commentthread_author_link'); - comment.authorName = $($selector).find('bdi').text(); - comment.authorId = $($selector).attr('href').replace(/https?:\/\/steamcommunity.com\/(id|profiles)\//, ''); + let $element = $(element); + let $selector = $($element.find('.commentthread_author_link')); + comment.authorName = $selector.find('bdi').text(); + comment.authorId = $selector.attr('href').replace(/https?:\/\/steamcommunity.com\/(id|profiles)\//, ''); comment.date = Helpers.decodeSteamTime($(this).find('.commentthread_comment_timestamp').text().trim()); - $selector = $(this).find('.commentthread_comment_text'); - comment.commentId = $($selector).attr('id').replace('comment_content_', ''); - comment.text = $($selector).html().trim(); + $selector = $($element.find('.commentthread_comment_text')); + comment.commentId = $selector.attr('id').replace('comment_content_', ''); + comment.text = $selector.html().trim(); comments.push(comment); }); From 48f0637c1856548155279d76e2d007ec2ef8572d Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Fri, 30 Jul 2021 02:06:58 -0400 Subject: [PATCH 46/68] Use steamid@2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7a53745f..d39df52e 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "node-bignumber": "^1.2.1", "request": "^2.88.0", "steam-totp": "^2.1.0", - "steamid": "^1.1.3", + "steamid": "^2.0.0", "tough-cookie": "^4.0.0", "xml2js": "^0.4.22" }, From 43c0504232d0f636c254ce27528aecae6f833a70 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Fri, 30 Jul 2021 02:07:04 -0400 Subject: [PATCH 47/68] Require node 12 or later --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d39df52e..01ea1c64 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "xml2js": "^0.4.22" }, "engines": { - "node": ">=8.0.0" + "node": ">=12.0.0" }, "devDependencies": { "eslint": "^7.31.0" From d28c2cdde910e0894fd23c45b3d471bc8c0437ce Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Mon, 24 Apr 2023 18:01:19 -0400 Subject: [PATCH 48/68] Require node 14 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 01ea1c64..3fcee441 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "xml2js": "^0.4.22" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "devDependencies": { "eslint": "^7.31.0" From 9218ec6dd91c34d9eecf387d569f2b78c77a8e83 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Mon, 24 Apr 2023 23:50:24 -0400 Subject: [PATCH 49/68] Use stdlib 2.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3fcee441..a2fc7ae0 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "url": "https://github.com/DoctorMcKay/node-steamcommunity.git" }, "dependencies": { - "@doctormckay/stdlib": "^1.14.1", + "@doctormckay/stdlib": "^2.1.0", "cheerio": "0.22.0", "got": "^11.8.2", "image-size": "^0.8.2", From 711ab8a3ae6ea6dc77583348a7c6258889a613b1 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Wed, 26 Apr 2023 14:22:09 -0400 Subject: [PATCH 50/68] Update stdlib version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a2fc7ae0..aabee361 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "url": "https://github.com/DoctorMcKay/node-steamcommunity.git" }, "dependencies": { - "@doctormckay/stdlib": "^2.1.0", + "@doctormckay/stdlib": "^2.1.2", "cheerio": "0.22.0", "got": "^11.8.2", "image-size": "^0.8.2", From f776d9fd4a217539b5e306ddc6253c754abf5728 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 01:20:06 -0400 Subject: [PATCH 51/68] Removed getWebApiOauthToken --- components/webapi.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/components/webapi.js b/components/webapi.js index 0040856a..13357837 100644 --- a/components/webapi.js +++ b/components/webapi.js @@ -45,18 +45,6 @@ SteamCommunity.prototype.getWebApiKey = function(domain, callback) { }, "steamcommunity"); }; -/** - * @deprecated No longer works. Will be removed in a future release. - * @param {function} callback - */ -SteamCommunity.prototype.getWebApiOauthToken = function(callback) { - if (this.oAuthToken) { - return callback(null, this.oAuthToken); - } - - callback(new Error('This operation requires an OAuth token, which is no longer issued by Steam.')); -}; - /** * Sets an access_token generated by steam-session using EAuthTokenPlatformType.MobileApp. * Required for some operations such as 2FA enabling and disabling. From 2bfaecd88fe535ca2032ead1f3666789022f719d Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 01:20:22 -0400 Subject: [PATCH 52/68] Removed oAuthLogin method --- index.js | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/index.js b/index.js index 7c1d6086..c17eee04 100644 --- a/index.js +++ b/index.js @@ -211,43 +211,6 @@ SteamCommunity.prototype.login = function(details, callback) { }); }; -/** - * @deprecated - * @param {string} steamguard - * @param {string} token - * @param {function} callback - */ -SteamCommunity.prototype.oAuthLogin = function(steamguard, token, callback) { - steamguard = steamguard.split('||'); - let steamID = new SteamID(steamguard[0]); - - this.httpRequestPost({ - uri: 'https://api.steampowered.com/IMobileAuthService/GetWGToken/v1/', - form: {access_token: token}, - json: true - }, (err, response, body) => { - if (err) { - callback(err); - return; - } - - if (!body.response || !body.response.token || !body.response.token_secure) { - callback(new Error('Malformed response')); - return; - } - - let cookies = [ - 'steamLogin=' + encodeURIComponent(steamID.getSteamID64() + '||' + body.response.token), - 'steamLoginSecure=' + encodeURIComponent(steamID.getSteamID64() + '||' + body.response.token_secure), - 'steamMachineAuth' + steamID.getSteamID64() + '=' + steamguard[1], - 'sessionid=' + this.getSessionID() - ]; - - this.setCookies(cookies); - callback(null, this.getSessionID(), cookies); - }, 'steamcommunity'); -}; - /** * Get a token that can be used to log onto Steam using steam-user. * @param {function} callback From b48e8fa537cee505c0be1c3a1c2eb24ff3014e24 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 01:21:01 -0400 Subject: [PATCH 53/68] Promote steam-session to a full dependency --- package.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/package.json b/package.json index 061d1d4b..5614499a 100644 --- a/package.json +++ b/package.json @@ -28,14 +28,12 @@ "image-size": "^0.8.2", "node-bignumber": "^1.2.1", "request": "^2.88.0", + "steam-session": "^1.2.4", "steam-totp": "^2.1.0", "steamid": "^2.0.0", "tough-cookie": "^4.0.0", "xml2js": "^0.4.22" }, - "devDependencies": { - "steam-session": "^1.2.3" - }, "engines": { "node": ">=14.0.0" }, From 2094f0545e835d62b6bd36ce9525607527b44027 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 01:21:31 -0400 Subject: [PATCH 54/68] Update xml2js dependency --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5614499a..e127d74a 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "steam-totp": "^2.1.0", "steamid": "^2.0.0", "tough-cookie": "^4.0.0", - "xml2js": "^0.4.22" + "xml2js": "^0.6.0" }, "engines": { "node": ">=14.0.0" From 078e39584eb19814cfaf6691f794039e8c6c5fd0 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 01:22:36 -0400 Subject: [PATCH 55/68] Remove got dependency --- index.js | 1 - package.json | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/index.js b/index.js index c17eee04..829e434b 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,4 @@ const {EventEmitter} = require('events'); -const Got = require('got'); const {hex2b64} = require('node-bignumber'); const Request = require('request'); const {Key: RSA} = require('node-bignumber'); diff --git a/package.json b/package.json index e127d74a..1ce69f9d 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,8 @@ "url": "https://github.com/DoctorMcKay/node-steamcommunity.git" }, "dependencies": { - "@doctormckay/stdlib": "^2.1.2", + "@doctormckay/stdlib": "^2.4.2", "cheerio": "0.22.0", - "got": "^11.8.2", "image-size": "^0.8.2", "node-bignumber": "^1.2.1", "request": "^2.88.0", From ccf16b64e6fb9cf0937df4f5940c1c01fc0953e7 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 02:07:42 -0400 Subject: [PATCH 56/68] Updated httpRequest to use StdLib HttpClient --- components/http.js | 195 +++++++++++++++++++++-------------------- components/market.js | 1 + index.js | 202 +++++++------------------------------------ package.json | 2 +- 4 files changed, 134 insertions(+), 266 deletions(-) diff --git a/components/http.js b/components/http.js index a1247b18..bf37cacb 100644 --- a/components/http.js +++ b/components/http.js @@ -1,131 +1,137 @@ -const SteamCommunity = require('../index.js'); - -SteamCommunity.prototype.httpRequest = function(uri, options, callback, source) { - if (typeof uri == 'object') { - source = callback; - callback = options; - options = uri; - uri = options.url || options.uri; - } else if (typeof options == 'function') { - source = callback; - callback = options; - options = {}; - } - - options.url = options.uri = uri; - - if (this._httpRequestConvenienceMethod) { - options.method = this._httpRequestConvenienceMethod; - delete this._httpRequestConvenienceMethod; - } - - let requestID = ++this._httpRequestID; - source = source || ''; +const {HttpResponse} = require('@doctormckay/stdlib/http'); // eslint-disable-line - let continued = false; +const SteamCommunity = require('../index.js'); - let continueRequest = (err) => { - if (continued) { - return; - } +/** + * @param {object} options + * @param {string} options.method + * @param {string} options.url + * @param {string} [options.source=''] + * @param {object} [options.qs] + * @param {*} [options.body] + * @param {object} [options.form] + * @param {object} [options.multipartForm] + * @param {boolean} [options.json=false] + * @param {boolean} [options.followRedirect=true] + * @param {boolean} [options.checkHttpError=true] + * @param {boolean} [options.checkCommunityError=true] + * @param {boolean} [options.checkTradeError=true] + * @param {boolean} [options.checkJsonError=true] + * @return {Promise} + */ +SteamCommunity.prototype.httpRequest = function(options) { + return new Promise((resolve, reject) => { + let requestID = ++this._httpRequestID; + let source = options.source || ''; + + let continued = false; + + let continueRequest = async (err) => { + if (continued) { + return; + } - continued = true; + continued = true; - if (err) { - if (callback) { - callback(err); + if (err) { + return reject(err); } - return; - } - - this.request(options, (err, response, body) => { - let hasCallback = !!callback; - let httpError = options.checkHttpError !== false && this._checkHttpError(err, response, callback, body); - let communityError = !options.json && options.checkCommunityError !== false && this._checkCommunityError(body, httpError ? noop : callback); // don't fire the callback if hasHttpError did it already - let tradeError = !options.json && options.checkTradeError !== false && this._checkTradeError(body, httpError || communityError ? noop : callback); // don't fire the callback if either of the previous already did - let jsonError = options.json && options.checkJsonError !== false && !body ? new Error('Malformed JSON response') : null; + /** @var {HttpResponse} result */ + let result; + + try { + result = await this._httpClient.request({ + method: options.method, + url: options.url, + queryString: options.qs, + headers: options.headers, + body: options.body, + urlEncodedForm: options.form, + multipartForm: options.multipartForm, + json: options.json, + followRedirects: options.followRedirect + }); + } catch (ex) { + return reject(ex); + } - this.emit('postHttpRequest', requestID, source, options, httpError || communityError || tradeError || jsonError || null, response, body, { - hasCallback, + let httpError = options.checkHttpError !== false && this._checkHttpError(result); + let communityError = !options.json && options.checkCommunityError !== false && this._checkCommunityError(result); + let tradeError = !options.json && options.checkTradeError !== false && this._checkTradeError(result); + let jsonError = options.json && options.checkJsonError !== false && !result.jsonBody ? new Error('Malformed JSON response') : null; + + this.emit('postHttpRequest', { + requestID, + source, + options, + response: result, + body: result.textBody, + error: httpError || communityError || tradeError || jsonError || null, httpError, communityError, tradeError, jsonError }); - if (hasCallback && !(httpError || communityError || tradeError)) { - if (jsonError) { - callback.call(this, jsonError, response); - } else { - callback.apply(this, arguments); - } - } - }); - }; - - if (!this.onPreHttpRequest || !this.onPreHttpRequest(requestID, source, options, continueRequest)) { - // No pre-hook, or the pre-hook doesn't want to delay the request. - continueRequest(null); - } -}; + resolve(result); + }; -SteamCommunity.prototype.httpRequestGet = function() { - this._httpRequestConvenienceMethod = 'GET'; - return this.httpRequest.apply(this, arguments); -}; - -SteamCommunity.prototype.httpRequestPost = function() { - this._httpRequestConvenienceMethod = 'POST'; - return this.httpRequest.apply(this, arguments); + if (!this.onPreHttpRequest || !this.onPreHttpRequest(requestID, source, options, continueRequest)) { + // No pre-hook, or the pre-hook doesn't want to delay the request. + continueRequest(null); + } + }); }; SteamCommunity.prototype._notifySessionExpired = function(err) { this.emit('sessionExpired', err); }; -SteamCommunity.prototype._checkHttpError = function(err, response, callback, body) { - if (err) { - callback(err, response, body); - return err; - } - +/** + * @param {HttpResponse} response + * @return {Error|boolean} + * @private + */ +SteamCommunity.prototype._checkHttpError = function(response) { if (response.statusCode >= 300 && response.statusCode <= 399 && response.headers.location.indexOf('/login') != -1) { - err = new Error('Not Logged In'); - callback(err, response, body); + let err = new Error('Not Logged In'); this._notifySessionExpired(err); return err; } - if (response.statusCode == 403 && typeof response.body == 'string' && response.body.match(/
Enter your PIN below to exit Family View.<\/div>/)) { - err = new Error('Family View Restricted'); - callback(err, response, body); - return err; + if ( + response.statusCode == 403 + && typeof response.textBody == 'string' + && response.textBody.match(/
Enter your PIN below to exit Family View.<\/div>/) + ) { + return new Error('Family View Restricted'); } if (response.statusCode >= 400) { - err = new Error(`HTTP error ${response.statusCode}`); + let err = new Error(`HTTP error ${response.statusCode}`); err.code = response.statusCode; - callback(err, response, body); return err; } return false; }; -SteamCommunity.prototype._checkCommunityError = function(html, callback) { - let err; +/** + * @param {HttpResponse} response + * @return {Error|boolean} + * @private + */ +SteamCommunity.prototype._checkCommunityError = function(response) { + let html = response.textBody; if (typeof html == 'string' && html.match(/

Sorry!<\/h1>/)) { let match = html.match(/

(.+)<\/h3>/); - err = new Error(match ? match[1] : 'Unknown error occurred'); - callback(err); - return err; + return new Error(match ? match[1] : 'Unknown error occurred'); } if (typeof html == 'string' && html.indexOf('g_steamID = false;') > -1 && html.indexOf('Sign In') > -1) { - err = new Error('Not Logged In'); - callback(err); + let err = new Error('Not Logged In'); this._notifySessionExpired(err); return err; } @@ -133,19 +139,22 @@ SteamCommunity.prototype._checkCommunityError = function(html, callback) { return false; }; -SteamCommunity.prototype._checkTradeError = function(html, callback) { +/** + * @param {HttpResponse} response + * @return {Error|boolean} + * @private + */ +SteamCommunity.prototype._checkTradeError = function(response) { + let html = response.textBody; + if (typeof html !== 'string') { return false; } let match = html.match(/
\s*([^<]+)\s*<\/div>/); if (match) { - let err = new Error(match[1].trim()); - callback(err); - return err; + return new Error(match[1].trim()); } return false; }; - -function noop() {} diff --git a/components/market.js b/components/market.js index 670dc6b5..0ede87cb 100644 --- a/components/market.js +++ b/components/market.js @@ -247,6 +247,7 @@ SteamCommunity.prototype.createBoosterPack = function(appid, useUntradableGems, // We can now check HTTP status codes if (this._checkHttpError(err, res, callback, body)) { + // TODO v4 return; } diff --git a/index.js b/index.js index 829e434b..3db5245e 100644 --- a/index.js +++ b/index.js @@ -1,15 +1,13 @@ const {EventEmitter} = require('events'); const {hex2b64} = require('node-bignumber'); -const Request = require('request'); const {Key: RSA} = require('node-bignumber'); const StdLib = require('@doctormckay/stdlib'); const SteamID = require('steamid'); -const {CookieJar} = require('tough-cookie'); const Util = require('util'); const Helpers = require('./components/helpers.js'); -const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36'; +const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'; Util.inherits(SteamCommunity, EventEmitter); @@ -21,31 +19,43 @@ SteamCommunity.EResult = require('./resources/EResult.js'); SteamCommunity.ESharedFileType = require('./resources/ESharedFileType.js'); SteamCommunity.EFriendRelationship = require('./resources/EFriendRelationship.js'); - +/** + * + * @param {object} [options] + * @param {number} [options.timeout=50000] - The time in milliseconds that SteamCommunity will wait for HTTP requests to complete. + * @param {string} [options.localAddress] - The local IP address that SteamCommunity will use for its HTTP requests. + * @param {string} [options.httpProxy] - A string containing the URI of an HTTP proxy to use for all requests, e.g. `http://user:pass@1.2.3.4:8888` + * @param {object} [options.defaultHttpHeaders] - An object containing some headers to send for every HTTP request + * @constructor + */ function SteamCommunity(options) { options = options || {}; - this._jar = new CookieJar(); + this._jar = new StdLib.HTTP.CookieJar(); this._captchaGid = -1; this._httpRequestID = 0; - let defaults = { - jar: this._jar, - timeout: options.timeout || 50000, - gzip: true, - headers: { - 'User-Agent': options.userAgent || USER_AGENT - } + let defaultHeaders = { + 'user-agent': USER_AGENT }; - this._options = options; - - if (options.localAddress) { - defaults.localAddress = options.localAddress; + // Apply the user's custom default headers + for (let i in (options.defaultHttpHeaders || {})) { + // Make sure all header names are lower case to avoid conflicts + defaultHeaders[i.toLowerCase()] = options.defaultHttpHeaders[i]; } - this.request = options.request || Request.defaults({forever: true}); // "forever" indicates that we want a keep-alive agent - this.request = this.request.defaults(defaults); + this._httpClient = new StdLib.HTTP.HttpClient({ + httpAgent: options.httpProxy ? StdLib.HTTP.getProxyAgent(false, options.httpProxy) : null, + httpsAgent: options.httpProxy ? StdLib.HTTP.getProxyAgent(true, options.httpProxy) : null, + localAddress: options.localAddress, + defaultHeaders, + defaultTimeout: options.timeout || 50000, + cookieJar: this._jar, + gzip: true + }); + + this._options = options; // English this._setCookie('Steam_Language=english'); @@ -54,160 +64,8 @@ function SteamCommunity(options) { this._setCookie('timezoneOffset=0,0'); } -SteamCommunity.prototype.login = function(details, callback) { - if (!details.accountName || !details.password) { - throw new Error('Missing either accountName or password to login; both are needed'); - } - - let callbackArgs = ['sessionID', 'cookies', 'steamguard', 'oauthToken']; - return StdLib.Promises.callbackPromise(callbackArgs, callback, false, (resolve, reject) => { - if (details.steamguard) { - let parts = details.steamguard.split('||'); - this._setCookie(`steamMachineAuth${parts[0]}=${encodeURIComponent(parts[1])}`, true); - } - - let disableMobile = typeof details.disableMobile == 'undefined' ? true : details.disableMobile; - - // Delete the cache - delete this._profileURL; - - // headers required to convince steam that we're logging in from a mobile device so that we can get the oAuth data - let mobileHeaders = {}; - if (!disableMobile) { - mobileHeaders = { - 'X-Requested-With': 'com.valvesoftware.android.steam.community', - Referer: 'https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client', - 'User-Agent': this._options.mobileUserAgent || details.mobileUserAgent || 'Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30', - Accept: 'text/javascript, text/html, application/xml, text/xml, */*' - }; - - this._setCookie('mobileClientVersion=0 (2.1.3)'); - this._setCookie('mobileClient=android'); - } else { - mobileHeaders = {Referer: 'https://steamcommunity.com/login'}; - } - - const deleteMobileCookies = () => { - this._setCookie('mobileClientVersion=; max-age=0'); - this._setCookie('mobileClient=; max-age=0'); - }; - - this.httpRequestPost('https://steamcommunity.com/login/getrsakey/', { - form: {username: details.accountName}, - headers: mobileHeaders, - json: true - }, (err, response, body) => { - // Remove the mobile cookies - if (err) { - deleteMobileCookies(); - return reject(err); - } - - if (!body.publickey_mod || !body.publickey_exp) { - deleteMobileCookies(); - return reject(new Error('Invalid RSA key received')); - } - - let key = new RSA(); - key.setPublic(body.publickey_mod, body.publickey_exp); - - let formObj = { - captcha_text: details.captcha || '', - captchagid: this._captchaGid, - emailauth: details.authCode || '', - emailsteamid: '', - password: hex2b64(key.encrypt(details.password)), - remember_login: 'true', - rsatimestamp: body.timestamp, - twofactorcode: details.twoFactorCode || '', - username: details.accountName, - loginfriendlyname: '', - donotcache: Date.now() - }; - - if (!disableMobile) { - formObj.oauth_client_id = 'DE45CD61'; - formObj.oauth_scope = 'read_profile write_profile read_client write_client'; - formObj.loginfriendlyname = '#login_emailauth_friendlyname_mobile'; - } - - this.httpRequestPost({ - uri: 'https://steamcommunity.com/login/dologin/', - json: true, - form: formObj, - headers: mobileHeaders - }, (err, response, body) => { - deleteMobileCookies(); - - if (err) { - return reject(err); - } - - let error; - if (!body.success && body.emailauth_needed) { - // Steam Guard (email) - error = new Error('SteamGuard'); - error.emaildomain = body.emaildomain; - - return reject(error); - } else if (!body.success && body.requires_twofactor) { - // Steam Guard (app) - return reject(new Error('SteamGuardMobile')); - } else if (!body.success && body.captcha_needed && body.message.match(/Please verify your humanity/)) { - error = new Error('CAPTCHA'); - error.captchaurl = 'https://steamcommunity.com/login/rendercaptcha/?gid=' + body.captcha_gid; - - this._captchaGid = body.captcha_gid; - - callback(error); - } else if (!body.success) { - callback(new Error(body.message || 'Unknown error')); - } else { - var sessionID = generateSessionID(); - var oAuth = {}; - self._setCookie(Request.cookie('sessionid=' + sessionID)); - - let cookies = this._jar.getCookieStringSync('https://steamcommunity.com').split(';').map(cookie => cookie.trim()); - - if (!disableMobile && body.oauth) { - oAuth = JSON.parse(body.oauth); - this.steamID = new SteamID(oAuth.steamid); - this.oAuthToken = oAuth.oauth_token; - } else { - for (let i = 0; i < cookies.length; i++) { - let parts = cookies[i].split('='); - if (parts[0] == 'steamLogin') { - this.steamID = new SteamID(decodeURIComponent(parts[1]).split('||')[0]); - break; - } - } - - this.oAuthToken = null; - } - - // Find the Steam Guard cookie - let steamguard = null; - for (let i = 0; i < cookies.length; i++) { - let parts = cookies[i].split('='); - if (parts[0] == 'steamMachineAuth' + this.steamID) { - steamguard = this.steamID.toString() + '||' + decodeURIComponent(parts[1]); - break; - } - } - - // Call setCookies to propagate our cookies to the other domains - this.setCookies(cookies); - - return resolve({ - sessionID, - cookies, - steamguard, - oauthToken: disableMobile ? null : oAuth.oauth_token - }); - } - }, 'steamcommunity'); - }, 'steamcommunity'); - }); +SteamCommunity.prototype.login = function(details) { + // TODO }; /** diff --git a/package.json b/package.json index 1ce69f9d..b6aec8ac 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "url": "https://github.com/DoctorMcKay/node-steamcommunity.git" }, "dependencies": { - "@doctormckay/stdlib": "^2.4.2", + "@doctormckay/stdlib": "^2.5.0", "cheerio": "0.22.0", "image-size": "^0.8.2", "node-bignumber": "^1.2.1", From 0ba889ee7eed1d364aa9bf6bf501a4e19ec10552 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 02:55:47 -0400 Subject: [PATCH 57/68] Fixed merge conflict remnant --- components/confirmations.js | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/components/confirmations.js b/components/confirmations.js index 44ec9681..e22f29ee 100644 --- a/components/confirmations.js +++ b/components/confirmations.js @@ -205,40 +205,6 @@ SteamCommunity.prototype.acceptConfirmationForObject = function(identitySecret, }, 1000 * 60 * 60 * 12).unref(); }); } - - function doConfirmation() { - var offset = self._timeOffset; - var time = SteamTotp.time(offset); - var confKey = SteamTotp.getConfirmationKey(identitySecret, time, 'list'); - self.getConfirmations(time, {tag: 'list', key: confKey}, function(err, confs) { - if (err) { - callback(err); - return; - } - - var conf = confs.filter(function(conf) { return conf.creator == objectID; }); - if (conf.length == 0) { - callback(new Error('Could not find confirmation for object ' + objectID)); - return; - } - - conf = conf[0]; - - // make sure we don't reuse the same time - var localOffset = 0; - do { - time = SteamTotp.time(offset) + localOffset++; - } while (self._usedConfTimes.indexOf(time) != -1); - - self._usedConfTimes.push(time); - if (self._usedConfTimes.length > 60) { - self._usedConfTimes.splice(0, self._usedConfTimes.length - 60); // we don't need to save more than 60 entries - } - - confKey = SteamTotp.getConfirmationKey(identitySecret, time, 'accept'); - conf.respond(time, {tag: 'accept', key: confKey}, true, callback); - }); - } }; /** From 028ee43bda68e70ca992abb8fc8a490b46e7bec1 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 02:56:05 -0400 Subject: [PATCH 58/68] Updated index.js methods to use new http interface --- .gitignore | 1 + components/http.js | 47 +++++- index.js | 364 +++++++++++++++++++++------------------------ package.json | 1 - 4 files changed, 213 insertions(+), 200 deletions(-) diff --git a/.gitignore b/.gitignore index d499ad4e..1fdd7bf4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules/* test.js +dev/ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 diff --git a/components/http.js b/components/http.js index bf37cacb..206c52ac 100644 --- a/components/http.js +++ b/components/http.js @@ -11,7 +11,7 @@ const SteamCommunity = require('../index.js'); * @param {*} [options.body] * @param {object} [options.form] * @param {object} [options.multipartForm] - * @param {boolean} [options.json=false] + * @param {boolean} [options.json=false] - Controls whether the *REQUEST* should be sent as json. * @param {boolean} [options.followRedirect=true] * @param {boolean} [options.checkHttpError=true] * @param {boolean} [options.checkCommunityError=true] @@ -84,6 +84,51 @@ SteamCommunity.prototype.httpRequest = function(options) { }); }; +/** + * @param {string|object} endpoint + * @param {object} [form] + * @private + */ +SteamCommunity.prototype._myProfile = async function(endpoint, form) { + if (!this._profileURL) { + let result = await this.httpRequest({ + method: 'GET', + url: 'https://steamcommunity.com/my', + followRedirect: false, + source: 'steamcommunity' + }); + + if (result.statusCode != 302) { + throw new Error(`HTTP error ${result.statusCode}`); + } + + let match = result.headers.location.match(/steamcommunity\.com(\/(id|profiles)\/[^/]+)\/?/); + if (!match) { + throw new Error('Can\'t get profile URL'); + } + + this._profileURL = match[1]; + setTimeout(() => { + delete this._profileURL; // delete the cache + }, 60000).unref(); + } + + let options = endpoint.endpoint ? endpoint : {}; + options.url = `https://steamcommunity.com${this._profileURL}/${endpoint.endpoint || endpoint}`; + options.followRedirect = true; + + if (form) { + options.method = 'POST'; + options.form = form; + } else if (!options.method) { + options.method = 'GET'; + } + + options.source = 'steamcommunity'; + + return await this.httpRequest(options); +}; + SteamCommunity.prototype._notifySessionExpired = function(err) { this.emit('sessionExpired', err); }; diff --git a/index.js b/index.js index 3db5245e..d8fe9fe8 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,4 @@ const {EventEmitter} = require('events'); -const {hex2b64} = require('node-bignumber'); -const {Key: RSA} = require('node-bignumber'); const StdLib = require('@doctormckay/stdlib'); const SteamID = require('steamid'); const Util = require('util'); @@ -70,34 +68,31 @@ SteamCommunity.prototype.login = function(details) { /** * Get a token that can be used to log onto Steam using steam-user. - * @param {function} callback + * @param {function} [callback] + * @return Promise<{steamID: SteamID, accountName: string, webLogonToken: string}> */ SteamCommunity.prototype.getClientLogonToken = function(callback) { - this.httpRequestGet({ - uri: 'https://steamcommunity.com/chat/clientjstoken', - json: true - }, (err, res, body) => { - if (err || res.statusCode != 200) { - callback(err ? err : new Error('HTTP error ' + res.statusCode)); - return; - } + return StdLib.Promises.callbackPromise(null, callback, false, async (resolve, reject) => { + let {jsonBody} = await this.httpRequest({ + method: 'GET', + uri: 'https://steamcommunity.com/chat/clientjstoken', + source: 'steamcommunity' + }); - if (!body.logged_in) { + if (!jsonBody.logged_in) { let e = new Error('Not Logged In'); - callback(e); this._notifySessionExpired(e); - return; + return reject(e); } - if (!body.steamid || !body.account_name || !body.token) { - callback(new Error('Malformed response')); - return; + if (!jsonBody.steamid || !jsonBody.account_name || !jsonBody.token) { + return reject(new Error('Malformed response')); } - callback(null, { - steamID: new SteamID(body.steamid), - accountName: body.account_name, - webLogonToken: body.token + resolve({ + steamID: new SteamID(jsonBody.steamid), + accountName: jsonBody.account_name, + webLogonToken: jsonBody.token }); }); }; @@ -105,15 +100,12 @@ SteamCommunity.prototype.getClientLogonToken = function(callback) { /** * Sets a single cookie in our cookie jar. * @param {string} cookie - * @param {boolean} [secure=false] * @private */ -SteamCommunity.prototype._setCookie = function(cookie, secure) { - let protocol = secure ? 'https' : 'http'; - - this._jar.setCookieSync(cookie, `${protocol}://steamcommunity.com`); - this._jar.setCookieSync(cookie, `${protocol}://store.steampowered.com`); - this._jar.setCookieSync(cookie, `${protocol}://help.steampowered.com`); +SteamCommunity.prototype._setCookie = function(cookie) { + this._jar.add(cookie, 'steamcommunity.com'); + this._jar.add(cookie, 'store.steampowered.com'); + this._jar.add(cookie, 'help.steampowered.com'); }; /** @@ -131,7 +123,7 @@ SteamCommunity.prototype.setCookies = function(cookies) { this.steamID = new SteamID(cookie.match(/=(\d+)/)[1]); } - this._setCookie(cookie, !!(cookieName.match(/^steamMachineAuth/) || cookieName.match(/Secure$/))); + this._setCookie(cookie); }); // The account we're logged in as might have changed, so verify that our mobile access token (if any) is still valid @@ -140,254 +132,231 @@ SteamCommunity.prototype.setCookies = function(cookies) { }; SteamCommunity.prototype.getSessionID = function() { - let sessionIdCookie = this._jar.getCookiesSync('http://steamcommunity.com').find(cookie => cookie.key == 'sessionid'); + let sessionIdCookie = this._jar.cookies + .filter(c => c.domain == 'steamcommunity.com') + .find(c => c.name == 'sessionid'); if (sessionIdCookie) { - return sessionIdCookie.value; + return sessionIdCookie.content; } - // Generate a new session id + // No cookie found? Generate a new session id let sessionID = require('crypto').randomBytes(12).toString('hex'); this._setCookie(`sessionid=${sessionID}`); return sessionID; }; +/** + * @param {string} pin + * @param {function} [callback] + * @return Promise + */ SteamCommunity.prototype.parentalUnlock = function(pin, callback) { let sessionID = this.getSessionID(); - this.httpRequestPost('https://steamcommunity.com/parental/ajaxunlock', { - json: true, - form: { - pin: pin, - sessionid: sessionID - } - }, (err, response, body) => { - if (!callback) { - return; - } - - if (err) { - callback(err); - return; - } + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + let {jsonBody} = await this.httpRequest({ + method: 'POST', + url: 'https://steamcommunity.com/parental/ajaxunlock', + form: { + pin: pin, + sessionid: sessionID + }, + source: 'steamcommunity' + }); - if (!body || typeof body.success !== 'boolean') { - callback('Invalid response'); - return; + if (!jsonBody || typeof jsonBody.success !== 'boolean') { + return reject('Invalid response'); } - if (!body.success) { - switch (body.eresult) { + if (!jsonBody.success) { + switch (jsonBody.eresult) { case SteamCommunity.EResult.AccessDenied: - callback('Incorrect PIN'); - break; + return reject('Incorrect PIN'); case SteamCommunity.EResult.LimitExceeded: - callback('Too many invalid PIN attempts'); - break; + return reject('Too many invalid PIN attempts'); default: - callback('Error ' + body.eresult); + return reject('Error ' + jsonBody.eresult); } - - return; } - callback(); - }, 'steamcommunity'); + resolve(); + }); }; +/** + * @param {function} [callback] + * @return Promise + */ SteamCommunity.prototype.getNotifications = function(callback) { - this.httpRequestGet({ - uri: 'https://steamcommunity.com/actions/GetNotificationCounts', - json: true - }, (err, response, body) => { - if (err) { - callback(err); - return; - } + return StdLib.Promises.callbackPromise(null, callback, false, async (resolve, reject) => { + let {jsonBody} = await this.httpRequest({ + method: 'GET', + url: 'https://steamcommunity.com/actions/GetNotificationCounts', + source: 'steamcommunity' + }); - if (!body || !body.notifications) { - callback(new Error('Malformed response')); - return; + if (!jsonBody || !jsonBody.notifications) { + return reject(new Error('Malformed response')); } let notifications = { - trades: body.notifications[1] || 0, - gameTurns: body.notifications[2] || 0, - moderatorMessages: body.notifications[3] || 0, - comments: body.notifications[4] || 0, - items: body.notifications[5] || 0, - invites: body.notifications[6] || 0, + trades: jsonBody.notifications[1] || 0, + gameTurns: jsonBody.notifications[2] || 0, + moderatorMessages: jsonBody.notifications[3] || 0, + comments: jsonBody.notifications[4] || 0, + items: jsonBody.notifications[5] || 0, + invites: jsonBody.notifications[6] || 0, // dunno about 7 - gifts: body.notifications[8] || 0, - chat: body.notifications[9] || 0, - helpRequestReplies: body.notifications[10] || 0, - accountAlerts: body.notifications[11] || 0 + gifts: jsonBody.notifications[8] || 0, + chat: jsonBody.notifications[9] || 0, + helpRequestReplies: jsonBody.notifications[10] || 0, + accountAlerts: jsonBody.notifications[11] || 0 }; - callback(null, notifications); - }, 'steamcommunity'); + resolve(notifications); + }); }; +/** + * @param {function} [callback] + * @return Promise + */ SteamCommunity.prototype.resetItemNotifications = function(callback) { - this.httpRequestGet('https://steamcommunity.com/my/inventory', (err, response, body) => { - if (!callback) { - return; - } + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + await this.httpRequest({ + method: 'GET', + url: 'https://steamcommunity.com/my/inventory', + source: 'steamcommunity' + }); - callback(err || null); - }, 'steamcommunity'); + resolve(); + }); }; +/** + * @param {function} [callback] + * @return Promise<{loggedIn: boolean, familyView: boolean}> + */ SteamCommunity.prototype.loggedIn = function(callback) { - this.httpRequestGet({ - uri: 'https://steamcommunity.com/my', - followRedirect: false, - checkHttpError: false - }, (err, response, body) => { - if (err || (response.statusCode != 302 && response.statusCode != 403)) { - callback(err || new Error('HTTP error ' + response.statusCode)); - return; + return StdLib.Promises.callbackPromise(['loggedIn', 'familyView'], callback, false, async (resolve, reject) => { + let result = await this.httpRequest({ + method: 'GET', + url: 'https://steamcommunity.com/my', + followRedirect: false, + checkHttpError: false, + source: 'steamcommunity' + }); + + if (result.statusCode != 302 && result.statusCode != 403) { + return reject(new Error(`HTTP error ${result.statusCode}`)); } - if (response.statusCode == 403) { - callback(null, true, true); - return; + if (result.statusCode == 403) { + // TODO check response body to see if this is an akamai block + return resolve({ + loggedIn: true, + familyView: true + }); } - callback(null, !!response.headers.location.match(/steamcommunity\.com(\/(id|profiles)\/[^/]+)\/?/), false); - }, 'steamcommunity'); + return resolve({ + loggedIn: !!result.headers.location.match(/steamcommunity\.com(\/(id|profiles)\/[^/]+)\/?/), + familyView: false + }); + }); }; +/** + * @param {function} [callback] + * @return Promise<{url: string, token: string}> + */ SteamCommunity.prototype.getTradeURL = function(callback) { - this._myProfile('tradeoffers/privacy', null, (err, response, body) => { - if (err) { - callback(err); - return; - } + return StdLib.Promises.callbackPromise(['url', 'token'], callback, false, async (resolve, reject) => { + let {textBody} = await this._myProfile('tradeoffers/privacy'); - let match = body.match(/https?:\/\/(www.)?steamcommunity.com\/tradeoffer\/new\/?\?partner=\d+(&|&)token=([a-zA-Z0-9-_]+)/); - if (match) { - let token = match[3]; - callback(null, match[0], token); - } else { - callback(new Error('Malformed response')); + let match = textBody.match(/https?:\/\/(www.)?steamcommunity.com\/tradeoffer\/new\/?\?partner=\d+(&|&)token=([a-zA-Z0-9-_]+)/); + if (!match) { + return reject(new Error('Malformed response')); } - }, 'steamcommunity'); + + let token = match[3]; + resolve({ + url: match[0], + token + }); + }); }; +/** + * @param [callback] + * @return Promise<{url: string, token: string}> + */ SteamCommunity.prototype.changeTradeURL = function(callback) { - this._myProfile('tradeoffers/newtradeurl', {sessionid: this.getSessionID()}, (err, response, body) => { - if (!callback) { - return; - } + return StdLib.Promises.callbackPromise(['url', 'token'], callback, true, async (resolve, reject) => { + let {textBody} = await this._myProfile('tradeoffers/newtradeurl', {sessionid: this.getSessionID()}); - if (!body || typeof body !== 'string' || body.length < 3 || body.indexOf('"') !== 0) { - callback(new Error('Malformed response')); - return; + if (!textBody || typeof textBody !== 'string' || textBody.length < 3 || textBody.indexOf('"') !== 0) { + return reject(new Error('Malformed response')); } - let newToken = body.replace(/"/g, ''); //"t1o2k3e4n" => t1o2k3e4n - callback(null, 'https://steamcommunity.com/tradeoffer/new/?partner=' + this.steamID.accountid + '&token=' + newToken, newToken); - }, 'steamcommunity'); + let newToken = textBody.replace(/"/g, ''); //"t1o2k3e4n" => t1o2k3e4n + resolve({ + url: `https://steamcommunity.com/tradeoffer/new/?partner=${this.steamID.accountid}&token=${newToken}`, + token: newToken + }); + }); }; /** * Clear your profile name (alias) history. - * @param {function} callback + * @param {function} [callback] + * @return Promise */ SteamCommunity.prototype.clearPersonaNameHistory = function(callback) { - this._myProfile('ajaxclearaliashistory/', {sessionid: this.getSessionID()}, (err, res, body) => { - if (!callback) { - return; - } - - if (err) { - return callback(err); - } + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + let {statusCode, textBody} = await this._myProfile('ajaxclearaliashistory/', {sessionid: this.getSessionID()}); - if (res.statusCode != 200) { - return callback(new Error('HTTP error ' + res.statusCode)); + if (statusCode != 200) { + return reject(new Error(`HTTP error ${statusCode}`)); } try { - body = JSON.parse(body); - callback(Helpers.eresultError(body.success)); + let body = JSON.parse(textBody); + let err = Helpers.eresultError(body.success); + return err ? reject(err) : resolve(); } catch (ex) { - return callback(new Error('Malformed response')); + return reject(new Error('Malformed response')); } }); }; -SteamCommunity.prototype._myProfile = function(endpoint, form, callback) { - const completeRequest = (url) => { - let options = endpoint.endpoint ? endpoint : {}; - options.uri = 'https://steamcommunity.com' + url + '/' + (endpoint.endpoint || endpoint); - - if (form) { - options.method = 'POST'; - options.form = form; - options.followAllRedirects = true; - } else if (!options.method) { - options.method = 'GET'; - } - - this.httpRequest(options, callback, 'steamcommunity'); - }; - - if (this._profileURL) { - completeRequest(this._profileURL); - } else { - this.httpRequest('https://steamcommunity.com/my', {followRedirect: false}, (err, response, body) => { - if (err || response.statusCode != 302) { - callback(err || 'HTTP error ' + response.statusCode); - return; - } - - let match = response.headers.location.match(/steamcommunity\.com(\/(id|profiles)\/[^/]+)\/?/); - if (!match) { - callback(new Error('Can\'t get profile URL')); - return; - } - - this._profileURL = match[1]; - setTimeout(() => { - delete this._profileURL; // delete the cache - }, 60000).unref(); - - completeRequest(match[1]); - }, 'steamcommunity'); - } -}; - /** * Returns an object whose keys are 64-bit SteamIDs, and whose values are values from the EFriendRelationship enum. * Therefore, you can deduce your friends or blocked list from this object. - * @param {function} callback + * @param {function} [callback] + * @return Promise */ SteamCommunity.prototype.getFriendsList = function(callback) { - this.httpRequestGet({ - uri: 'https://steamcommunity.com/textfilter/ajaxgetfriendslist', - json: true - }, (err, res, body) => { - if (err) { - callback(err ? err : new Error('HTTP error ' + res.statusCode)); - return; - } + return StdLib.Promises.callbackPromise(['friends'], callback, false, async (resolve, reject) => { + let {jsonBody} = await this.httpRequest({ + method: 'GET', + url: 'https://steamcommunity.com/textfilter/ajaxgetfriendslist', + source: 'steamcommunity' + }); - if (body.success != 1) { - callback(Helpers.eresultError(body.success)); - return; + if (jsonBody.success != SteamCommunity.EResult.OK) { + return reject(Helpers.eresultError(jsonBody.success)); } - if (!body.friendslist || !body.friendslist.friends) { - callback(new Error('Malformed response')); - return; + if (!jsonBody.friendslist || !jsonBody.friendslist.friends) { + return reject(new Error('Malformed response')); } const friends = {}; - body.friendslist.friends.forEach(friend => (friends[friend.ulfriendid] = friend.efriendrelationship)); - callback(null, friends); + jsonBody.friendslist.friends.forEach(friend => (friends[friend.ulfriendid] = friend.efriendrelationship)); + resolve({friends}); }); }; @@ -397,7 +366,6 @@ 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'); require('./components/confirmations.js'); diff --git a/package.json b/package.json index b6aec8ac..6d3c0a88 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "@doctormckay/stdlib": "^2.5.0", "cheerio": "0.22.0", "image-size": "^0.8.2", - "node-bignumber": "^1.2.1", "request": "^2.88.0", "steam-session": "^1.2.4", "steam-totp": "^2.1.0", From c1901d5f55a385c64536f2a0ef1669ccda9d94a1 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 03:38:56 -0400 Subject: [PATCH 59/68] Updated confirmations.js methods to use new http interface --- classes/CConfirmation.js | 35 ++++-- components/confirmations.js | 226 ++++++++++++++---------------------- components/http.js | 92 ++++++--------- package.json | 2 +- 4 files changed, 149 insertions(+), 206 deletions(-) diff --git a/classes/CConfirmation.js b/classes/CConfirmation.js index f23c03f4..b6e91509 100644 --- a/classes/CConfirmation.js +++ b/classes/CConfirmation.js @@ -1,3 +1,5 @@ +const StdLib = require('@doctormckay/stdlib'); + const SteamCommunity = require('../index.js'); module.exports = CConfirmation; @@ -18,20 +20,33 @@ function CConfirmation(community, data) { this.offerID = this.type == SteamCommunity.ConfirmationType.Trade ? this.creator : null; } +/** + * @param {number} time + * @param {string} key + * @param {function} [callback] + * @return Promise<{offerID: number}> + */ CConfirmation.prototype.getOfferID = function(time, key, callback) { - if (this.type && this.creator) { - if (this.type != SteamCommunity.ConfirmationType.Trade) { - callback(new Error('Not a trade confirmation')); - return; - } + return StdLib.Promises.callbackPromise(['offerID'], null, false, async (resolve, reject) => { + if (this.type && this.creator) { + if (this.type != SteamCommunity.ConfirmationType.Trade) { + return reject(new Error('Not a trade confirmation')); + } - callback(null, this.creator); - return; - } + return resolve({offerID: this.creator}); + } - this._community.getConfirmationOfferID(this.id, time, key, callback); + return await this._community.getConfirmationOfferID(this.id, time, key, callback); + }); }; +/** + * @param {number} time + * @param {string} key + * @param {boolean} accept + * @param {function} [callback] + * @return Promise + */ CConfirmation.prototype.respond = function(time, key, accept, callback) { - this._community.respondToConfirmation(this.id, this.key, time, key, accept, callback); + return this._community.respondToConfirmation(this.id, this.key, time, key, accept, callback); }; diff --git a/components/confirmations.js b/components/confirmations.js index e22f29ee..9adcbd5d 100644 --- a/components/confirmations.js +++ b/components/confirmations.js @@ -1,47 +1,34 @@ const Cheerio = require('cheerio'); +const StdLib = require('@doctormckay/stdlib'); const SteamTotp = require('steam-totp'); const SteamCommunity = require('../index.js'); const CConfirmation = require('../classes/CConfirmation.js'); -var EConfirmationType = require('../resources/EConfirmationType.js'); +const EConfirmationType = SteamCommunity.EConfirmationType; /** * Get a list of your account's currently outstanding confirmations. * @param {int} time - The unix timestamp with which the following key was generated * @param {string} key - The confirmation key that was generated using the preceeding time and the tag 'conf' (this key can be reused) - * @param {SteamCommunity~getConfirmations} callback - Called when the list of confirmations is received + * @param {SteamCommunity~getConfirmations} [callback] - Called when the list of confirmations is received + * @return Promise<{confirmations: CConfirmation[]}> */ SteamCommunity.prototype.getConfirmations = function(time, key, callback) { - var self = this; - - // Ugly hack to maintain backward compatibility - var tag = 'conf'; - if (typeof key == 'object') { - tag = key.tag; - key = key.key; - } - - // The official Steam app uses the tag 'list', but 'conf' still works so let's use that for backward compatibility. - request(this, 'getlist', key, time, tag, null, true, function(err, body) { - if (err) { - callback(err); - return; - } + return StdLib.Promises.callbackPromise(['confirmations'], callback, false, async (resolve, reject) => { + let body = await request(this, 'getlist', key, time, 'list', null); if (!body.success) { if (body.needauth) { - var err = new Error('Not Logged In'); - self._notifySessionExpired(err); - callback(err); - return; + let err = new Error('Not Logged In'); + this._notifySessionExpired(err); + return reject(err); } - callback(new Error(body.message || body.detail || 'Failed to get confirmation list')); - return; + return reject(new Error(body.message || body.detail || 'Failed to get confirmation list')); } - var confs = (body.conf || []).map(conf => new CConfirmation(self, { + let confs = (body.conf || []).map(conf => new CConfirmation(this, { id: conf.id, type: conf.type, creator: conf.creator_id, @@ -54,7 +41,7 @@ SteamCommunity.prototype.getConfirmations = function(time, key, callback) { icon: conf.icon || '' })); - callback(null, confs); + resolve({confirmations: confs}); }); }; @@ -69,29 +56,24 @@ SteamCommunity.prototype.getConfirmations = function(time, key, callback) { * @param {int} confID - The ID of the confirmation in question * @param {int} time - The unix timestamp with which the following key was generated * @param {string} key - The confirmation key that was generated using the preceeding time and the tag "detail" (this key can be reused) - * @param {SteamCommunity~getConfirmationOfferID} callback + * @param {SteamCommunity~getConfirmationOfferID} [callback] + * @return Promise<{offerID: string|null}> */ SteamCommunity.prototype.getConfirmationOfferID = function(confID, time, key, callback) { - // The official Steam app uses the tag 'detail', but 'details' still works so let's use that for backward compatibility - request(this, 'detailspage/' + confID, key, time, 'details', null, false, function(err, body) { - if (err) { - callback(err); - return; - } + return StdLib.Promises.callbackPromise(['offerID'], callback, false, async (resolve, reject) => { + let body = await request(this, 'detailspage/' + confID, key, time, 'detail', null); if (typeof body != 'string') { - callback(new Error('Cannot load confirmation details')); - return; + return reject(new Error('Cannot load confirmation details')); } let $ = Cheerio.load(body); let offer = $('.tradeoffer'); if (offer.length < 1) { - callback(null, null); - return; + return resolve({offerID: null}); } - callback(null, offer.attr('id').split('_')[1]); + resolve({offerID: offer.attr('id').split('_')[1]}); }); }; @@ -108,42 +90,25 @@ SteamCommunity.prototype.getConfirmationOfferID = function(confID, time, key, ca * @param {int} time - The unix timestamp with which the following key was generated * @param {string} key - The confirmation key that was generated using the preceding time and the tag "allow" (if accepting) or "cancel" (if not accepting) * @param {boolean} accept - true if you want to accept the confirmation, false if you want to cancel it - * @param {SteamCommunity~genericErrorCallback} callback - Called when the request is complete + * @param {SteamCommunity~genericErrorCallback} [callback] - Called when the request is complete + * @return Promise */ SteamCommunity.prototype.respondToConfirmation = function(confID, confKey, time, key, accept, callback) { - // Ugly hack to maintain backward compatibility - var tag = accept ? 'allow' : 'cancel'; - if (typeof key == 'object') { - tag = key.tag; - key = key.key; - } - - // The official app uses tags reject/accept, but cancel/allow still works so use these for backward compatibility - request(this, (confID instanceof Array) ? 'multiajaxop' : 'ajaxop', key, time, tag, { - op: accept ? 'allow' : 'cancel', - cid: confID, - ck: confKey - }, true, function(err, body) { - if (!callback) { - return; - } - - if (err) { - callback(err); - return; - } + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + let tag = accept ? 'accept' : 'reject'; + + // The official app uses tags reject/accept, but cancel/allow still works so use these for backward compatibility + let body = await request(this, (confID instanceof Array) ? 'multiajaxop' : 'ajaxop', key, time, tag, { + op: accept ? 'allow' : 'cancel', + cid: confID, + ck: confKey + }); if (body.success) { - callback(null); - return; + return resolve(); } - if (body.message) { - callback(new Error(body.message)); - return; - } - - callback(new Error('Could not act on confirmation')); + reject(new Error(body.message || body.detail || 'Could not act on confirmation')); }); }; @@ -151,94 +116,79 @@ SteamCommunity.prototype.respondToConfirmation = function(confID, confKey, time, * Accept a confirmation for a given object (trade offer or market listing) automatically. * @param {string} identitySecret * @param {number|string} objectID - * @param {SteamCommunity~genericErrorCallback} callback + * @param {SteamCommunity~genericErrorCallback} [callback] + * @return Promise */ SteamCommunity.prototype.acceptConfirmationForObject = function(identitySecret, objectID, callback) { this._usedConfTimes = this._usedConfTimes || []; - let doConfirmation = () => { + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + // Figure out our time offset + if (typeof this._timeOffset == 'undefined') { + await new Promise((resolve) => { + SteamTotp.getTimeOffset((err, offset) => { + if (err) { + // not critical that this succeeds + return resolve(); + } + + this._timeOffset = offset; + resolve(); + }); + }); + } + let offset = this._timeOffset; let time = SteamTotp.time(offset); - this.getConfirmations(time, SteamTotp.getConfirmationKey(identitySecret, time, 'conf'), (err, confs) => { - if (err) { - callback(err); - return; - } - - let conf = confs.find(conf => conf.creator == objectID); - if (!conf) { - callback(new Error('Could not find confirmation for object ' + objectID)); - return; - } + let key = SteamTotp.getConfirmationKey(identitySecret, time, 'list'); + let {confirmations} = await this.getConfirmations(time, key); - // make sure we don't reuse the same time - let localOffset = 0; - do { - time = SteamTotp.time(offset) + localOffset++; - } while (this._usedConfTimes.indexOf(time) != -1); - - this._usedConfTimes.push(time); - if (this._usedConfTimes.length > 60) { - this._usedConfTimes.splice(0, this._usedConfTimes.length - 60); // we don't need to save more than 60 entries - } - - conf.respond(time, SteamTotp.getConfirmationKey(identitySecret, time, 'allow'), true, callback); - }); - }; + let conf = confirmations.find(conf => conf.creator == objectID); + if (!conf) { + return reject(new Error(`Could not find confirmation for object ${objectID}`)); + } - if (typeof this._timeOffset !== 'undefined') { - // time offset is already known and saved - doConfirmation(); - } else { - SteamTotp.getTimeOffset((err, offset) => { - if (err) { - callback(err); - return; - } + // make sure we don't reuse the same time + let localOffset = 0; + do { + time = SteamTotp.time(offset) + localOffset++; + } while (this._usedConfTimes.includes(time)); - this._timeOffset = offset; - doConfirmation(); + this._usedConfTimes.push(time); + if (this._usedConfTimes.length > 60) { + this._usedConfTimes.splice(0, this._usedConfTimes.length - 60); // we don't need to save more than 60 entries + } - setTimeout(() => { - // Delete the saved time offset after 12 hours because why not - delete this._timeOffset; - }, 1000 * 60 * 60 * 12).unref(); - }); - } + await conf.respond(time, SteamTotp.getConfirmationKey(identitySecret, time, 'accept'), true); + }); }; /** * Send a single request to Steam to accept all outstanding confirmations (after loading the list). If one fails, the * entire request will fail and there will be no way to know which failed without loading the list again. * @param {number} time - * @param {string} confKey - * @param {string} allowKey - * @param {function} callback + * @param {string} listKey + * @param {string} acceptKey + * @param {function} [callback] + * @return Promise<{confirmations: CConfirmation[]}> */ -SteamCommunity.prototype.acceptAllConfirmations = function(time, confKey, allowKey, callback) { - this.getConfirmations(time, confKey, (err, confs) => { - if (err) { - callback(err); - return; - } +SteamCommunity.prototype.acceptAllConfirmations = function(time, listKey, acceptKey, callback) { + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + let {confirmations} = await this.getConfirmations(time, listKey); - if (confs.length == 0) { - callback(null, []); - return; + if (confirmations.length == 0) { + return resolve({confirmations: []}); } - this.respondToConfirmation(confs.map(conf => conf.id), confs.map(conf => conf.key), time, allowKey, true, (err) => { - if (err) { - callback(err); - return; - } + let confIds = confirmations.map(conf => conf.id); + let confKeys = confirmations.map(conf => conf.key); + await this.respondToConfirmation(confIds, confKeys, time, acceptKey, true); - callback(err, confs); - }); + resolve({confirmations}); }); }; -function request(community, url, key, time, tag, params, json, callback) { +async function request(community, url, key, time, tag, params) { if (!community.steamID) { throw new Error('Must be logged in before trying to do anything with confirmations'); } @@ -253,8 +203,8 @@ function request(community, url, key, time, tag, params, json, callback) { let req = { method: url == 'multiajaxop' ? 'POST' : 'GET', - uri: 'https://steamcommunity.com/mobileconf/' + url, - json: !!json + url: `https://steamcommunity.com/mobileconf/${url}`, + source: 'steamcommunity' }; if (req.method == 'GET') { @@ -263,12 +213,6 @@ function request(community, url, key, time, tag, params, json, callback) { req.form = params; } - community.httpRequest(req, (err, response, body) => { - if (err) { - callback(err); - return; - } - - callback(null, body); - }, 'steamcommunity'); + let result = await community.httpRequest(req); + return result.jsonBody || result.textBody; } diff --git a/components/http.js b/components/http.js index 206c52ac..62ad6623 100644 --- a/components/http.js +++ b/components/http.js @@ -1,4 +1,5 @@ const {HttpResponse} = require('@doctormckay/stdlib/http'); // eslint-disable-line +const {betterPromise} = require('@doctormckay/stdlib/promises'); const SteamCommunity = require('../index.js'); @@ -20,67 +21,50 @@ const SteamCommunity = require('../index.js'); * @return {Promise} */ SteamCommunity.prototype.httpRequest = function(options) { - return new Promise((resolve, reject) => { + return betterPromise(async (resolve, reject) => { let requestID = ++this._httpRequestID; let source = options.source || ''; - let continued = false; - - let continueRequest = async (err) => { - if (continued) { - return; + await betterPromise((resolve, reject) => { + if (!this.onPreHttpRequest || !this.onPreHttpRequest(requestID, source, options, (err) => { + err ? reject(err) : resolve(); + })) { + // No pre-hook, or the pre-hook doesn't want to delay the request. + resolve(); } + }); - continued = true; - - if (err) { - return reject(err); - } + let result = await this._httpClient.request({ + method: options.method, + url: options.url, + queryString: options.qs, + headers: options.headers, + body: options.body, + urlEncodedForm: options.form, + multipartForm: options.multipartForm, + json: options.json, + followRedirects: options.followRedirect + }); - /** @var {HttpResponse} result */ - let result; - - try { - result = await this._httpClient.request({ - method: options.method, - url: options.url, - queryString: options.qs, - headers: options.headers, - body: options.body, - urlEncodedForm: options.form, - multipartForm: options.multipartForm, - json: options.json, - followRedirects: options.followRedirect - }); - } catch (ex) { - return reject(ex); - } + let httpError = options.checkHttpError !== false && this._checkHttpError(result); + let communityError = !options.json && options.checkCommunityError !== false && this._checkCommunityError(result); + let tradeError = !options.json && options.checkTradeError !== false && this._checkTradeError(result); + let jsonError = options.json && options.checkJsonError !== false && !result.jsonBody ? new Error('Malformed JSON response') : null; + + this.emit('postHttpRequest', { + requestID, + source, + options, + response: result, + body: result.textBody, + error: httpError || communityError || tradeError || jsonError || null, + httpError, + communityError, + tradeError, + jsonError + }); - let httpError = options.checkHttpError !== false && this._checkHttpError(result); - let communityError = !options.json && options.checkCommunityError !== false && this._checkCommunityError(result); - let tradeError = !options.json && options.checkTradeError !== false && this._checkTradeError(result); - let jsonError = options.json && options.checkJsonError !== false && !result.jsonBody ? new Error('Malformed JSON response') : null; - - this.emit('postHttpRequest', { - requestID, - source, - options, - response: result, - body: result.textBody, - error: httpError || communityError || tradeError || jsonError || null, - httpError, - communityError, - tradeError, - jsonError - }); - - resolve(result); - }; - - if (!this.onPreHttpRequest || !this.onPreHttpRequest(requestID, source, options, continueRequest)) { - // No pre-hook, or the pre-hook doesn't want to delay the request. - continueRequest(null); - } + resolve(result); }); }; diff --git a/package.json b/package.json index 6d3c0a88..3c830925 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "url": "https://github.com/DoctorMcKay/node-steamcommunity.git" }, "dependencies": { - "@doctormckay/stdlib": "^2.5.0", + "@doctormckay/stdlib": "^2.6.0", "cheerio": "0.22.0", "image-size": "^0.8.2", "request": "^2.88.0", From e9bc778e2aff7bdbf5b57d8e9671cfa65cf24ecf Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 03:45:04 -0400 Subject: [PATCH 60/68] Removed request dependency --- classes/CSteamSharedFile.js | 16 ++++----------- components/helpers.js | 40 ------------------------------------ index.js | 41 +++++++++++++++++++++++++++++++++++++ package.json | 1 - 4 files changed, 45 insertions(+), 53 deletions(-) diff --git a/classes/CSteamSharedFile.js b/classes/CSteamSharedFile.js index 277dbb90..9e1b65c5 100644 --- a/classes/CSteamSharedFile.js +++ b/classes/CSteamSharedFile.js @@ -31,7 +31,7 @@ SteamCommunity.prototype.getSteamSharedFile = function(sharedFileId, callback) { }; // Get DOM of sharedfile - this.httpRequestGet(`https://steamcommunity.com/sharedfiles/filedetails/?id=${sharedFileId}`, (err, res, body) => { + this.httpRequestGet(`https://steamcommunity.com/sharedfiles/filedetails/?id=${sharedFileId}`, async (err, res, body) => { try { /* --------------------- Preprocess output --------------------- */ @@ -136,18 +136,10 @@ SteamCommunity.prototype.getSteamSharedFile = function(sharedFileId, callback) { // Find owner profile link, convert to steamID64 using SteamIdResolver lib and create a SteamID object let ownerHref = $(".friendBlockLinkOverlay").attr()["href"]; - Helpers.resolveVanityURL(ownerHref, (err, data) => { // This request takes <1 sec - if (err) { - callback(err); - return; - } - - sharedfile.owner = new SteamID(data.steamID); - - // Make callback when ID was resolved as otherwise owner will always be null - callback(null, new CSteamSharedFile(this, sharedfile)); - }); + let {steamID} = await this._resolveVanityURL(ownerHref); + sharedfile.owner = steamID; + callback(null, new CSteamSharedFile(this, sharedfile)); } catch (err) { callback(err, null); } diff --git a/components/helpers.js b/components/helpers.js index e4eb5aae..5a975599 100644 --- a/components/helpers.js +++ b/components/helpers.js @@ -1,6 +1,4 @@ const EResult = require('../resources/EResult.js'); -const request = require('request'); -const xml2js = require('xml2js'); /** * Make sure that a provided input is a valid SteamID object. @@ -64,41 +62,3 @@ exports.decodeJwt = function(jwt) { return JSON.parse(Buffer.from(standardBase64, 'base64').toString('utf8')); }; - -/** - * 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[0]; - let vanityURL = parsed.profile.customURL[0]; - - callback(null, {"vanityURL": vanityURL, "steamID": steamID64}); - }); - }); -}; \ No newline at end of file diff --git a/index.js b/index.js index d8fe9fe8..dc52723a 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ const {EventEmitter} = require('events'); const StdLib = require('@doctormckay/stdlib'); const SteamID = require('steamid'); const Util = require('util'); +const xml2js = require('xml2js'); const Helpers = require('./components/helpers.js'); @@ -360,6 +361,46 @@ SteamCommunity.prototype.getFriendsList = function(callback) { }); }; +/** + * @param {string} url + * @return Promise<{vanityURL: string, steamID: SteamID}> + * @private + */ +SteamCommunity.prototype._resolveVanityURL = async function(url) { + // 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 + let {textBody} = await this._httpRequest({ + method: 'GET', + url, + source: 'steamcommunity' + }); + + return await new Promise((resolve, reject) => { + // Parse XML data returned from Steam into an object + new xml2js.Parser().parseString(textBody, (err, parsed) => { + if (err) { + return reject(new Error('Couldn\'t parse XML response')); + } + + if (parsed.response && parsed.response.error) { + return reject(new Error('Couldn\'t find Steam ID')); + } + + let steamID64 = parsed.profile.steamID64[0]; + let vanityURL = parsed.profile.customURL[0]; + + resolve({ + vanityURL, + steamID: new SteamID(steamID64) + }); + }); + }); +}; + require('./components/http.js'); require('./components/profile.js'); require('./components/market.js'); diff --git a/package.json b/package.json index 3c830925..9bc3dca3 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "@doctormckay/stdlib": "^2.6.0", "cheerio": "0.22.0", "image-size": "^0.8.2", - "request": "^2.88.0", "steam-session": "^1.2.4", "steam-totp": "^2.1.0", "steamid": "^2.0.0", From 96e3e7ebc0d3574d74fb265556b1823846c1f632 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 03:50:52 -0400 Subject: [PATCH 61/68] Updated help.js methods to use new http interface --- components/help.js | 84 ++++++++++++++++++++++++---------------------- index.js | 4 +-- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/components/help.js b/components/help.js index c0d6a862..d43caeee 100644 --- a/components/help.js +++ b/components/help.js @@ -1,3 +1,7 @@ +const StdLib = require('@doctormckay/stdlib'); +// eslint-disable-next-line no-unused-vars +const {HttpResponse} = require('@doctormckay/stdlib/http'); + const SteamCommunity = require('../index.js'); const HELP_SITE_DOMAIN = 'https://help.steampowered.com'; @@ -5,18 +9,24 @@ const HELP_SITE_DOMAIN = 'https://help.steampowered.com'; /** * Restore a previously removed steam package from your steam account. * @param {int|string} packageID - * @param {function} callback + * @param {function} [callback] + * @return Promise */ SteamCommunity.prototype.restorePackage = function(packageID, callback) { - this.httpRequestPost({ - uri: HELP_SITE_DOMAIN + '/wizard/AjaxDoPackageRestore', - form: { - packageid: packageID, - sessionid: this.getSessionID(HELP_SITE_DOMAIN), - wizard_ajax: 1 - }, - json: true - }, wizardAjaxHandler(callback)); + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + let result = await this.httpRequest({ + method: 'POST', + url: `${HELP_SITE_DOMAIN}/wizard/AjaxDoPackageRestore`, + form: { + packageid: packageID, + sessionid: this.getSessionID(HELP_SITE_DOMAIN), + wizard_ajax: 1 + }, + source: 'steamcommunity' + }); + + wizardAjaxHandler(result, resolve, reject); + }); }; /** @@ -25,38 +35,32 @@ SteamCommunity.prototype.restorePackage = function(packageID, callback) { * @param {function} callback */ SteamCommunity.prototype.removePackage = function(packageID, callback) { - this.httpRequestPost({ - uri: HELP_SITE_DOMAIN + '/wizard/AjaxDoPackageRemove', - form: { - packageid: packageID, - sessionid: this.getSessionID(HELP_SITE_DOMAIN), - wizard_ajax: 1 - }, - json: true - }, wizardAjaxHandler(callback)); + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + let result = await this.httpRequest({ + method: 'POST', + url: `${HELP_SITE_DOMAIN}/wizard/AjaxDoPackageRemove`, + form: { + packageid: packageID, + sessionid: this.getSessionID(HELP_SITE_DOMAIN), + wizard_ajax: 1 + }, + source: 'steamcommunity' + }); + + wizardAjaxHandler(result, resolve, reject); + }); }; /** - * Returns a handler for wizard ajax HTTP requests. - * @param {function} callback - * @returns {(function(*=, *, *): void)|*} + * + * @param {HttpResponse} result + * @param {function} resolve + * @param {function} reject */ -function wizardAjaxHandler(callback) { - return (err, res, body) => { - if (!callback) { - return; - } - - if (err) { - callback(err); - return; - } - - if (!body.success) { - callback(new Error(body.errorMsg || 'Unexpected error')); - return; - } - - callback(null); - }; +function wizardAjaxHandler(result, resolve, reject) { + if (!result.jsonBody || !result.jsonBody.success) { + return reject(new Error((result.jsonBody || {}).errorMsg || 'Unexpected error')); + } + + resolve(); } diff --git a/index.js b/index.js index dc52723a..8008d906 100644 --- a/index.js +++ b/index.js @@ -132,9 +132,9 @@ SteamCommunity.prototype.setCookies = function(cookies) { this._verifyMobileAccessToken(); }; -SteamCommunity.prototype.getSessionID = function() { +SteamCommunity.prototype.getSessionID = function(domain = 'steamcommunity.com') { let sessionIdCookie = this._jar.cookies - .filter(c => c.domain == 'steamcommunity.com') + .filter(c => c.domain == domain) .find(c => c.name == 'sessionid'); if (sessionIdCookie) { return sessionIdCookie.content; From 3a65768b3fe8b244a075c448bb0d5507b0c6646e Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 03:58:13 -0400 Subject: [PATCH 62/68] Updated sharedfiles.js methods to use new http interface --- components/sharedfiles.js | 191 +++++++++++++++++++------------------- 1 file changed, 98 insertions(+), 93 deletions(-) diff --git a/components/sharedfiles.js b/components/sharedfiles.js index b4fa2c7b..7c0712bb 100644 --- a/components/sharedfiles.js +++ b/components/sharedfiles.js @@ -1,6 +1,7 @@ -var SteamID = require('steamid'); +const StdLib = require('@doctormckay/stdlib'); +const SteamID = require('steamid'); -var SteamCommunity = require('../index.js'); +const SteamCommunity = require('../index.js'); /** @@ -8,50 +9,52 @@ var SteamCommunity = require('../index.js'); * @param {SteamID | String} userID - ID of the user associated to this 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 + * @param {function} [callback] - Takes only an Error object/null as the first argument + * @return Promise */ SteamCommunity.prototype.deleteSharedFileComment = function(userID, sharedFileId, cid, callback) { - if (typeof userID === "string") { + if (typeof userID == 'string') { userID = new SteamID(userID); } - this.httpRequestPost({ - "uri": `https://steamcommunity.com/comment/PublishedFile_Public/delete/${userID.toString()}/${sharedFileId}/`, - "form": { - "gidcomment": cid, - "count": 10, - "sessionid": this.getSessionID() - } - }, function(err, response, body) { - if (!callback) { - return; - } - - callback(err); - }, "steamcommunity"); + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + await this.httpRequest({ + method: 'POST', + url: `https://steamcommunity.com/comment/PublishedFile_Public/delete/${userID.toString()}/${sharedFileId}/`, + form: { + gidcomment: cid, + count: 10, + sessionid: this.getSessionID() + }, + source: 'steamcommunity' + }); + + resolve(); + }); }; /** * Favorites a 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 + * @param {function} [callback] - Takes only an Error object/null as the first argument + * @return Promise */ SteamCommunity.prototype.favoriteSharedFile = function(sharedFileId, appid, callback) { - this.httpRequestPost({ - "uri": "https://steamcommunity.com/sharedfiles/favorite", - "form": { - "id": sharedFileId, - "appid": appid, - "sessionid": this.getSessionID() - } - }, function(err, response, body) { - if (!callback) { - return; - } - - callback(err); - }, "steamcommunity"); + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + await this.httpRequest({ + method: 'POST', + url: 'https://steamcommunity.com/sharedfiles/favorite', + form: { + id: sharedFileId, + appid, + sessionid: this.getSessionID() + }, + source: 'steamcommunity' + }); + + resolve(); + }); }; /** @@ -59,76 +62,79 @@ SteamCommunity.prototype.favoriteSharedFile = function(sharedFileId, appid, call * @param {SteamID | String} userID - ID of the user associated to this 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 + * @param {function} [callback] - Takes only an Error object/null as the first argument + * @return Promise */ SteamCommunity.prototype.postSharedFileComment = function(userID, sharedFileId, message, callback) { - if (typeof userID === "string") { + if (typeof userID == 'string') { userID = new SteamID(userID); } - this.httpRequestPost({ - "uri": `https://steamcommunity.com/comment/PublishedFile_Public/post/${userID.toString()}/${sharedFileId}/`, - "form": { - "comment": message, - "count": 10, - "sessionid": this.getSessionID() - } - }, function(err, response, body) { - if (!callback) { - return; - } - - callback(err); - }, "steamcommunity"); + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + await this.httpRequest({ + method: 'POST', + url: `https://steamcommunity.com/comment/PublishedFile_Public/post/${userID.toString()}/${sharedFileId}/`, + form: { + comment: message, + count: 10, + sessionid: this.getSessionID() + }, + source: 'steamcommunity' + }); + + resolve(); + }); }; /** * 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} sharedFileId ID of the sharedfile - * @param {function} callback - Takes only an Error object/null as the first argument + * @param {function} [callback] - Takes only an Error object/null as the first argument + * @return Promise */ SteamCommunity.prototype.subscribeSharedFileComments = function(userID, sharedFileId, callback) { - if (typeof userID === "string") { + if (typeof userID == 'string') { userID = new SteamID(userID); } - this.httpRequestPost({ - "uri": `https://steamcommunity.com/comment/PublishedFile_Public/subscribe/${userID.toString()}/${sharedFileId}/`, - "form": { - "count": 10, - "sessionid": this.getSessionID() - } - }, function(err, response, body) { // eslint-disable-line - if (!callback) { - return; - } - - callback(err); - }, "steamcommunity"); + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + await this.httpRequest({ + method: 'POST', + url: `https://steamcommunity.com/comment/PublishedFile_Public/subscribe/${userID.toString()}/${sharedFileId}/`, + form: { + count: 10, + sessionid: this.getSessionID() + }, + source: 'steamcommunity' + }); + + resolve(); + }); }; /** * Unfavorites a 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 + * @param {function} [callback] - Takes only an Error object/null as the first argument + * @return Promise */ SteamCommunity.prototype.unfavoriteSharedFile = function(sharedFileId, appid, callback) { - this.httpRequestPost({ - "uri": "https://steamcommunity.com/sharedfiles/unfavorite", - "form": { - "id": sharedFileId, - "appid": appid, - "sessionid": this.getSessionID() - } - }, function(err, response, body) { - if (!callback) { - return; - } - - callback(err); - }, "steamcommunity"); + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + await this.httpRequest({ + method: 'POST', + url: 'https://steamcommunity.com/sharedfiles/unfavorite', + form: { + id: sharedFileId, + appid, + sessionid: this.getSessionID() + }, + source: 'steamcommunity' + }); + + resolve(); + }); }; /** @@ -138,21 +144,20 @@ SteamCommunity.prototype.unfavoriteSharedFile = function(sharedFileId, appid, ca * @param {function} callback - Takes only an Error object/null as the first argument */ SteamCommunity.prototype.unsubscribeSharedFileComments = function(userID, sharedFileId, callback) { - if (typeof userID === "string") { + if (typeof userID === 'string') { userID = new SteamID(userID); } - this.httpRequestPost({ - "uri": `https://steamcommunity.com/comment/PublishedFile_Public/unsubscribe/${userID.toString()}/${sharedFileId}/`, - "form": { - "count": 10, - "sessionid": this.getSessionID() - } - }, function(err, response, body) { // eslint-disable-line - if (!callback) { - return; - } - - callback(err); - }, "steamcommunity"); + return StdLib.Promises.callbackPromise(null, callback, true, async (resolve, reject) => { + await this.httpRequest({ + method: 'POST', + url: `https://steamcommunity.com/comment/PublishedFile_Public/unsubscribe/${userID.toString()}/${sharedFileId}/`, + form: { + count: 10, + sessionid: this.getSessionID() + } + }); + + resolve(); + }); }; From e65d50b5f7e2f20c8d77a802e769c17e3651e23a Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 04:06:40 -0400 Subject: [PATCH 63/68] Updated twofactor.js methods to use new http interface --- components/twofactor.js | 217 +++++++++++++++++++++------------------- 1 file changed, 112 insertions(+), 105 deletions(-) diff --git a/components/twofactor.js b/components/twofactor.js index e18478be..7213261b 100644 --- a/components/twofactor.js +++ b/components/twofactor.js @@ -1,3 +1,4 @@ +const StdLib = require('@doctormckay/stdlib'); const SteamTotp = require('steam-totp'); const SteamCommunity = require('../index.js'); @@ -6,152 +7,158 @@ const Helpers = require('./helpers.js'); const ETwoFactorTokenType = { None: 0, // No token-based two-factor authentication ValveMobileApp: 1, // Tokens generated using Valve's special charset (5 digits, alphanumeric) - ThirdParty: 2 // Tokens generated using literally everyone else's standard charset (6 digits, numeric). This is disabled. + ThirdParty: 2 // Tokens generated using literally everyone else's standard charset (6 digits, numeric). This is disabled on the backend. }; +/** + * @param {function} [callback] + * @return {Promise} + */ SteamCommunity.prototype.enableTwoFactor = function(callback) { - this._verifyMobileAccessToken(); - - if (!this.mobileAccessToken) { - callback(new Error('No mobile access token available. Provide one by calling setMobileAppAccessToken()')); - return; - } - - this.httpRequestPost({ - uri: "https://api.steampowered.com/ITwoFactorService/AddAuthenticator/v1/?access_token=" + this.mobileAccessToken, - // TODO: Send this as protobuf to more closely mimic official app behavior - form: { - steamid: this.steamID.getSteamID64(), - authenticator_time: Math.floor(Date.now() / 1000), - authenticator_type: ETwoFactorTokenType.ValveMobileApp, - device_identifier: SteamTotp.getDeviceID(this.steamID), - sms_phone_id: '1' - }, - json: true - }, (err, response, body) => { - if (err) { - callback(err); - return; + return StdLib.Promises.callbackPromise(null, callback, false, async (resolve, reject) => { + this._verifyMobileAccessToken(); + + if (!this.mobileAccessToken) { + return reject(new Error('No mobile access token available. Provide one by calling setMobileAppAccessToken()')); } - if (!body.response) { - callback(new Error('Malformed response')); - return; + let {jsonBody} = await this.httpRequest({ + method: 'POST', + url: `https://api.steampowered.com/ITwoFactorService/AddAuthenticator/v1/?access_token=${this.mobileAccessToken}`, + // TODO: Send this as protobuf to more closely mimic official app behavior + form: { + steamid: this.steamID.getSteamID64(), + authenticator_time: Math.floor(Date.now() / 1000), + authenticator_type: ETwoFactorTokenType.ValveMobileApp, + device_identifier: SteamTotp.getDeviceID(this.steamID), + sms_phone_id: '1' + }, + source: 'steamcommunity' + }); + + + if (!jsonBody.response) { + return reject(new Error('Malformed response')); } - if (body.response.status != 1) { - var error = new Error('Error ' + body.response.status); - error.eresult = body.response.status; - callback(error); - return; + if (jsonBody.response.status != 1) { + let error = new Error(`Error ${jsonBody.response.status}`); + error.eresult = jsonBody.response.status; + return reject(error); } - callback(null, body.response); - }, 'steamcommunity'); + resolve(jsonBody.response); + }); }; +/** + * @param {string} secret + * @param {string} activationCode + * @param {function} [callback] + * @return Promise + */ SteamCommunity.prototype.finalizeTwoFactor = function(secret, activationCode, callback) { - this._verifyMobileAccessToken(); - - if (!this.mobileAccessToken) { - callback(new Error('No mobile access token available. Provide one by calling setMobileAppAccessToken()')); - return; - } + return StdLib.Promises.callbackPromise(null, callback, false, async (resolve, reject) => { + this._verifyMobileAccessToken(); - let attemptsLeft = 30; - let diff = 0; + if (!this.mobileAccessToken) { + return reject(new Error('No mobile access token available. Provide one by calling setMobileAppAccessToken()')); + } - let finalize = () => { - let code = SteamTotp.generateAuthCode(secret, diff); + let attemptsLeft = 30; + let diff = 0; - this.httpRequestPost({ - uri: 'https://api.steampowered.com/ITwoFactorService/FinalizeAddAuthenticator/v1/?access_token=' + this.mobileAccessToken, - form: { - steamid: this.steamID.getSteamID64(), - authenticator_code: code, - authenticator_time: Math.floor(Date.now() / 1000), - activation_code: activationCode - }, - json: true - }, (err, response, body) => { - if (err) { - callback(err); - return; - } + await new Promise((resolve, reject) => { + SteamTotp.getTimeOffset(function(err, offset, latency) { + if (err) { + return reject(err); + } - if (!body.response) { - callback(new Error('Malformed response')); - return; + diff = offset; + resolve(); + }); + }); + + let finalize = async () => { + let code = SteamTotp.generateAuthCode(secret, diff); + + let {jsonBody} = this.httpRequest({ + method: 'POST', + url: `https://api.steampowered.com/ITwoFactorService/FinalizeAddAuthenticator/v1/?access_token=${this.mobileAccessToken}`, + form: { + steamid: this.steamID.getSteamID64(), + authenticator_code: code, + authenticator_time: Math.floor(Date.now() / 1000), + activation_code: activationCode + }, + source: 'steamcommunity' + }); + + if (!jsonBody.response) { + return reject(new Error('Malformed response')); } - body = body.response; + jsonBody = jsonBody.response; - if (body.server_time) { - diff = body.server_time - Math.floor(Date.now() / 1000); + if (jsonBody.server_time) { + diff = jsonBody.server_time - Math.floor(Date.now() / 1000); } - if (body.status == SteamCommunity.EResult.TwoFactorActivationCodeMismatch) { - callback(new Error('Invalid activation code')); - } else if (body.want_more) { + if (jsonBody.status == SteamCommunity.EResult.TwoFactorActivationCodeMismatch) { + return reject(new Error('Invalid activation code')); + } else if (jsonBody.want_more) { if (--attemptsLeft <= 0) { // We made more than 30 attempts, something must be wrong - return callback(Helpers.eresultError(SteamCommunity.EResult.Fail)); + return reject(Helpers.eresultError(SteamCommunity.EResult.Fail)); } diff += 30; finalize(); - } else if(!body.success) { - callback(new Error('Error ' + body.status)); + } else if (!jsonBody.success) { + return reject(new Error(`Error ${jsonBody.status}`)); } else { - callback(null); + resolve(); } - }, 'steamcommunity'); - } + }; - SteamTotp.getTimeOffset(function(err, offset, latency) { - if (err) { - callback(err); - return; - } - - diff = offset; finalize(); }); }; +/** + * @param {string} revocationCode + * @param {function} [callback] + * @return Promise + */ SteamCommunity.prototype.disableTwoFactor = function(revocationCode, callback) { - this._verifyMobileAccessToken(); - - if (!this.mobileAccessToken) { - callback(new Error('No mobile access token available. Provide one by calling setMobileAppAccessToken()')); - return; - } - - this.httpRequestPost({ - uri: 'https://api.steampowered.com/ITwoFactorService/RemoveAuthenticator/v1/?access_token=' + this.mobileAccessToken, - form: { - steamid: this.steamID.getSteamID64(), - revocation_code: revocationCode, - steamguard_scheme: 1 - }, - json: true - }, function(err, response, body) { - if (err) { - callback(err); + return StdLib.Promises.callbackPromise(null, callback, false, async (resolve, reject) => { + this._verifyMobileAccessToken(); + + if (!this.mobileAccessToken) { + callback(new Error('No mobile access token available. Provide one by calling setMobileAppAccessToken()')); return; } - if (!body.response) { - callback(new Error('Malformed response')); - return; + let {jsonBody} = await this.httpRequest({ + method: 'POST', + url: `https://api.steampowered.com/ITwoFactorService/RemoveAuthenticator/v1/?access_token=${this.mobileAccessToken}`, + form: { + steamid: this.steamID.getSteamID64(), + revocation_code: revocationCode, + steamguard_scheme: 1 + }, + source: 'steamcommunity' + }); + + if (!jsonBody.response) { + return reject(new Error('Malformed response')); } - if (!body.response.success) { - callback(new Error('Request failed')); - return; + if (!jsonBody.response.success) { + return reject(new Error('Request failed')); } // success = true means it worked - callback(null); - }, 'steamcommunity'); + resolve(); + }); }; From 38d3fb39bf2034175d282903d2179943fb97d919 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 04:11:04 -0400 Subject: [PATCH 64/68] Updated webapi.js methods to use new http interface --- components/webapi.js | 70 +++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/components/webapi.js b/components/webapi.js index 13357837..1312f6da 100644 --- a/components/webapi.js +++ b/components/webapi.js @@ -1,48 +1,52 @@ +const StdLib = require('@doctormckay/stdlib'); + const SteamCommunity = require('../index.js'); const Helpers = require('./helpers.js'); +/** + * @param {string} domain + * @param {function} [callback] + * @return Promise<{key: string}> + */ SteamCommunity.prototype.getWebApiKey = function(domain, callback) { - this.httpRequest({ - uri: 'https://steamcommunity.com/dev/apikey?l=english', - followRedirect: false - }, (err, response, body) => { - if (err) { - callback(err); - return; + return StdLib.Promises.callbackPromise(['key'], callback, false, async (resolve, reject) => { + let {textBody} = await this.httpRequest({ + method: 'GET', + url: 'https://steamcommunity.com/dev/apikey?l=english', + followRedirect: false, + source: 'steamcommunity' + }); + + if (textBody.includes('

Access Denied

')) { + return reject(new Error('Access Denied')); } - if (body.includes('

Access Denied

')) { - return callback(new Error('Access Denied')); + if (textBody.includes('You must have a validated email address to create a Steam Web API key.')) { + return reject(new Error('You must have a validated email address to create a Steam Web API key.')); } - if (body.includes('You must have a validated email address to create a Steam Web API key.')) { - return callback(new Error('You must have a validated email address to create a Steam Web API key.')); - } - - let match = body.match(/

Key: ([0-9A-F]+)<\/p>/); + let match = textBody.match(/

Key: ([0-9A-F]+)<\/p>/); if (match) { // We already have an API key registered - callback(null, match[1]); - } else { - // We need to register a new API key - this.httpRequestPost('https://steamcommunity.com/dev/registerkey?l=english', { - form: { - domain, - agreeToTerms: 'agreed', - sessionid: this.getSessionID(), - Submit: 'Register' - } - }, (err, response, body) => { - if (err) { - callback(err); - return; - } - - this.getWebApiKey(domain, callback); - }, 'steamcommunity'); + return resolve({key: match[1]}); } - }, "steamcommunity"); + + // We need to register a new API key + await this.httpRequest({ + method: 'POST', + url: 'https://steamcommunity.com/dev/registerkey?l=english', + form: { + domain, + agreeToTerms: 'agreed', + sessionid: this.getSessionID(), + Submit: 'Register' + }, + source: 'steamcommunity' + }); + + resolve({key: await this.getWebApiKey(domain)}); + }); }; /** From 8e1f214612d2f59740ad88921d8c327e4a072cf0 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 04:27:39 -0400 Subject: [PATCH 65/68] Log into steam using steam-session --- index.js | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 8008d906..fcee9e6c 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ const {EventEmitter} = require('events'); const StdLib = require('@doctormckay/stdlib'); const SteamID = require('steamid'); +const {LoginSession, EAuthTokenPlatformType, EAuthSessionGuardType} = require('steam-session'); const Util = require('util'); const xml2js = require('xml2js'); @@ -63,8 +64,85 @@ function SteamCommunity(options) { this._setCookie('timezoneOffset=0,0'); } +/** + * @param {object} details + * @param {string} details.accountName + * @param {string} details.password + * @param {string} [details.authCode] + * @param {string} [details.twoFactorCode] + * @param {string} [details.authTokenPlatformType] - A value from steam-session's EAuthTokenPlatformType enum. Defaults to MobileApp. + * @return Promise<{cookies: string[], sessionID: string, refreshToken: string}> + */ SteamCommunity.prototype.login = function(details) { - // TODO + if (typeof details.accountName != 'string' || typeof details.password != 'string') { + throw new Error('You must provide your accountName and password to login to steamcommunity.com'); + } + + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + let platformType = details.authTokenPlatformType || EAuthTokenPlatformType.MobileApp; + let session = new LoginSession(platformType); + + session.on('authenticated', async () => { + try { + let cookies = await session.getWebCookies(); + this.setCookies(cookies); + + if (platformType == EAuthTokenPlatformType.MobileApp) { + this.setMobileAppAccessToken(session.accessToken); + } + + // TODO set refresh token for session keep-alive + + let sessionID = this.getSessionID(); + if (!cookies.some(c => c.startsWith('sessionid='))) { + // make sure that the sessionid we return is in the cookies list we return + cookies.push(`sessionid=${sessionID}`); + } + + resolve({ + cookies, + sessionID, + refreshToken: session.refreshToken + }); + } catch (ex) { + reject(ex); + } + }); + + session.on('timeout', () => { + // This really shouldn't happen + reject(new Error('Login attempt timed out')); + }); + session.on('error', reject); + + try { + let startResult = await session.startWithCredentials({ + accountName: details.accountName, + password: details.password, + steamGuardCode: details.twoFactorCode || details.authCode + }); + + if (!startResult.actionRequired) { + return; // 'authenticated' should get emitted soon + } + + session.cancelLoginAttempt(); + + if (startResult.validActions.some(a => a.type == EAuthSessionGuardType.EmailCode)) { + return reject(new Error('SteamGuard')); + } + + if (startResult.validActions.some(a => a.type == EAuthSessionGuardType.DeviceCode)) { + return reject(new Error('SteamGuardMobile')); + } + + let validActions = startResult.validActions.map(a => a.type).join(', '); + return reject(new Error(`Unexpected guard action(s) ${validActions}`)); + } catch (ex) { + reject(ex); + } + }); }; /** From c006526f69889896fe266ac7dcb48bad2f6eebba Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 04:28:28 -0400 Subject: [PATCH 66/68] Pass httpProxy down to steam-session --- index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index fcee9e6c..42cb47ad 100644 --- a/index.js +++ b/index.js @@ -81,7 +81,9 @@ SteamCommunity.prototype.login = function(details) { // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { let platformType = details.authTokenPlatformType || EAuthTokenPlatformType.MobileApp; - let session = new LoginSession(platformType); + let session = new LoginSession(platformType, { + httpProxy: this._options.httpProxy + }); session.on('authenticated', async () => { try { From 25dfa2cdd2d3186161de2d6aeb374985fc78a946 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Tue, 27 Jun 2023 04:31:25 -0400 Subject: [PATCH 67/68] Rename uri to url everywhere --- classes/CMarketItem.js | 4 ++-- classes/CMarketSearchResult.js | 2 +- components/groups.js | 30 +++++++++++++++--------------- components/market.js | 6 +++--- components/profile.js | 4 ++-- components/users.js | 28 ++++++++++++++-------------- index.js | 2 +- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/classes/CMarketItem.js b/classes/CMarketItem.js index 8dd40c54..68654c5b 100644 --- a/classes/CMarketItem.js +++ b/classes/CMarketItem.js @@ -105,7 +105,7 @@ CMarketItem.prototype.updatePriceForCommodity = function(currency, callback) { } this._community.httpRequest({ - uri: 'https://steamcommunity.com/market/itemordershistogram?country=US&language=english¤cy=' + currency + '&item_nameid=' + this.commodityID, + url: 'https://steamcommunity.com/market/itemordershistogram?country=US&language=english¤cy=' + currency + '&item_nameid=' + this.commodityID, json: true }, (err, response, body) => { if (err) { @@ -148,7 +148,7 @@ CMarketItem.prototype.updatePriceForNonCommodity = function(currency, callback) } this._community.httpRequest({ - uri: 'https://steamcommunity.com/market/listings/' + + url: 'https://steamcommunity.com/market/listings/' + this._appid + '/' + encodeURIComponent(this._hashName) + '/render/?query=&start=0&count=10&country=US&language=english¤cy=' + currency, diff --git a/classes/CMarketSearchResult.js b/classes/CMarketSearchResult.js index d0f9e804..ff999546 100644 --- a/classes/CMarketSearchResult.js +++ b/classes/CMarketSearchResult.js @@ -32,7 +32,7 @@ SteamCommunity.prototype.marketSearch = function(options, callback) { let results = []; const performSearch = () => { this.httpRequest({ - uri: 'https://steamcommunity.com/market/search/render/', + url: 'https://steamcommunity.com/market/search/render/', qs: qs, headers: { referer: 'https://steamcommunity.com/market/search' diff --git a/components/groups.js b/components/groups.js index 1eac14f3..098d8dff 100644 --- a/components/groups.js +++ b/components/groups.js @@ -76,7 +76,7 @@ SteamCommunity.prototype.joinGroup = function(gid, callback) { } this.httpRequestPost({ - uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}`, + url: `https://steamcommunity.com/gid/${gid.getSteamID64()}`, form: { action: 'join', sessionID: this.getSessionID() @@ -119,7 +119,7 @@ SteamCommunity.prototype.getAllGroupAnnouncements = function(gid, time, callback } this.httpRequest({ - uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/rss/` + url: `https://steamcommunity.com/gid/${gid.getSteamID64()}/rss/` }, (err, response, body) => { if (err) { callback(err); @@ -175,7 +175,7 @@ SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content } this.httpRequestPost({ - uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/announcements`, + url: `https://steamcommunity.com/gid/${gid.getSteamID64()}/announcements`, form }, (err, response, body) => { if (!callback) { @@ -192,7 +192,7 @@ SteamCommunity.prototype.editGroupAnnouncement = function(gid, aid, headline, co } let submitData = { - uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/announcements`, + url: `https://steamcommunity.com/gid/${gid.getSteamID64()}/announcements`, form: { sessionID: this.getSessionID(), gid: aid, @@ -220,7 +220,7 @@ SteamCommunity.prototype.deleteGroupAnnouncement = function(gid, aid, callback) } let submitData = { - uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/announcements/delete/${aid}?sessionID=${this.getSessionID()}` + url: `https://steamcommunity.com/gid/${gid.getSteamID64()}/announcements/delete/${aid}?sessionID=${this.getSessionID()}` }; this.httpRequestGet(submitData, (err, response, body) => { @@ -284,7 +284,7 @@ SteamCommunity.prototype.scheduleGroupEvent = function(gid, name, type, descript } this.httpRequestPost({ - uri: `https://steamcommunity.com/gid/${gid.toString()}/eventEdit`, + url: `https://steamcommunity.com/gid/${gid.toString()}/eventEdit`, form }, (err, response, body) => { if (!callback) { @@ -348,7 +348,7 @@ SteamCommunity.prototype.editGroupEvent = function(gid, id, name, type, descript } this.httpRequestPost({ - uri: `https://steamcommunity.com/gid/${gid.toString()}/eventEdit`, + url: `https://steamcommunity.com/gid/${gid.toString()}/eventEdit`, form }, (err, response, body) => { if (!callback) { @@ -371,7 +371,7 @@ SteamCommunity.prototype.deleteGroupEvent = function(gid, id, callback) { }; this.httpRequestPost({ - uri: `https://steamcommunity.com/gid/${gid.toString()}/eventEdit`, + url: `https://steamcommunity.com/gid/${gid.toString()}/eventEdit`, form }, (err, response, body) => { if (!callback) { @@ -392,7 +392,7 @@ SteamCommunity.prototype.setGroupPlayerOfTheWeek = function(gid, steamID, callba } this.httpRequestPost({ - uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/potwEdit`, + url: `https://steamcommunity.com/gid/${gid.getSteamID64()}/potwEdit`, form: { xml: 1, action: 'potw', @@ -434,7 +434,7 @@ SteamCommunity.prototype.kickGroupMember = function(gid, steamID, callback) { } this.httpRequestPost({ - uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/membersManage`, + url: `https://steamcommunity.com/gid/${gid.getSteamID64()}/membersManage`, form: { sessionID: this.getSessionID(), action: 'kick', @@ -535,7 +535,7 @@ SteamCommunity.prototype.getAllGroupComments = function(gid, from, count, callba } let options = { - uri: `https://steamcommunity.com/comment/Clan/render/${gid.getSteamID64()}/-1/`, + url: `https://steamcommunity.com/comment/Clan/render/${gid.getSteamID64()}/-1/`, form: { start: from, count @@ -582,7 +582,7 @@ SteamCommunity.prototype.deleteGroupComment = function(gid, cid, callback) { } let options = { - uri: `https://steamcommunity.com/comment/Clan/delete/${gid.getSteamID64()}/-1/`, + url: `https://steamcommunity.com/comment/Clan/delete/${gid.getSteamID64()}/-1/`, form: { sessionid: this.getSessionID(), gidcomment: cid @@ -604,7 +604,7 @@ SteamCommunity.prototype.postGroupComment = function(gid, message, callback) { } let options = { - uri: `https://steamcommunity.com/comment/Clan/post/${gid.getSteamID64()}/-1/`, + url: `https://steamcommunity.com/comment/Clan/post/${gid.getSteamID64()}/-1/`, form: { comment: message, count: 6, @@ -668,7 +668,7 @@ SteamCommunity.prototype.respondToGroupJoinRequests = function(gid, steamIDs, ap let rgAccounts = (!Array.isArray(steamIDs) ? [steamIDs] : steamIDs).map(sid => sid.toString()); this.httpRequestPost({ - uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/joinRequestsManage`, + url: `https://steamcommunity.com/gid/${gid.getSteamID64()}/joinRequestsManage`, form: { rgAccounts: rgAccounts, bapprove: approve ? '1' : '0', @@ -703,7 +703,7 @@ SteamCommunity.prototype.respondToAllGroupJoinRequests = function(gid, approve, } this.httpRequestPost({ - uri: `https://steamcommunity.com/gid/${gid.getSteamID64()}/joinRequestsManage`, + url: `https://steamcommunity.com/gid/${gid.getSteamID64()}/joinRequestsManage`, form: { bapprove: approve ? '1' : '0', json: '1', diff --git a/components/market.js b/components/market.js index 0ede87cb..282bd602 100644 --- a/components/market.js +++ b/components/market.js @@ -221,7 +221,7 @@ SteamCommunity.prototype.createBoosterPack = function(appid, useUntradableGems, } this.httpRequestPost({ - uri: 'https://steamcommunity.com/tradingcards/ajaxcreatebooster/', + url: 'https://steamcommunity.com/tradingcards/ajaxcreatebooster/', form: { sessionid: this.getSessionID(), appid, @@ -267,7 +267,7 @@ SteamCommunity.prototype.createBoosterPack = function(appid, useUntradableGems, */ SteamCommunity.prototype.getGiftDetails = function(giftID, callback) { this.httpRequestPost({ - uri: `https://steamcommunity.com/gifts/${giftID}/validateunpack`, + url: `https://steamcommunity.com/gifts/${giftID}/validateunpack`, form: { sessionid: this.getSessionID() }, @@ -303,7 +303,7 @@ SteamCommunity.prototype.getGiftDetails = function(giftID, callback) { */ SteamCommunity.prototype.redeemGift = function(giftID, callback) { this.httpRequestPost({ - uri: `https://steamcommunity.com/gifts/${giftID}/unpack`, + url: `https://steamcommunity.com/gifts/${giftID}/unpack`, form: { sessionid: this.getSessionID() }, diff --git a/components/profile.js b/components/profile.js index 2cbe7a16..9aeab93c 100644 --- a/components/profile.js +++ b/components/profile.js @@ -304,7 +304,7 @@ SteamCommunity.prototype.uploadAvatar = function(image, format, callback) { } this.httpRequestPost({ - uri: 'https://steamcommunity.com/actions/FileUploader', + url: 'https://steamcommunity.com/actions/FileUploader', formData: { MAX_FILE_SIZE: buffer.length, type: 'player_avatar_image', @@ -350,7 +350,7 @@ SteamCommunity.prototype.uploadAvatar = function(image, format, callback) { doUpload(image); } else if (image.match(/^https?:\/\//)) { this.httpRequestGet({ - uri: image, + url: image, encoding: null }, (err, response, body) => { if (err || response.statusCode != 200) { diff --git a/components/users.js b/components/users.js index 35b70cd8..fe13db77 100644 --- a/components/users.js +++ b/components/users.js @@ -14,7 +14,7 @@ SteamCommunity.prototype.addFriend = function(userID, callback) { } this.httpRequestPost({ - uri: 'https://steamcommunity.com/actions/AddFriendAjax', + url: 'https://steamcommunity.com/actions/AddFriendAjax', form: { accept_invite: 0, sessionID: this.getSessionID(), @@ -44,7 +44,7 @@ SteamCommunity.prototype.acceptFriendRequest = function(userID, callback) { } this.httpRequestPost({ - uri: 'https://steamcommunity.com/actions/AddFriendAjax', + url: 'https://steamcommunity.com/actions/AddFriendAjax', form: { accept_invite: 1, sessionID: this.getSessionID(), @@ -65,7 +65,7 @@ SteamCommunity.prototype.removeFriend = function(userID, callback) { } this.httpRequestPost({ - uri: 'https://steamcommunity.com/actions/RemoveFriendAjax', + url: 'https://steamcommunity.com/actions/RemoveFriendAjax', form: { sessionID: this.getSessionID(), steamid: userID.toString() @@ -85,7 +85,7 @@ SteamCommunity.prototype.blockCommunication = function(userID, callback) { } this.httpRequestPost({ - uri: 'https://steamcommunity.com/actions/BlockUserAjax', + url: 'https://steamcommunity.com/actions/BlockUserAjax', form: { sessionID: this.getSessionID(), steamid: userID.toString() @@ -127,7 +127,7 @@ SteamCommunity.prototype.postUserComment = function(userID, message, callback) { } this.httpRequestPost({ - uri: `https://steamcommunity.com/comment/Profile/post/${userID.toString()}/-1`, + url: `https://steamcommunity.com/comment/Profile/post/${userID.toString()}/-1`, form: { comment: message, count: 1, @@ -163,7 +163,7 @@ SteamCommunity.prototype.deleteUserComment = function(userID, commentID, callbac } this.httpRequestPost({ - uri: `https://steamcommunity.com/comment/Profile/delete/${userID.toString()}/-1`, + url: `https://steamcommunity.com/comment/Profile/delete/${userID.toString()}/-1`, form: { gidcomment: commentID, start: 0, @@ -212,7 +212,7 @@ SteamCommunity.prototype.getUserComments = function(userID, options, callback) { }, options); this.httpRequestPost({ - uri: `https://steamcommunity.com/comment/Profile/render/${userID.toString()}/-1`, + url: `https://steamcommunity.com/comment/Profile/render/${userID.toString()}/-1`, form, json: true }, (err, response, body) => { @@ -259,7 +259,7 @@ SteamCommunity.prototype.inviteUserToGroup = function(userID, groupID, callback) } this.httpRequestPost({ - uri: 'https://steamcommunity.com/actions/GroupInvite', + url: 'https://steamcommunity.com/actions/GroupInvite', form: { group: groupID.toString(), invitee: userID.toString(), @@ -294,7 +294,7 @@ SteamCommunity.prototype.getUserAliases = function(userID, callback) { } this.httpRequestGet({ - uri: `https://steamcommunity.com/profiles/${userID.getSteamID64()}/ajaxaliases`, + url: `https://steamcommunity.com/profiles/${userID.getSteamID64()}/ajaxaliases`, json: true }, (err, response, body) => { if (err) { @@ -423,7 +423,7 @@ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, t const get = (inventory, currency, start) => { this.httpRequest({ - uri: `https://steamcommunity.com${endpoint}/inventory/json/${appID}/${contextID}`, + url: `https://steamcommunity.com${endpoint}/inventory/json/${appID}/${contextID}`, headers: { Referer: `https://steamcommunity.com${endpoint}/inventory` }, @@ -516,7 +516,7 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont const get = (inventory, currency, start) => { this.httpRequest({ - uri: `https://steamcommunity.com/inventory/${userID.getSteamID64()}/${appID}/${contextID}`, + url: `https://steamcommunity.com/inventory/${userID.getSteamID64()}/${appID}/${contextID}`, headers: { Referer: `https://steamcommunity.com/profiles/${userID.getSteamID64()}/inventory` }, @@ -637,7 +637,7 @@ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer, let filename = Date.now() + '_image.' + imageDetails.type; this.httpRequestPost({ - uri: 'https://steamcommunity.com/chat/beginfileupload/?l=english', + url: 'https://steamcommunity.com/chat/beginfileupload/?l=english', headers: { referer: 'https://steamcommunity.com/chat/' }, @@ -688,7 +688,7 @@ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer, }); this.httpRequest({ - uri: uploadUrl, + url: uploadUrl, method: 'PUT', headers, body: imageContentsBuffer @@ -700,7 +700,7 @@ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer, // Now we need to commit the upload this.httpRequestPost({ - uri: 'https://steamcommunity.com/chat/commitfileupload/', + url: 'https://steamcommunity.com/chat/commitfileupload/', headers: { referer: 'https://steamcommunity.com/chat/' }, diff --git a/index.js b/index.js index 42cb47ad..1b45732e 100644 --- a/index.js +++ b/index.js @@ -156,7 +156,7 @@ SteamCommunity.prototype.getClientLogonToken = function(callback) { return StdLib.Promises.callbackPromise(null, callback, false, async (resolve, reject) => { let {jsonBody} = await this.httpRequest({ method: 'GET', - uri: 'https://steamcommunity.com/chat/clientjstoken', + url: 'https://steamcommunity.com/chat/clientjstoken', source: 'steamcommunity' }); From 4ec372b44bfd1525059338df331c9f7293f95cc0 Mon Sep 17 00:00:00 2001 From: Alex Corn Date: Thu, 6 Jul 2023 21:52:31 -0400 Subject: [PATCH 68/68] Added packageName and packageVersion properties --- index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index.js b/index.js index 1b45732e..4915bb3b 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,7 @@ const Util = require('util'); const xml2js = require('xml2js'); const Helpers = require('./components/helpers.js'); +const Package = require('./package.json'); const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'; @@ -31,6 +32,9 @@ SteamCommunity.EFriendRelationship = require('./resources/EFriendRelationship.js function SteamCommunity(options) { options = options || {}; + this.packageName = Package.name; + this.packageVersion = Package.version; + this._jar = new StdLib.HTTP.CookieJar(); this._captchaGid = -1; this._httpRequestID = 0;