Compare commits

...

42 Commits

Author SHA1 Message Date
Alexander Corn
ec60e1b73e 3.30.2 2016-12-30 17:11:12 -05:00
Alexander Corn
a22a75d4e1 Merge pull request #150 from Vankxr/master
Check if requested inventory is ours.
2016-12-30 17:06:21 -05:00
Vankxr
313753bb05 Check if requested inventory is ours.
Should fix https://github.com/DoctorMcKay/node-steamcommunity/issues/149
2016-12-30 21:24:53 +00:00
Alexander Corn
459fe4d8b1 3.30.1 2016-12-15 00:53:46 -05:00
Alexander Corn
489c0441a5 Restore market_fee_app property to CEconItem for Steam Community items 2016-12-15 00:53:26 -05:00
Alexander Corn
d91e74d02a 3.30.0 2016-12-13 01:53:57 -05:00
Alexander Corn
d120d09970 Fixed tag data to be identical to old endpoint 2016-12-13 01:53:30 -05:00
Alexander Corn
84ce979a78 Fixed new inventory loading not working properly 2016-12-13 01:52:52 -05:00
Alexander Corn
88b0495386 Renamed getUserInventory, added back old one and marked it as deprecated 2016-12-13 01:32:17 -05:00
Alexander Corn
d367e8eae4 Added better paging controls for new inventory endpoint 2016-12-13 01:29:06 -05:00
Alexander Corn
9a3cf927c0 Fixed some more currency detection 2016-12-13 01:28:48 -05:00
Alexander Corn
56cdfff672 3.29.1 2016-12-13 01:07:12 -05:00
Alexander Corn
35e6dd16e7 Re-add "pos" property to inventory items for backwards compatibility 2016-12-13 01:07:00 -05:00
Alexander Corn
d0af8584c1 Send referer when fetching inventories 2016-12-13 00:50:29 -05:00
Alexander Corn
f1afcd1962 3.29.0 2016-12-13 00:46:23 -05:00
Alexander Corn
513b0bff1c Added some optimizations and backwards-compatibility for inv fetching 2016-12-13 00:46:02 -05:00
Alexander Corn
ab037b1931 Pass response body back to caller for inventory fetching 2016-12-13 00:45:38 -05:00
Alexander Corn
06df0ecd42 Handle currencies with new endpoint's data in CEconItem 2016-12-13 00:45:23 -05:00
João Silva
b789e13a70 Update inventory loading endpoint (#148)
* Update inventory loading endpoint

Should fix 429s :)

* Fix missing contextID parameter at CEconItem class

* Revert "Fix missing contextID parameter at CEconItem class"

This reverts commit 416321105b.

* Revert "Revert "Fix missing contextID parameter at CEconItem class""

This reverts commit 49ecd46954.

