Compare commits

...

16 Commits

Author SHA1 Message Date
Alex Corn
7462e4b767 3.37.0 2018-07-11 00:00:16 -04:00
Alex Corn
adbf1d47a7 Added postProfileStatus and deleteProfileStatus (closes #200) 2018-07-10 23:59:57 -04:00
Alex Corn
8c4fde720a Updated EResult 2018-07-10 23:53:05 -04:00
Alex Corn
458d7b6db1 Renamed getProfileBackground to getUserProfileBackground and added it to CSteamUser 2018-07-10 23:28:26 -04:00
Alex Corn
411d74036f Updated getProfileBackground output a bit 2018-07-10 23:24:41 -04:00
Mert
1b3117b8dc Add getProfileBackground method
Adds ability to get URL of Steamcommunity profile background.
2018-07-10 23:18:03 -04:00
Alex Corn
6eec88d196 3.36.2 2018-05-18 17:32:48 -04:00
Alex Corn
3921ce17d0 Follow redirects when posting to own profile (fixes #178) 2018-05-18 17:32:33 -04:00
Alex Corn
4441d6f6e0 Bumped required request version 2018-05-18 17:29:14 -04:00
Alex Corn
870de6817e 3.36.1 2018-04-15 10:43:14 -04:00
Alex Corn
d5dbb230df Removed extra import 2018-04-15 10:43:05 -04:00
Alex Corn
5e9c06bdc4 3.36.0 2018-04-11 00:27:57 -04:00
Alex Corn
8d163d0d85 Added privacy controls for gameDetails and playtime 2018-04-11 00:27:10 -04:00
Alex Corn
051d376508 3.35.2 2018-04-10 01:08:14 -04:00
Alex Corn
913dff68ef Fixed crashes if somehow market items are really broken (fixes #197) 2018-04-10 01:06:52 -04:00
Alex Corn
754847ee88 Make CMarketItem not broken for non-CSGO apps (fixes #196) 2018-04-10 01:01:03 -04:00
9 changed files with 246 additions and 76 deletions

View File

@@ -12,7 +12,7 @@ SteamCommunity.prototype.getMarketItem = function(appid, hashName, currency, cal
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."));
@@ -35,7 +35,7 @@ function CMarketItem(appid, hashName, community, body, $) {
this._hashName = hashName;
this._community = community;
this._$ = $;
this._country = "US";
var match = body.match(/var g_strCountryCode = "([^"]+)";/);
if(match) {
@@ -47,14 +47,14 @@ function CMarketItem(appid, hashName, community, body, $) {
if(match) {
this._language = match[1];
}
this.commodity = false;
var match = body.match(/Market_LoadOrderSpread\(\s*(\d+)\s*\);/);
match = body.match(/Market_LoadOrderSpread\(\s*(\d+)\s*\);/);
if(match) {
this.commodity = true;
this.commodityID = parseInt(match[1], 10);
}
this.medianSalePrices = null;
match = body.match(/var line1=([^;]+);/);
if(match) {
@@ -78,7 +78,8 @@ function CMarketItem(appid, hashName, community, body, $) {
if (match) {
try {
this.assets = JSON.parse(match[1]);
this.assets = this.assets['730']['2'];
this.assets = this.assets[appid];
this.assets = this.assets[Object.keys(this.assets)[0]];
this.firstAsset = this.assets[Object.keys(this.assets)[0]];
} catch (e) {
// ignore
@@ -112,29 +113,29 @@ CMarketItem.prototype.updatePriceForCommodity = function(currency, callback) {
callback(err);
return;
}
if(body.success != 1) {
if(callback) {
callback(new Error("Error " + body.success));
}
return;
}
var match = (body.sell_order_summary || '').match(/<span class="market_commodity_orders_header_promote">(\d+)<\/span>/);
if(match) {
self.quantity = parseInt(match[1], 10);
}
self.buyQuantity = 0;
match = (body.buy_order_summary || '').match(/<span class="market_commodity_orders_header_promote">(\d+)<\/span>/);
if(match) {
self.buyQuantity = parseInt(match[1], 10);
}
self.lowestPrice = parseInt(body.lowest_sell_order, 10);
self.highestBuyOrder = parseInt(body.highest_buy_order, 10);
// TODO: The tables?
if(callback) {
callback(null);

View File

@@ -3,26 +3,26 @@ var Cheerio = require('cheerio');
SteamCommunity.prototype.marketSearch = function(options, callback) {
var qs = {};
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) {
continue;
}
// This is a tag
qs['category_' + qs.appid + '_' + i + '[]'] = 'tag_' + options[i];
}
}
}
qs.start = 0;
qs.count = 100;
qs.sort_column = 'price';
@@ -80,10 +80,10 @@ SteamCommunity.prototype.marketSearch = function(options, callback) {
function CMarketSearchResult(row) {
var 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);
}

View File

@@ -7,29 +7,29 @@ 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 === '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) {
if (err) {
callback(err);
return;
}
xml2js.parseString(body, function(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) {
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) {
@@ -43,7 +43,7 @@ SteamCommunity.prototype.getSteamUser = function(id, callback) {
callback(new Error("No valid response"));
return;
}
callback(null, new CSteamUser(self, result.profile, customurl));
});
}, "steamcommunity");
@@ -51,7 +51,7 @@ SteamCommunity.prototype.getSteamUser = function(id, callback) {
function CSteamUser(community, userData, customurl) {
this._community = community;
this.steamID = new SteamID(userData.steamID64[0]);
this.name = processItem('steamID');
this.onlineState = processItem('onlineState');
@@ -67,7 +67,7 @@ function CSteamUser(community, userData, customurl) {
this.tradeBanState = processItem('tradeBanState', 'None');
this.isLimitedAccount = processItem('isLimitedAccount') == 1;
this.customURL = processItem('customURL', customurl);
if(this.visibilityState == 3) {
this.memberSince = new Date(processItem('memberSince', '0').replace(/(\d{1,2})(st|nd|th)/, "$1"));
this.location = processItem('location');
@@ -79,19 +79,19 @@ function CSteamUser(community, userData, customurl) {
this.realName = null;
this.summary = null;
}
// Maybe handle mostPlayedGames?
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]);
}
return new SteamID(group.groupID64[0]);
});
}
@@ -110,7 +110,7 @@ CSteamUser.getAvatarURL = function(hash, size, protocol) {
protocol = protocol || 'http://';
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";
@@ -182,3 +182,11 @@ CSteamUser.prototype.getInventory = function(appID, contextID, tradableOnly, cal
CSteamUser.prototype.getInventoryContents = function(appID, contextID, tradableOnly, callback) {
this._community.getUserInventoryContents(this.steamID, appID, contextID, tradableOnly, callback);
};
/**
* Get the background URL of user's profile.
* @param {function} callback
*/
CSteamUser.prototype.getProfileBackground = function(callback) {
this._community.getUserProfileBackground(this.steamID, callback);
};

View File

@@ -1,3 +1,5 @@
const EResult = require('../resources/EResult.js');
exports.isSteamID = function(input) {
var keys = Object.keys(input);
if (keys.length != 4) {
@@ -36,3 +38,19 @@ exports.decodeSteamTime = function(time) {
return date;
};
/**
* Get an Error object for a particular EResult
* @param {int} eresult
* @returns {null|Error}
*/
exports.eresultError = function(eresult) {
if (eresult == EResult.OK) {
// no error
return null;
}
var err = new Error(EResult[eresult] || ("Error " + eresult));
err.eresult = eresult;
return err;
};

View File

@@ -1,7 +1,9 @@
var SteamCommunity = require('../index.js');
var SteamID = require('steamid');
var Cheerio = require('cheerio');
var fs = require('fs');
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,
@@ -10,9 +12,9 @@ SteamCommunity.PrivacyState = {
};
var CommentPrivacyState = {
"1": "commentselfonly",
"2": "commentfriendsonly",
"3": "commentanyone"
"1": 2, // private
"2": 0, // friends only
"3": 1 // anyone
};
SteamCommunity.prototype.setupProfile = function(callback) {
@@ -148,10 +150,9 @@ SteamCommunity.prototype.editProfile = function(settings, callback) {
};
SteamCommunity.prototype.profileSettings = function(settings, callback) {
var self = this;
this._myProfile("edit/settings", null, function(err, response, body) {
if(err || response.statusCode != 200) {
if(callback) {
this._myProfile("edit/settings", null, (err, response, body) => {
if (err || response.statusCode != 200) {
if (callback) {
callback(err || new Error("HTTP error " + response.statusCode));
}
@@ -159,8 +160,8 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) {
}
var $ = Cheerio.load(body);
var form = $('#editForm');
if(!form) {
var existingSettings = $('.ProfileReactRoot[data-privacysettings]').data('privacysettings');
if (!existingSettings) {
if(callback) {
callback(new Error("Malformed response"));
}
@@ -168,46 +169,71 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) {
return;
}
var values = {};
form.serializeArray().forEach(function(item) {
values[item.name] = item.value;
});
// PrivacySettings => {PrivacyProfile, PrivacyInventory, PrivacyInventoryGifts, PrivacyOwnedGames, PrivacyPlaytime}
// eCommentPermission
var privacy = existingSettings.PrivacySettings;
var commentPermission = existingSettings.eCommentPermission;
for(var i in settings) {
if(!settings.hasOwnProperty(i)) {
for (var i in settings) {
if (!settings.hasOwnProperty(i)) {
continue;
}
switch(i) {
switch (i) {
case 'profile':
values.privacySetting = settings[i];
privacy.PrivacyProfile = settings[i];
break;
case 'comments':
values.commentSetting = CommentPrivacyState[settings[i]];
commentPermission = CommentPrivacyState[settings[i]];
break;
case 'inventory':
values.inventoryPrivacySetting = settings[i];
privacy.PrivacyInventory = settings[i];
break;
case 'inventoryGifts':
values.inventoryGiftPrivacy = settings[i] ? 1 : 0;
privacy.PrivacyInventoryGifts = settings[i] ? SteamCommunity.PrivacyState.Private : SteamCommunity.PrivacyState.Public;
break;
case 'gameDetails':
privacy.PrivacyOwnedGames = settings[i];
break;
case 'playtime':
privacy.PrivacyPlaytime = settings[i] ? SteamCommunity.PrivacyState.Private : SteamCommunity.PrivacyState.Public;
break;
}
}
self._myProfile("edit/settings", values, function(err, response, body) {
if(err || response.statusCode != 200) {
if(callback) {
this._myProfile({
"method": "POST",
"endpoint": "ajaxsetprivacy/",
"json": true,
"formData": { // it's multipart because lolvalve
"sessionid": this.getSessionID(),
"Privacy": JSON.stringify(privacy),
"eCommentPermission": commentPermission
}
}, null, function(err, response, body) {
if (err || response.statusCode != 200) {
if (callback) {
callback(err || new Error("HTTP error " + response.statusCode));
}
return;
}
if(callback) {
callback(null);
if (body.success != 1) {
if (callback) {
callback(new Error(body.success ? "Error " + body.success : "Request was not successful"));
}
return;
}
if (callback) {
callback(null, body.Privacy);
}
});
});
@@ -256,7 +282,7 @@ SteamCommunity.prototype.uploadAvatar = function(image, format, callback) {
}
}
fs.readFile(image, function(err, file) {
FS.readFile(image, function(err, file) {
if(err) {
if(callback) {
callback(err);
@@ -367,3 +393,68 @@ SteamCommunity.prototype.uploadAvatar = function(image, format, callback) {
}, "steamcommunity");
}
};
/**
* Post a new status to your profile activity feed.
* @param {string} statusText - The text of this status update
* @param {{appID: int}} [options] - Options for this status update. All are optional. If you don't pass any options, this can be omitted.
* @param {function} callback - err, postID
*/
SteamCommunity.prototype.postProfileStatus = function(statusText, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
this._myProfile("ajaxpostuserstatus/", {
"appid": options.appID || 0,
"sessionid": this.getSessionID(),
"status_text": statusText
}, (err, res, body) => {
try {
body = JSON.parse(body);
if (body.message) {
callback(new Error(body.message));
return;
}
var match = body.blotter_html.match(/id="userstatus_(\d+)_/);
if (!match) {
callback(new Error("Malformed response"));
return;
}
callback(null, parseInt(match[1], 10));
} catch (ex) {
callback(ex);
}
});
};
/**
* Delete a previously-posted profile status update.
* @param {int} postID
* @param {function} [callback]
*/
SteamCommunity.prototype.deleteProfileStatus = function(postID, callback) {
this._myProfile("ajaxdeleteuserstatus/", {
"sessionid": this.getSessionID(),
"postid": postID
}, (err, res, body) => {
if (!callback) {
return;
}
try {
body = JSON.parse(body);
if (!body.success) {
callback(new Error("Malformed response"));
return;
}
callback(Helpers.eresultError(body.success));
} catch (ex) {
callback(ex);
}
});
};

View File

@@ -1,7 +1,10 @@
var SteamCommunity = require('../index.js');
var SteamID = require('steamid');
var CEconItem = require('../classes/CEconItem.js');
var Helpers = require('./helpers.js');
const Cheerio = require('cheerio');
const SteamID = require('steamid');
const SteamCommunity = require('../index.js');
const CEconItem = require('../classes/CEconItem.js');
const Helpers = require('./helpers.js');
SteamCommunity.prototype.addFriend = function(userID, callback) {
if(typeof userID === 'string') {
@@ -217,6 +220,45 @@ SteamCommunity.prototype.getUserAliases = function(userID, callback) {
}, "steamcommunity");
};
/**
* Get the background URL of user's profile.
* @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
* @param {function} callback
*/
SteamCommunity.prototype.getUserProfileBackground = function(userID, callback) {
if (typeof userID === 'string') {
userID = new SteamID(userID);
}
this.httpRequest("https://steamcommunity.com/profiles/" + userID.getSteamID64(), (err, response, body) => {
if (err) {
callback(err);
return;
}
var $ = Cheerio.load(body);
var $privateProfileInfo = $('.profile_private_info');
if ($privateProfileInfo.length > 0) {
callback(new Error($privateProfileInfo.text().trim()));
return;
}
if ($('body').hasClass('has_profile_background')) {
var backgroundUrl = $('div.profile_background_image_content').css('background-image');
var matcher = backgroundUrl.match(/\(([^)]+)\)/);
if (matcher.length != 2 || !matcher[1].length) {
callback(new Error("Malformed response"));
} else {
callback(null, matcher[1]);
}
} else {
callback(null, null);
}
}, "steamcommunity");
};
SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) {
if (typeof userID === 'string') {
userID = new SteamID(userID);
@@ -389,7 +431,7 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont
// We can never get private profile error for our own inventory!
self._notifySessionExpired(err);
}
callback(new Error("This profile is private."));
return;
}
@@ -423,13 +465,13 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont
} else {
callback(new Error("Malformed response"));
}
return;
}
for (var i = 0; i < body.assets.length; i++) {
var description = getDescription(body.descriptions, body.assets[i].classid, body.assets[i].instanceid);
if (!tradableOnly || (description && description.tradable)) {
body.assets[i].pos = pos++;
(body.assets[i].currencyid ? currency : inventory).push(new CEconItem(body.assets[i], description, contextID));
@@ -457,7 +499,7 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont
for (var i = 0; i < descriptions.length; i++) {
quickDescriptionLookup[descriptions[i].classid + '_' + (descriptions[i].instanceid || '0')] = descriptions[i];
}
return quickDescriptionLookup[key];
}
};

View File

@@ -1,9 +1,9 @@
require('@doctormckay/stats-reporter').setup(require('./package.json'));
var Request = require('request');
var RSA = require('node-bignumber').Key;
var hex2b64 = require('node-bignumber').hex2b64;
var SteamID = require('steamid');
const hex2b64 = require('node-bignumber').hex2b64;
const Request = require('request');
const RSA = require('node-bignumber').Key;
const SteamID = require('steamid');
const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36";
@@ -460,11 +460,13 @@ SteamCommunity.prototype._myProfile = function(endpoint, form, callback) {
function completeRequest(url) {
var options = endpoint.endpoint ? endpoint : {};
options.uri = "https://steamcommunity.com" + url + "/" + (endpoint.endpoint || endpoint);
options.method = "GET";
if (form) {
options.method = "POST";
options.form = form;
options.followAllRedirects = true;
} else if (!options.method) {
options.method = "GET";
}
self.httpRequest(options, callback, "steamcommunity");

View File

@@ -1,6 +1,6 @@
{
"name": "steamcommunity",
"version": "3.35.1",
"version": "3.37.0",
"description": "Provides an interface for logging into and interacting with the Steam Community website",
"keywords": [
"steam",
@@ -22,7 +22,7 @@
},
"dependencies": {
"@doctormckay/stats-reporter": "^1.0.2",
"request": "^2.81.0",
"request": "^2.86.0",
"node-bignumber": "^1.2.1",
"steamid": "^1.0.0",
"xml2js": "^0.4.11",

View File

@@ -119,6 +119,10 @@ module.exports = {
"GSLTExpired": 106,
"InsufficientFunds": 107,
"TooManyPending": 108,
"NoSiteLicensesFound": 109,
"WGNetworkSendExceeded": 110,
"AccountNotFriends": 111,
"LimitedUserAccount": 112,
// Value-to-name mapping for convenience
"0": "Invalid",
@@ -229,4 +233,8 @@ module.exports = {
"106": "GSLTExpired",
"107": "InsufficientFunds",
"108": "TooManyPending",
"109": "NoSiteLicensesFound",
"110": "WGNetworkSendExceeded",
"111": "AccountNotFriends",
"112": "LimitedUserAccount",
};