diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 00000000..25ea1eb6
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,50 @@
+module.exports = {
+ env: {
+ commonjs: true,
+ es2021: true,
+ node: true
+ },
+ extends: 'eslint:recommended',
+ parserOptions: {
+ ecmaVersion: 12
+ },
+ rules: {
+ // 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: ['error', 'single'],
+ // Always require semicolons
+ semi: ['error', 'always'],
+ // Don't use 'var'
+ 'no-var': 'error',
+ // Only use quotes in object literal keys as needed
+ 'quote-props': ['error', 'as-needed'],
+ // Don't allow trailing spaces after a line
+ 'no-trailing-spaces': 'error',
+ // 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', 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
+ '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.
+ // 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.
+ }
+};
diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml
new file mode 100644
index 00000000..4e6734d0
--- /dev/null
+++ b/.github/workflows/eslint.yml
@@ -0,0 +1,31 @@
+name: ESLint
+
+on: [push, pull_request]
+
+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]
+
+ 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
+ run: npm run lint
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/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 797d04f2..32776d02 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -1,15 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
index 6e6eec11..80e519c3 100644
--- a/.idea/codeStyles/codeStyleConfig.xml
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -3,4 +3,4 @@
-
\ No newline at end of file
+
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 2d5fea61..a69dc013 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -4,8 +4,10 @@
+
+
\ No newline at end of file
diff --git a/classes/CConfirmation.js b/classes/CConfirmation.js
index 81fb17ce..b6e91509 100644
--- a/classes/CConfirmation.js
+++ b/classes/CConfirmation.js
@@ -1,4 +1,6 @@
-var SteamCommunity = require('../index.js');
+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/classes/CEconItem.js b/classes/CEconItem.js
index 4ccfc9b3..5d7dd9ed 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];
}
}
@@ -49,20 +44,18 @@ 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
- 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 +75,7 @@ function CEconItem(item, description, contextID) {
this.cache_expiration = this.item_expiration;
}
- if (this.actions === "") {
+ if (this.actions === '') {
this.actions = [];
}
@@ -94,15 +87,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 +103,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 a23c1b77..68654c5b 100644
--- a/classes/CMarketItem.js
+++ b/classes/CMarketItem.js
@@ -1,33 +1,35 @@
-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") {
+ 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, $);
- item.updatePrice(currency, function(err) {
- if(err) {
+ let item = new CMarketItem(appid, hashName, this, body, $);
+ item.updatePrice(currency, (err) => {
+ if (err) {
callback(err);
} else {
callback(null, item);
}
});
- }, "steamcommunity");
+ }, 'steamcommunity');
};
function CMarketItem(appid, hashName, community, body, $) {
@@ -36,38 +38,36 @@ 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)
- };
- });
- } catch(e) {
+ this.medianSalePrices = this.medianSalePrices.map((item) => ({
+ hour: new Date(item[0]),
+ price: item[1],
+ quantity: parseInt(item[2], 10)
+ }));
+ } catch (e) {
// ignore
}
}
@@ -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 {
@@ -100,90 +100,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) {
+ url: '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");
+CMarketItem.prototype.updatePriceForNonCommodity = function(currency, callback) {
+ 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 + "/" +
+ url: '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 e345b7d9..ff999546 100644
--- a/classes/CMarketSearchResult.js
+++ b/classes/CMarketSearchResult.js
@@ -1,19 +1,20 @@
-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 = {};
+ 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;
}
@@ -28,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({
+ url: 'https://steamcommunity.com/market/search/render/',
+ qs: qs,
+ headers: {
+ referer: 'https://steamcommunity.com/market/search'
},
- "json": true
- }, function(err, response, body) {
+ json: true
+ }, (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 337d2f53..edb2a5c7 100644
--- a/classes/CSteamGroup.js
+++ b/classes/CSteamGroup.js
@@ -1,33 +1,33 @@
-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)) {
- 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) {
+ XML2JS.parseString(body, (err, result) => {
+ 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) {
@@ -49,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;
}
@@ -83,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) {
@@ -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/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/classes/CSteamUser.js b/classes/CSteamUser.js
index f019b77b..191f26ad 100644
--- a/classes/CSteamUser.js
+++ b/classes/CSteamUser.js
@@ -1,52 +1,52 @@
-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)) {
- 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"));
+ XML2JS.parseString(body, (err, result) => {
+ 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) {
@@ -59,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];
}
@@ -68,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 == SteamCommunity.PrivacyState.Public) {
+ let memberSinceValue = processItem('memberSince', '0').replace(/(\d{1,2})(st|nd|th)/, '$1');
if (memberSinceValue.indexOf(',') === -1) {
memberSinceValue += ', ' + new Date().getFullYear();
@@ -91,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]);
@@ -103,7 +102,7 @@ function CSteamUser(community, userData, customurl) {
}
function processItem(name, defaultVal) {
- if(!userData[name]) {
+ if (!userData[name]) {
return defaultVal;
}
@@ -115,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/chat.js b/components/chat.js
deleted file mode 100644
index ae35f6d2..00000000
--- a/components/chat.js
+++ /dev/null
@@ -1,283 +0,0 @@
-var SteamCommunity = require('../index.js');
-var SteamID = require('steamid');
-
-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;
-
- var self = this;
- this.getWebApiOauthToken(function(err, token) {
- if(err) {
- var fatal = err.message.indexOf('not authorized') != -1;
-
- if (!fatal) {
- self.chatState = SteamCommunity.ChatState.LogOnFailed;
- setTimeout(self.chatLogon.bind(self), 5000);
- } else {
- self.chatState = SteamCommunity.ChatState.Offline;
- }
-
- self.emit('chatLogOnFailed', err, fatal);
- self.emit('debug', "Cannot get oauth token: " + err.message);
- return;
- }
-
- self.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);
- 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);
- return;
- }
-
- self._chat = {
- "umqid": body.umqid,
- "message": body.message,
- "accessToken": token,
- "interval": interval,
- "uiMode": uiMode
- };
-
- self.chatFriends = {};
-
- self.chatState = SteamCommunity.ChatState.LoggedOn;
- self.emit('chatLoggedOn');
- self._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';
-
- var self = this;
- 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
- }, function(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() {
- 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);
- } else {
- self.emit('chatLoggedOff');
- clearTimeout(self._chat.timer);
- delete self._chat;
- delete self.chatFriends;
- self.chatState = SteamCommunity.ChatState.Offline;
- }
- }, "steamcommunity");
-};
-
-/**
- * @private
- */
-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,
- "pollid": 1,
- "sectimeout": 20,
- "secidletime": 0,
- "use_accountids": 1,
- "access_token": self._chat.accessToken
- },
- "json": true
- }, function(err, response, body) {
- 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") {
- self._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 && body.error == "Not Logged On") {
- self._relogWebChat();
- }
-
- 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);
- }
- });
- }, "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);
- 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) {
- return; // welp
- }
-
- if(err || response.statusCode != 200) {
- self.emit('debug', 'Chat update persona error: ' + (err ? err.message : "HTTP error " + response.statusCode));
- setTimeout(function() {
- self._chatUpdatePersona(steamID);
- }, 2000);
- return;
- }
-
- var 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
- };
-
- self.emit('chatPersonaState', steamID, persona);
- self.chatFriends[steamID.getSteamID64()] = persona;
- }, "steamcommunity");
-};
diff --git a/components/confirmations.js b/components/confirmations.js
index cdf86924..9adcbd5d 100644
--- a/components/confirmations.js
+++ b/components/confirmations.js
@@ -1,47 +1,34 @@
-var SteamCommunity = require('../index.js');
-var Cheerio = require('cheerio');
-var SteamTotp = require('steam-totp');
-var Async = require('async');
+const Cheerio = require('cheerio');
+const StdLib = require('@doctormckay/stdlib');
+const SteamTotp = require('steam-totp');
-var CConfirmation = require('../classes/CConfirmation.js');
-var EConfirmationType = require('../resources/EConfirmationType.js');
+const SteamCommunity = require('../index.js');
+
+const CConfirmation = require('../classes/CConfirmation.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,14 +41,14 @@ SteamCommunity.prototype.getConfirmations = function(time, key, callback) {
icon: conf.icon || ''
}));
- callback(null, confs);
+ resolve({confirmations: confs});
});
};
/**
* @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
*/
/**
@@ -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'));
}
- var $ = Cheerio.load(body);
- var offer = $('.tradeoffer');
- if(offer.length < 1) {
- callback(null, null);
- return;
+ let $ = Cheerio.load(body);
+ let offer = $('.tradeoffer');
+ if (offer.length < 1) {
+ return resolve({offerID: null});
}
- callback(null, offer.attr('id').split('_')[1]);
+ resolve({offerID: offer.attr('id').split('_')[1]});
});
};
@@ -103,47 +85,30 @@ 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)
* @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,101 +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) {
- 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).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;
- }
+ 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();
+ });
+ });
+ }
- 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;
- }
+ let offset = this._timeOffset;
+ let time = SteamTotp.time(offset);
+ let key = SteamTotp.getConfirmationKey(identitySecret, time, 'list');
+ let {confirmations} = await this.getConfirmations(time, key);
- conf = conf[0];
+ let conf = confirmations.find(conf => conf.creator == objectID);
+ if (!conf) {
+ return reject(new Error(`Could not find confirmation for object ${objectID}`));
+ }
- // make sure we don't reuse the same time
- var localOffset = 0;
- do {
- time = SteamTotp.time(offset) + localOffset++;
- } while (self._usedConfTimes.indexOf(time) != -1);
+ // make sure we don't reuse the same time
+ let localOffset = 0;
+ do {
+ time = SteamTotp.time(offset) + localOffset++;
+ } while (this._usedConfTimes.includes(time));
- 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
+ }
- confKey = SteamTotp.getConfirmationKey(identitySecret, time, 'accept');
- conf.respond(time, {tag: 'accept', key: confKey}, true, callback);
- });
- }
+ 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) {
- var self = this;
-
- this.getConfirmations(time, confKey, function(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: []});
}
- self.respondToConfirmation(confs.map(function(conf) { return conf.id; }), confs.map(function(conf) { return conf.key; }), time, allowKey, true, function(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');
}
@@ -258,10 +201,10 @@ function request(community, url, key, time, tag, params, json, callback) {
params.m = 'react';
params.tag = tag;
- var req = {
+ 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') {
@@ -270,159 +213,6 @@ function request(community, url, key, time, tag, params, json, callback) {
req.form = params;
}
- community.httpRequest(req, function(err, response, body) {
- if (err) {
- callback(err);
- return;
- }
-
- callback(null, body);
- }, 'steamcommunity');
+ let result = await community.httpRequest(req);
+ return result.jsonBody || result.textBody;
}
-
-// 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});
- });
-};
diff --git a/components/groups.js b/components/groups.js
index 9f74f501..098d8dff 100644
--- a/components/groups.js
+++ b/components/groups.js
@@ -1,69 +1,69 @@
-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 || [];
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");
+ 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`;
}
}
}
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) {
@@ -71,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()
+ url: `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) {
+ this._myProfile('home_process', {
+ sessionID: this.getSessionID(),
+ action: 'leaveGroup',
+ groupId: gid.getSteamID64()
+ }, (err, response, body) => {
+ if (!callback) {
return;
}
@@ -111,168 +109,167 @@ 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) {
+ url: `https://steamcommunity.com/gid/${gid.getSteamID64()}/rss/`
+ }, (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]
- }
- }).filter(function(announcement) {
- return (announcement.date > time);
- });
+ 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(announcement => announcement.date > time);
return callback(null, announcements);
});
- }, "steamcommunity");
+ }, 'steamcommunity');
};
SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content, hidden, callback) {
- if(typeof gid === 'string') {
+ if (typeof gid == 'string') {
gid = new SteamID(gid);
}
- if(typeof hidden === 'function') {
+ if (typeof hidden === 'function') {
callback = hidden;
hidden = false;
}
- var self = this;
- var form = {
- "sessionID": this.getSessionID(),
- "action": "post",
- "headline": headline,
- "body": content,
- "languages[0][headline]": headline,
- "languages[0][body]": content
+ let form = {
+ sessionID: this.getSessionID(),
+ action: 'post',
+ headline: headline,
+ body: content,
+ 'languages[0][headline]': headline,
+ 'languages[0][body]': content
};
-
- if(hidden) {
- form.is_hidden = "is_hidden"
+
+ if (hidden) {
+ form.is_hidden = 'is_hidden';
}
-
+
this.httpRequestPost({
- "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/announcements",
+ url: `https://steamcommunity.com/gid/${gid.getSteamID64()}/announcements`,
form
- }, 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 = {
- "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
+ let submitData = {
+ url: `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
}
};
- 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 = {
- "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/announcements/delete/" + aid + "?sessionID=" + this.getSessionID()
+ let submitData = {
+ url: `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 = {
- "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"
+ 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'
};
- if(time === null) {
+ if (time === null) {
form.startDate = 'MM/DD/YY';
form.startHour = '12';
form.startMinute = '00';
@@ -286,20 +283,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) {
+ url: `https://steamcommunity.com/gid/${gid.toString()}/eventEdit`,
+ 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);
}
@@ -307,27 +303,34 @@ 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 = {
- "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"
+ 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'
};
if (time === null) {
@@ -344,155 +347,150 @@ 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) {
+ url: `https://steamcommunity.com/gid/${gid.toString()}/eventEdit`,
+ form
+ }, (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 = {
- "sessionid": this.getSessionID(),
- "action": "deleteEvent",
- "eventID": id
+ 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) {
+ url: `https://steamcommunity.com/gid/${gid.toString()}/eventEdit`,
+ 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": {
- "xml": 1,
- "action": "potw",
- "memberId": steamID.getSteam3RenderedID(),
- "sessionid": this.getSessionID()
- }
- }, function(err, response, body) {
- if(!callback) {
+ url: `https://steamcommunity.com/gid/${gid.getSteamID64()}/potwEdit`,
+ form: {
+ xml: 1,
+ action: 'potw',
+ memberId: steamID.getSteam3RenderedID(),
+ sessionid: this.getSessionID()
+ }
+ }, (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}`));
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": {
- "sessionID": this.getSessionID(),
- "action": "kick",
- "memberId": steamID.getSteamID64(),
- "queryString": ""
- }
- }, function(err, response, body) {
- if(!callback) {
+ url: `https://steamcommunity.com/gid/${gid.getSteamID64()}/membersManage`,
+ form: {
+ sessionID: this.getSessionID(),
+ action: 'kick',
+ memberId: steamID.getSteamID64(),
+ queryString: ''
+ }
+ }, (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;
@@ -501,7 +499,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;
@@ -511,14 +509,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);
}
@@ -528,102 +526,99 @@ 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 = {
- "uri": "https://steamcommunity.com/comment/Clan/render/" + gid.getSteamID64() + "/-1/",
- "form": {
- "start": from,
- "count": count
+ let options = {
+ url: `https://steamcommunity.com/comment/Clan/render/${gid.getSteamID64()}/-1/`,
+ form: {
+ start: from,
+ count
}
};
- 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((i, element) => {
+ let comment = {};
- var $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 $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);
});
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 = {
- "uri": "https://steamcommunity.com/comment/Clan/delete/" + gid.getSteamID64() + "/-1/",
- "form": {
- "sessionid": this.getSessionID(),
- "gidcomment": cid
+ let options = {
+ url: `https://steamcommunity.com/comment/Clan/delete/${gid.getSteamID64()}/-1/`,
+ form: {
+ sessionid: this.getSessionID(),
+ gidcomment: cid
}
};
- 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 = {
- "uri": "https://steamcommunity.com/comment/Clan/post/" + gid.getSteamID64() + "/-1/",
- "form": {
- "comment": message,
- "count": 6,
- "sessionid": this.getSessionID()
+ let options = {
+ url: `https://steamcommunity.com/comment/Clan/post/${gid.getSteamID64()}/-1/`,
+ form: {
+ comment: message,
+ count: 6,
+ sessionid: this.getSessionID()
}
};
- 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');
};
/**
@@ -632,30 +627,30 @@ 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);
}
- 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;
}
- 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++) {
- requests.push(new SteamID("[U:1:" + matches[i].match(/JoinRequests_ApproveDenyUser\(\W*['"](\d+)['"],\W0\W\)/)[1] + "]"));
+ 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');
};
/**
@@ -666,34 +661,34 @@ 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",
- "form": {
- "rgAccounts": rgAccounts,
- "bapprove": approve ? "1" : "0",
- "json": "1",
- "sessionID": this.getSessionID()
+ url: `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) {
- 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');
};
/**
@@ -703,30 +698,30 @@ 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);
}
this.httpRequestPost({
- "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/joinRequestsManage",
- "form": {
- "bapprove": approve ? "1" : "0",
- "json": "1",
- "action": "bulkrespond",
- "sessionID": this.getSessionID()
+ url: `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) {
- 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');
};
diff --git a/components/help.js b/components/help.js
index 81c8614c..d43caeee 100644
--- a/components/help.js
+++ b/components/help.js
@@ -1,24 +1,32 @@
-const SteamCommunity = require('../index.js');
+const StdLib = require('@doctormckay/stdlib');
+// eslint-disable-next-line no-unused-vars
+const {HttpResponse} = require('@doctormckay/stdlib/http');
-const Helpers = require('./helpers.js');
+const SteamCommunity = require('../index.js');
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);
+ });
};
/**
@@ -27,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(body.errorMsg ? new Error(body.errorMsg) : Helpers.eresultError(body.success));
- return;
- }
+function wizardAjaxHandler(result, resolve, reject) {
+ if (!result.jsonBody || !result.jsonBody.success) {
+ return reject(new Error((result.jsonBody || {}).errorMsg || 'Unexpected error'));
+ }
- callback(null);
- };
+ resolve();
}
diff --git a/components/helpers.js b/components/helpers.js
index e887fff5..5a975599 100644
--- a/components/helpers.js
+++ b/components/helpers.js
@@ -1,39 +1,32 @@
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.
+ * @param {object} input
+ * @returns {boolean}
+ */
exports.isSteamID = function(input) {
- var 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;
+ return ['universe', 'type', 'instance', 'accountid'].every(prop => typeof input[prop] == 'number' || typeof input[prop] == 'bigint');
};
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);
}
}
@@ -43,16 +36,17 @@ 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}
*/
-exports.eresultError = function(eresult) {
+exports.eresultError = function(eresult, message) {
if (eresult == EResult.OK) {
// no error
return null;
}
- var err = new Error(EResult[eresult] || ("Error " + eresult));
+ let err = new Error(message || EResult[eresult] || `Error ${eresult}`);
err.eresult = eresult;
return err;
};
@@ -68,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/components/http.js b/components/http.js
index 5be5a46f..62ad6623 100644
--- a/components/http.js
+++ b/components/http.js
@@ -1,132 +1,166 @@
-var 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;
+const {HttpResponse} = require('@doctormckay/stdlib/http'); // eslint-disable-line
+const {betterPromise} = require('@doctormckay/stdlib/promises');
+
+const SteamCommunity = require('../index.js');
+
+/**
+ * @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] - Controls whether the *REQUEST* should be sent as json.
+ * @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 betterPromise(async (resolve, reject) => {
+ let requestID = ++this._httpRequestID;
+ let source = options.source || '';
+
+ 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();
+ }
+ });
- if (this._httpRequestConvenienceMethod) {
- options.method = this._httpRequestConvenienceMethod;
- delete this._httpRequestConvenienceMethod;
- }
+ 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 requestID = ++this._httpRequestID;
- source = source || "";
+ 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
+ });
- var self = this;
- var continued = false;
+ 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);
- }
+/**
+ * @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'
+ });
- function continueRequest(err) {
- if (continued) {
- return;
+ if (result.statusCode != 302) {
+ throw new Error(`HTTP error ${result.statusCode}`);
}
- continued = true;
+ let match = result.headers.location.match(/steamcommunity\.com(\/(id|profiles)\/[^/]+)\/?/);
+ if (!match) {
+ throw new Error('Can\'t get profile URL');
+ }
- if (err) {
- if (callback) {
- callback(err);
- }
+ this._profileURL = match[1];
+ setTimeout(() => {
+ delete this._profileURL; // delete the cache
+ }, 60000).unref();
+ }
- return;
- }
+ let options = endpoint.endpoint ? endpoint : {};
+ options.url = `https://steamcommunity.com${this._profileURL}/${endpoint.endpoint || endpoint}`;
+ options.followRedirect = true;
- 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;
-
- self.emit('postHttpRequest', requestID, source, options, httpError || communityError || tradeError || jsonError || null, response, body, {
- "hasCallback": hasCallback,
- "httpError": httpError,
- "communityError": communityError,
- "tradeError": tradeError,
- "jsonError": jsonError
- });
-
- if (hasCallback && !(httpError || communityError || tradeError)) {
- if (jsonError) {
- callback.call(self, jsonError, response);
- } else {
- callback.apply(self, arguments);
- }
- }
- });
+ if (form) {
+ options.method = 'POST';
+ options.form = form;
+ } else if (!options.method) {
+ options.method = 'GET';
}
-};
-SteamCommunity.prototype.httpRequestGet = function() {
- this._httpRequestConvenienceMethod = "GET";
- return this.httpRequest.apply(this, arguments);
-};
+ options.source = 'steamcommunity';
-SteamCommunity.prototype.httpRequestPost = function() {
- this._httpRequestConvenienceMethod = "POST";
- return this.httpRequest.apply(this, arguments);
+ return await this.httpRequest(options);
};
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) {
- var err;
-
- if(typeof html === 'string' && html.match(/
Sorry!<\/h1>/)) {
- var match = html.match(/
(.+)<\/h3>/);
- err = new Error(match ? match[1] : "Unknown error occurred");
- callback(err);
- return 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>/);
+ 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);
+ if (typeof html == 'string' && html.indexOf('g_steamID = false;') > -1 && html.indexOf('Sign In') > -1) {
+ let err = new Error('Not Logged In');
this._notifySessionExpired(err);
return err;
}
@@ -134,16 +168,21 @@ 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;
}
- var match = html.match(/
\s*([^<]+)\s*<\/div>/);
+ let match = html.match(/
\s*([^<]+)\s*<\/div>/);
if (match) {
- var err = new Error(match[1].trim());
- callback(err);
- return err;
+ return new Error(match[1].trim());
}
return false;
diff --git a/components/inventoryhistory.js b/components/inventoryhistory.js
deleted file mode 100644
index 961d9c19..00000000
--- a/components/inventoryhistory.js
+++ /dev/null
@@ -1,173 +0,0 @@
-var SteamCommunity = require('../index.js');
-var CEconItem = require('../classes/CEconItem.js');
-var Helpers = require('./helpers.js');
-var SteamID = require('steamid');
-var request = require('request');
-var Cheerio = require('cheerio');
-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, Helpers.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");
-};
-
diff --git a/components/market.js b/components/market.js
index 01f3d2e0..282bd602 100644
--- a/components/market.js
+++ b/components/market.js
@@ -1,6 +1,6 @@
-const SteamCommunity = require('../index.js');
const Cheerio = require('cheerio');
+const SteamCommunity = require('../index.js');
const Helpers = require('./helpers.js');
/**
@@ -8,28 +8,27 @@ 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;
}
- 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) {
- 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);
+ $('.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;
+ 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,35 +77,33 @@ 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,33 +114,31 @@ 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;
}
callback(null, body.rgItems);
- })
+ });
};
/**
@@ -228,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,
@@ -254,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;
}
@@ -273,33 +267,31 @@ SteamCommunity.prototype.createBoosterPack = function(appid, useUntradableGems,
*/
SteamCommunity.prototype.getGiftDetails = function(giftID, callback) {
this.httpRequestPost({
- "uri": "https://steamcommunity.com/gifts/" + giftID + "/validateunpack",
- "form": {
- "sessionid": this.getSessionID()
+ url: `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
});
});
};
@@ -311,23 +303,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()
+ url: `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);
diff --git a/components/profile.js b/components/profile.js
index 081a1f07..9aeab93c 100644
--- a/components/profile.js
+++ b/components/profile.js
@@ -1,40 +1,46 @@
const Cheerio = require('cheerio');
const FS = require('fs');
-const SteamID = require('steamid');
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 +49,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,8 +59,8 @@ SteamCommunity.prototype.editProfile = function(settings, callback) {
return;
}
- var values = {
- sessionID: self.getSessionID(),
+ let values = {
+ sessionID: this.getSessionID(),
type: 'profileSave',
weblink_1_title: '',
weblink_1_url: '',
@@ -72,12 +78,8 @@ SteamCommunity.prototype.editProfile = function(settings, callback) {
json: 1
};
- for (var i in settings) {
- if(!settings.hasOwnProperty(i)) {
- continue;
- }
-
- switch(i) {
+ for (let i in settings) {
+ switch (i) {
case 'name':
values.personaName = settings[i];
break;
@@ -131,9 +133,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 +148,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 +172,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,14 +184,10 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) {
// PrivacySettings => {PrivacyProfile, PrivacyInventory, PrivacyInventoryGifts, PrivacyOwnedGames, PrivacyPlaytime}
// eCommentPermission
- var privacy = existingSettings.Privacy.PrivacySettings;
- var commentPermission = existingSettings.Privacy.eCommentPermission;
-
- for (var i in settings) {
- if (!settings.hasOwnProperty(i)) {
- continue;
- }
+ let privacy = existingSettings.Privacy.PrivacySettings;
+ let commentPermission = existingSettings.Privacy.eCommentPermission;
+ for (let i in settings) {
switch (i) {
case 'profile':
privacy.PrivacyProfile = settings[i];
@@ -230,7 +228,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 +237,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;
}
@@ -255,78 +251,34 @@ 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;
}
// 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 +296,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({
+ url: '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({
+ url: 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 +401,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 +413,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 +432,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 +443,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;
}
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();
+ });
};
diff --git a/components/twofactor.js b/components/twofactor.js
index eb5d1058..7213261b 100644
--- a/components/twofactor.js
+++ b/components/twofactor.js
@@ -1,152 +1,164 @@
-var SteamTotp = require('steam-totp');
-var SteamCommunity = require('../index.js');
+const StdLib = require('@doctormckay/stdlib');
+const SteamTotp = require('steam-totp');
-var ETwoFactorTokenType = {
+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.
+ 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