* Update #1
2016-12-13 00:23:02 -05:00
Alexander Corn
95a36b00c1 Remember time offset for purposes of acceptConfirmationForObject
Closes #147
2016-12-08 17:57:01 -05:00
Alexander Corn
41c87501c4 3.28.0 2016-10-28 19:24:05 -04:00
Alexander Corn
4f3ddcc787 Merge pull request #134 from Aareksio/confirmations
Emit `confirmationAccepted` after accepting the confirmation using `confirmationChecker`
2016-10-27 17:07:41 -04:00
Arkadiusz Sygulski
d10fa9983d Emit confirmationAccepted after accepting the confirmation 2016-10-27 22:30:58 +02:00
Arkadiusz Sygulski
666226731e Remove unused function: handleNewConfirmation 2016-10-27 22:18:31 +02:00
Alexander Corn
d096f47cac 3.27.2 2016-10-10 00:55:36 -04:00
Alexander Corn
a8eedb3067 Make it possible to skip json error check 2016-10-10 00:55:19 -04:00
Alexander Corn
ed12a54e81 3.27.1 2016-10-04 09:16:08 -04:00
Alexander Corn
eb341314ef Merge pull request #130 from Pavelas/master
Delete the profile URL cache after log in.
2016-10-04 09:15:03 -04:00
Pavelas
33cd9474ab Delete the profile URL cache after log in. 2016-10-04 10:26:20 +02:00
Alexander Corn
e8a43734be 3.27.0 2016-09-16 19:30:32 -04:00
Alexander Corn
8a27147f5c Added accountAlerts to getNotifications 2016-09-16 19:30:19 -04:00
Alexander Corn
d00d07f828 Added acceptConfirmationForObject convenience method 2016-09-16 19:03:49 -04:00
Alexander Corn
471c11d406 Merge pull request #128 from SzymonLisowiec/patch-1
Problem when search "Key:" in polish language.
2016-09-16 18:21:17 -04:00
Alexander Corn
dd3a9254aa 3.26.1 2016-09-12 11:22:29 -04:00
Alexander Corn
71a3893636 Fixed cookies not being set for non-community domains after logging in
Also fixed secure flag not working for cookies
2016-09-12 11:22:18 -04:00
Alexander Corn
5928c3b76a 3.26.0 2016-09-12 01:33:30 -04:00
Alexander Corn
1b60712b8b Updated getNotifications to use the new JSON endpoint 2016-09-12 01:33:11 -04:00
Alexander Corn
335a955abd Fixed cookies not being set 2016-09-12 01:24:39 -04:00
Alexander Corn
6d534df8ba Set cookies on all Steam domains, and properly flag as secure 2016-09-12 01:17:00 -04:00
Alexander Corn
9022a9a45e 3.25.0 2016-09-08 19:45:05 -04:00
Alexander Corn
ce4d7ae026 Pull trade offer ID directly from listing page 2016-09-08 19:23:15 -04:00
Kysune
b863377c87 Problem when search "Key:" in polish language. 2016-09-01 21:05:46 +02:00
9 changed files with 347 additions and 112 deletions

View File

@@ -1,17 +1,32 @@
var SteamCommunity = require('../index.js');
module.exports = CConfirmation;
function CConfirmation(community, data) {
Object.defineProperty(this, "_community", {"value": community});
this.id = data.id;
this.type = data.type;
this.creator = data.creator;
this.key = data.key;
this.title = data.title;
this.receiving = data.receiving;
this.time = data.time;
this.icon = data.icon;
this.offerID = this.type == SteamCommunity.ConfirmationType.Trade ? this.creator : null;
}
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;
}
callback(null, this.creator);
return;
}
this._community.getConfirmationOfferID(this.id, time, key, callback);
};

View File

@@ -1,31 +1,40 @@
module.exports = CEconItem;
function CEconItem(item, descriptions, contextID) {
function CEconItem(item, description, contextID) {
var thing;
for(thing in item) {
if(item.hasOwnProperty(thing)) {
for (thing in item) {
if (item.hasOwnProperty(thing)) {
this[thing] = item[thing];
}
}
this.assetid = this.id = (this.id || this.assetid);
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
if (isCurrency) {
this.currencyid = this.id = (this.id || this.currencyid);
} else {
this.assetid = this.id = (this.id || this.assetid);
}
this.instanceid = this.instanceid || '0';
this.amount = parseInt(this.amount, 10);
this.contextid = this.contextid || contextID.toString();
// Merge the description
if(descriptions) {
var description = descriptions[this.classid + '_' + this.instanceid];
if(description) {
for(thing in description) {
if(description.hasOwnProperty(thing)) {
this[thing] = description[thing];
}
if (description) {
// Is this a listing of descriptions?
if (description[this.classid + '_' + this.instanceid]) {
description = description[this.classid + '_' + this.instanceid];
}
for (thing in description) {
if (description.hasOwnProperty(thing)) {
this[thing] = description[thing];
}
}
}
this.is_currency = !!this.is_currency;
this.is_currency = isCurrency;
this.tradable = !!this.tradable;
this.marketable = !!this.marketable;
this.commodity = !!this.commodity;
@@ -34,9 +43,34 @@ function CEconItem(item, descriptions, contextID) {
this.fraudwarnings = this.fraudwarnings || [];
this.descriptions = this.descriptions || [];
if(this.owner && JSON.stringify(this.owner) == '{}') {
if (this.owner && JSON.stringify(this.owner) == '{}') {
this.owner = null;
}
// 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
};
});
}
// 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+)\-/))) {
this.market_fee_app = parseInt(match[1], 10);
}
if (this.actions === "") {
this.actions = [];
}
delete this.currency;
}
CEconItem.prototype.getImageURL = function() {
@@ -52,12 +86,12 @@ CEconItem.prototype.getLargeImageURL = function() {
};
CEconItem.prototype.getTag = function(category) {
if(!this.tags) {
if (!this.tags) {
return null;
}
for(var i = 0; i < this.tags.length; i++) {
if(this.tags[i].category == category) {
for (var i = 0; i < this.tags.length; i++) {
if (this.tags[i].category == category) {
return this.tags[i];
}
}

View File

@@ -158,6 +158,25 @@ CSteamUser.prototype.getInventoryContexts = function(callback) {
this._community.getUserInventoryContexts(this.steamID, callback);
};
/**
* Get the contents of a user's inventory context.
* @deprecated Use CSteamUser#getInventoryContents instead
* @param {int} appID - The Steam application ID of the game for which you want an inventory
* @param {int} contextID - The ID of the "context" within the game you want to retrieve
* @param {boolean} tradableOnly - true to get only tradable items and currencies
* @param callback
*/
CSteamUser.prototype.getInventory = function(appID, contextID, tradableOnly, callback) {
this._community.getUserInventory(this.steamID, appID, contextID, tradableOnly, callback);
};
/**
* Get the contents of a user's inventory context.
* @param {int} appID - The Steam application ID of the game for which you want an inventory
* @param {int} contextID - The ID of the "context" within the game you want to retrieve
* @param {boolean} tradableOnly - true to get only tradable items and currencies
* @param callback
*/
CSteamUser.prototype.getInventoryContents = function(appID, contextID, tradableOnly, callback) {
this._community.getUserInventoryContents(this.steamID, appID, contextID, tradableOnly, callback);
};

View File

@@ -52,6 +52,8 @@ SteamCommunity.prototype.getConfirmations = function(time, key, callback) {
var img = conf.find('.mobileconf_list_entry_icon img');
confs.push(new CConfirmation(self, {
"id": conf.data('confid'),
"type": conf.data('type'),
"creator": conf.data('creator'),
"key": conf.data('key'),
"title": conf.find('.mobileconf_list_entry_description>div:nth-of-type(1)').text().trim(),
"receiving": conf.find('.mobileconf_list_entry_description>div:nth-of-type(2)').text().trim(),
@@ -111,7 +113,7 @@ SteamCommunity.prototype.getConfirmationOfferID = function(confID, time, key, ca
* @param {int|int[]} 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 preceeding time and the tag "allow" (if accepting) or "cancel" (if not accepting)
* @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
*/
@@ -144,6 +146,77 @@ 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
*/
SteamCommunity.prototype.acceptConfirmationForObject = function(identitySecret, objectID, callback) {
var self = this;
this._usedConfTimes = this._usedConfTimes || [];
if (typeof this._timeOffset !== 'undefined') {
// time offset is already known and saved
doConfirmation();
} else {
SteamTotp.getTimeOffset(function(err, offset) {
if (err) {
callback(err);
return;
}
self._timeOffset = offset;
doConfirmation();
setTimeout(function() {
// Delete the saved time offset after 12 hours because why not
delete self._timeOffset;
}, 1000 * 60 * 60 * 12);
});
}
function doConfirmation() {
var offset = self._timeOffset;
var time = SteamTotp.time(offset);
self.getConfirmations(time, SteamTotp.getConfirmationKey(identitySecret, time, "conf"), function(err, confs) {
if (err) {
callback(err);
return;
}
var conf = confs.filter(function(conf) { return conf.creator == objectID; });
if (conf.length == 0) {
callback(new Error("Could not find confirmation for object " + objectID));
return;
}
conf = conf[0];
// make sure we don't reuse the same time
var localOffset = 0;
do {
time = SteamTotp.time(offset) + localOffset++;
} while (self._usedConfTimes.indexOf(time) != -1);
self._usedConfTimes.push(time);
if (self._usedConfTimes.length > 60) {
self._usedConfTimes.splice(0, self._usedConfTimes.length - 60); // we don't need to save more than 60 entries
}
conf.respond(time, SteamTotp.getConfirmationKey(identitySecret, time, "allow"), true, callback);
});
}
};
/**
* 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
*/
SteamCommunity.prototype.acceptAllConfirmations = function(time, confKey, allowKey, callback) {
var self = this;
@@ -256,8 +329,9 @@ SteamCommunity.prototype.checkConfirmations = function() {
// 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() {
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
});
@@ -294,24 +368,13 @@ SteamCommunity.prototype.checkConfirmations = function() {
return; // No new ones
}
// We have new confirmations! Grab a key to get details.
self._confirmationCheckerGetKey('details', function(err, key) {
newOnes.forEach(function(conf) {
self._knownConfirmations[conf.id] = conf; // Add it to our list of known confirmations
if(err) {
self._confirmationQueue.push(conf);
} else {
// Get its offer ID, if we can
conf.getOfferID(key.time, key.key, function(err, offerID) {
conf.offerID = offerID ? offerID : null;
self._confirmationQueue.push(conf);
});
}
});
resetTimer();
// 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();
});
});
@@ -320,28 +383,6 @@ SteamCommunity.prototype.checkConfirmations = function() {
self._confirmationTimer = setTimeout(self.checkConfirmations.bind(self), self._confirmationPollInterval);
}
}
function handleNewConfirmation(conf, handleNumber) {
self._knownConfirmations[conf.id] = conf;
// Delay them by 1 second per new confirmation that we see, so that keys won't be the same.
setTimeout(function() {
if(self._identitySecret) {
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 (err) {
self.emit('debug', "Can't accept confirmation #" + conf.id + ": " + err.message);
}
// We'll just retry next time we poll
delete self._knownConfirmations[conf.id];
});
} else {
self.emit('newConfirmation', conf);
}
}, handleNumber * 1000);
}
};
SteamCommunity.prototype._confirmationCheckerGetKey = function(tag, callback) {

View File

@@ -47,10 +47,10 @@ SteamCommunity.prototype.httpRequest = function(uri, options, callback, source)
self.request(options, function (err, response, body) {
var hasCallback = !!callback;
var httpError = options.checkHttpError !== false && self._checkHttpError(err, response, 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 && !body ? new Error("Malformed JSON response") : null;
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,
@@ -85,29 +85,29 @@ SteamCommunity.prototype._notifySessionExpired = function(err) {
this.emit('sessionExpired', err);
};
SteamCommunity.prototype._checkHttpError = function(err, response, callback) {
SteamCommunity.prototype._checkHttpError = function(err, response, callback, body) {
if (err) {
callback(err);
callback(err, response, body);
return err;
}
if (response.statusCode >= 300 && response.statusCode <= 399 && response.headers.location.indexOf('/login') != -1) {
err = new Error("Not Logged In");
callback(err);
callback(err, response, body);
this._notifySessionExpired(err);
return err;
}
if (response.statusCode == 403 && response.body && response.body.match(/<div id="parental_notice_instructions">Enter your PIN below to exit Family View.<\/div>/)) {
err = new Error("Family View Restricted");
callback(err);
callback(err, response, body);
return err;
}
if (response.statusCode >= 400) {
err = new Error("HTTP error " + response.statusCode);
err.code = response.statusCode;
callback(err);
callback(err, response, body);
return err;
}

View File

@@ -257,10 +257,19 @@ SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) {
}, "steamcommunity");
};
/**
* Get the contents of a user's inventory context.
* @deprecated Use getUserInventoryContents instead
* @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
* @param {int} appID - The Steam application ID of the game for which you want an inventory
* @param {int} contextID - The ID of the "context" within the game you want to retrieve
* @param {boolean} tradableOnly - true to get only tradable items and currencies
* @param {function} callback
*/
SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, tradableOnly, callback) {
var self = this;
if(typeof userID === 'string') {
if (typeof userID === 'string') {
userID = new SteamID(userID);
}
@@ -270,6 +279,9 @@ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, t
function get(inventory, currency, start) {
self.httpRequest({
"uri": "https://steamcommunity.com" + endpoint + "/inventory/json/" + appID + "/" + contextID,
"headers": {
"Referer": "https://steamcommunity.com" + endpoint + "/inventory"
},
"qs": {
"start": start,
"trading": tradableOnly ? 1 : undefined
@@ -281,34 +293,34 @@ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, t
return;
}
if(!body || !body.success || !body.rgInventory || !body.rgDescriptions || !body.rgCurrency) {
if(body) {
if (!body || !body.success || !body.rgInventory || !body.rgDescriptions || !body.rgCurrency) {
if (body) {
callback(new Error(body.Error || "Malformed response"));
} else {
callback(new Error("Malformed response"));
}
return;
}
var i;
for(i in body.rgInventory) {
if(!body.rgInventory.hasOwnProperty(i)) {
for (i in body.rgInventory) {
if (!body.rgInventory.hasOwnProperty(i)) {
continue;
}
inventory.push(new CEconItem(body.rgInventory[i], body.rgDescriptions, contextID));
}
for(i in body.rgCurrency) {
if(!body.rgCurrency.hasOwnProperty(i)) {
for (i in body.rgCurrency) {
if (!body.rgCurrency.hasOwnProperty(i)) {
continue;
}
currency.push(new CEconItem(body.rgInventory[i], body.rgDescriptions, contextID));
}
if(body.more) {
if (body.more) {
var match = response.request.uri.href.match(/\/(profiles|id)\/([^\/]+)\//);
if(match) {
endpoint = "/" + match[1] + "/" + match[2];
@@ -321,3 +333,100 @@ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, t
}, "steamcommunity");
}
};
/**
* Get the contents of a user's inventory context.
* @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
* @param {int} appID - The Steam application ID of the game for which you want an inventory
* @param {int} contextID - The ID of the "context" within the game you want to retrieve
* @param {boolean} tradableOnly - true to get only tradable items and currencies
* @param {function} callback
*/
SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, contextID, tradableOnly, callback) {
var self = this;
if(typeof userID === 'string') {
userID = new SteamID(userID);
}
var pos = 1;
get([], []);
function get(inventory, currency, start) {
self.httpRequest({
"uri": "https://steamcommunity.com/inventory/" + userID.getSteamID64() + "/" + appID + "/" + contextID,
"headers": {
"Referer": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory"
},
"qs": {
"l": "english", // Default language
"count": 5000, // Max items per 'page'
"start_assetid": start
},
"json": true
}, function(err, response, body) {
if (err) {
if (err.message == "HTTP error 403" && body === null) {
// 403 with a body of "null" means the inventory/profile is private.
if(userID.getSteamID64() == self.steamID.getSteamID64()) {
// We can never get private profile error for our own inventory!
self._notifySessionExpired(err);
}
callback(new Error("This profile is private."));
return;
}
callback(err);
return;
}
if (!body || !body.success || !body.assets || !body.descriptions) {
if (body) {
// Dunno if the error/Error property even exists on this new endpoint
callback(new Error(body.error || body.Error || "Malformed response"));
} 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));
}
}
if (body.more_items) {
get(inventory, currency, body.last_assetid);
} else {
callback(null, inventory, currency, body.total_inventory_count);
}
}, "steamcommunity");
}
// A bit of optimization; objects are hash tables so it's more efficient to look up by key than to iterate an array
var quickDescriptionLookup = {};
function getDescription(descriptions, classID, instanceID) {
instanceID = instanceID || '0'; // instanceID can be undefined, in which case it's 0.
var key = classID + '_' + instanceID;
if (quickDescriptionLookup[key]) {
return quickDescriptionLookup[key];
}
for (var i = 0; i < descriptions.length; i++) {
quickDescriptionLookup[key] = descriptions[i];
if (descriptions[i].classid == classID && descriptions[i].instanceid == instanceID) {
return descriptions[i];
}
}
}
};

View File

@@ -3,7 +3,7 @@ var SteamCommunity = require('../index.js');
SteamCommunity.prototype.getWebApiKey = function(domain, callback) {
var self = this;
this.httpRequest({
"uri": "https://steamcommunity.com/dev/apikey",
"uri": "https://steamcommunity.com/dev/apikey?l=english",
"followRedirect": false
}, function(err, response, body) {
if (err) {
@@ -21,7 +21,7 @@ SteamCommunity.prototype.getWebApiKey = function(domain, callback) {
callback(null, match[1]);
} else {
// We need to register a new API key
self.httpRequestPost('https://steamcommunity.com/dev/registerkey', {
self.httpRequestPost('https://steamcommunity.com/dev/registerkey?l=english', {
"form": {
"domain": domain,
"agreeToTerms": "agreed",

View File

@@ -10,6 +10,12 @@ require('util').inherits(SteamCommunity, require('events').EventEmitter);
module.exports = SteamCommunity;
SteamCommunity.SteamID = SteamID;
SteamCommunity.ConfirmationType = {
// 1 is unknown, possibly "Invalid"
"Trade": 2,
"MarketListing": 3
// 4 is opt-out or other like account confirmation?
};
function SteamCommunity(options) {
options = options || {};
@@ -43,20 +49,23 @@ function SteamCommunity(options) {
this.request = this.request.defaults(defaults);
// English
this._jar.setCookie(Request.cookie('Steam_Language=english'), 'https://steamcommunity.com');
this._setCookie(Request.cookie('Steam_Language=english'));
// UTC
this._jar.setCookie(Request.cookie('timezoneOffset=0,0'), 'https://steamcommunity.com');
this._setCookie(Request.cookie('timezoneOffset=0,0'));
}
SteamCommunity.prototype.login = function(details, callback) {
if(details.steamguard) {
var parts = details.steamguard.split('||');
this._jar.setCookie(Request.cookie('steamMachineAuth' + parts[0] + '=' + encodeURIComponent(parts[1])), 'https://steamcommunity.com');
this._setCookie(Request.cookie('steamMachineAuth' + parts[0] + '=' + encodeURIComponent(parts[1])), true);
}
var self = this;
// Delete the cache
delete self._profileURL;
// headers required to convince steam that we're logging in from a mobile device so that we can get the oAuth data
var mobileHeaders = {
"X-Requested-With": "com.valvesoftware.android.steam.community",
@@ -65,8 +74,8 @@ SteamCommunity.prototype.login = function(details, callback) {
"Accept": "text/javascript, text/html, application/xml, text/xml, */*"
};
this._jar.setCookie(Request.cookie("mobileClientVersion=0 (2.1.3)"), "https://steamcommunity.com");
this._jar.setCookie(Request.cookie("mobileClient=android"), "https://steamcommunity.com");
this._setCookie(Request.cookie("mobileClientVersion=0 (2.1.3)"));
this._setCookie(Request.cookie("mobileClient=android"));
this.httpRequestPost("https://steamcommunity.com/login/getrsakey/", {
"form": {"username": details.accountName},
@@ -140,7 +149,7 @@ SteamCommunity.prototype.login = function(details, callback) {
} else {
var sessionID = generateSessionID();
var oAuth = JSON.parse( body.oauth );
self._jar.setCookie(Request.cookie('sessionid=' + sessionID), 'http://steamcommunity.com');
self._setCookie(Request.cookie('sessionid=' + sessionID));
self.steamID = new SteamID(oAuth.steamid);
self.oAuthToken = oAuth.oauth_token;
@@ -158,6 +167,8 @@ SteamCommunity.prototype.login = function(details, callback) {
break;
}
}
self.setCookies(cookies);
callback(null, sessionID, cookies, steamguard, oAuth.oauth_token);
}
@@ -167,11 +178,11 @@ SteamCommunity.prototype.login = function(details, callback) {
function deleteMobileCookies() {
var cookie = Request.cookie('mobileClientVersion=');
cookie.expires = new Date(0);
self._jar.setCookie(cookie, "https://steamcommunity.com");
self._setCookie(cookie);
cookie = Request.cookie('mobileClient=');
cookie.expires = new Date(0);
self._jar.setCookie(cookie, "https://steamcommunity.com");
self._setCookie(cookie);
}
};
@@ -209,6 +220,15 @@ SteamCommunity.prototype.oAuthLogin = function(steamguard, token, callback) {
}, "steamcommunity");
};
SteamCommunity.prototype._setCookie = function(cookie, secure) {
var protocol = secure ? "https" : "http";
cookie.secure = !!secure;
this._jar.setCookie(cookie.clone(), protocol + "://steamcommunity.com");
this._jar.setCookie(cookie.clone(), protocol + "://store.steampowered.com");
this._jar.setCookie(cookie.clone(), protocol + "://help.steampowered.com");
};
SteamCommunity.prototype.setCookies = function(cookies) {
var self = this;
cookies.forEach(function(cookie) {
@@ -216,8 +236,8 @@ SteamCommunity.prototype.setCookies = function(cookies) {
if(cookieName == 'steamLogin') {
self.steamID = new SteamID(cookie.match(/=(\d+)/)[1]);
}
self._jar.setCookie(Request.cookie(cookie), (cookieName.match(/^steamMachineAuth/) || cookieName.match(/Secure$/) ? "https://" : "http://") + "steamcommunity.com");
self._setCookie(Request.cookie(cookie), !!(cookieName.match(/^steamMachineAuth/) || cookieName.match(/Secure$/)));
});
};
@@ -231,7 +251,7 @@ SteamCommunity.prototype.getSessionID = function() {
}
var sessionID = generateSessionID();
this._jar.setCookie(Request.cookie('sessionid=' + sessionID), "http://steamcommunity.com");
this._setCookie(Request.cookie('sessionid=' + sessionID));
return sessionID;
};
@@ -273,37 +293,34 @@ SteamCommunity.prototype.parentalUnlock = function(pin, callback) {
SteamCommunity.prototype.getNotifications = function(callback) {
var self = this;
this.httpRequestGet("https://steamcommunity.com/actions/RefreshNotificationArea", function(err, response, body) {
this.httpRequestGet({
"uri": "https://steamcommunity.com/actions/GetNotificationCounts",
"json": true
}, function(err, response, body) {
if (err) {
callback(err);
return;
}
var notifications = {
"comments": 0,
"items": 0,
"invites": 0,
"gifts": 0,
"chat": 0,
"trades": 0
};
var items = {
"comments": /(\d+) new comments?/,
"items": /(\d+) new items? in your inventory/,
"invites": /(\d+) new invites?/,
"gifts": /(\d+) new gifts?/,
"chat": /(\d+) unread chat messages?/,
"trades": /(\d+) new trade notifications?/
};
var match;
for(var i in items) {
if(match = body.match(items[i])) {
notifications[i] = parseInt(match[1], 10);
}
if (!body || !body.notifications) {
callback(new Error("Malformed response"));
return;
}
var notifications = {
"trades": body.notifications[1] || 0,
"gameTurns": body.notifications[2] || 0,
"moderatorMessages": body.notifications[3] || 0,
"comments": body.notifications[4] || 0,
"items": body.notifications[5] || 0,
"invites": body.notifications[6] || 0,
// dunno about 7
"gifts": body.notifications[8] || 0,
"chat": body.notifications[9] || 0,
"helpRequestReplies": body.notifications[10] || 0,
"accountAlerts": body.notifications[11] || 0
};
callback(null, notifications);
}, "steamcommunity");
};

View File

@@ -1,6 +1,6 @@
{
"name": "steamcommunity",
"version": "3.24.0",
"version": "3.30.2",
"description": "Provides an interface for logging into and interacting with the Steam Community website",
"keywords": ["steam", "steam community"],
"homepage": "https://github.com/DoctorMcKay/node-steamcommunity",