mirror of
https://github.com/DoctorMcKay/node-steamcommunity.git
synced 2026-08-19 21:23:28 +08:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9ec1ccfb5 | ||
|
|
0c7c030f78 | ||
|
|
e5f1f14aad | ||
|
|
0f72ea7d93 | ||
|
|
d1906a2c83 | ||
|
|
3964af7ade | ||
|
|
5a5c5373aa | ||
|
|
ec60e1b73e | ||
|
|
a22a75d4e1 | ||
|
|
313753bb05 | ||
|
|
459fe4d8b1 | ||
|
|
489c0441a5 | ||
|
|
d91e74d02a | ||
|
|
d120d09970 | ||
|
|
84ce979a78 | ||
|
|
88b0495386 | ||
|
|
d367e8eae4 | ||
|
|
9a3cf927c0 | ||
|
|
56cdfff672 | ||
|
|
35e6dd16e7 | ||
|
|
d0af8584c1 | ||
|
|
f1afcd1962 | ||
|
|
513b0bff1c | ||
|
|
ab037b1931 | ||
|
|
06df0ecd42 | ||
|
|
b789e13a70 | ||
|
|
95a36b00c1 | ||
|
|
41c87501c4 | ||
|
|
4f3ddcc787 | ||
|
|
d10fa9983d | ||
|
|
666226731e | ||
|
|
d096f47cac | ||
|
|
a8eedb3067 | ||
|
|
ed12a54e81 | ||
|
|
eb341314ef | ||
|
|
33cd9474ab | ||
|
|
e8a43734be | ||
|
|
8a27147f5c | ||
|
|
d00d07f828 | ||
|
|
471c11d406 | ||
|
|
dd3a9254aa | ||
|
|
71a3893636 | ||
|
|
5928c3b76a | ||
|
|
1b60712b8b | ||
|
|
335a955abd | ||
|
|
6d534df8ba | ||
|
|
9022a9a45e | ||
|
|
ce4d7ae026 | ||
|
|
b863377c87 | ||
|
|
20785ee92b | ||
|
|
3afb393f58 | ||
|
|
79543187f0 | ||
|
|
3139aec463 | ||
|
|
bb917af86e | ||
|
|
17f51b9caa | ||
|
|
dd638df118 | ||
|
|
b937a05442 | ||
|
|
9c34d18cd9 | ||
|
|
02e3798124 | ||
|
|
5d9922cc33 | ||
|
|
153011e1c9 | ||
|
|
575c196cb7 | ||
|
|
47a24d19cf | ||
|
|
0eac19b71c | ||
|
|
ca73ecc786 | ||
|
|
e8733613d7 | ||
|
|
89a1df67ec | ||
|
|
3ba0a8592e | ||
|
|
57fa7a679f | ||
|
|
446e315183 | ||
|
|
c6a9b74471 | ||
|
|
018e18cb5d |
@@ -1,17 +1,32 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
|
||||
module.exports = CConfirmation;
|
||||
|
||||
function CConfirmation(community, data) {
|
||||
this._community = community;
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,8 +57,9 @@ SteamCommunity.prototype.marketSearch = function(options, callback) {
|
||||
}
|
||||
|
||||
var $ = Cheerio.load(body.results_html);
|
||||
if($('.market_listing_table_message').length > 0) {
|
||||
callback(new Error($('.market_listing_table_message').text()));
|
||||
var $errorMsg = $('.market_listing_table_message');
|
||||
if($errorMsg.length > 0) {
|
||||
callback(new Error($errorMsg.text()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -78,11 +79,11 @@ SteamCommunity.prototype.marketSearch = function(options, callback) {
|
||||
};
|
||||
|
||||
function CMarketSearchResult(row) {
|
||||
var match = row.attr('href').match(/\/market\/listings\/(\d+)\/(.+)/);
|
||||
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.price = parseInt(row.find('.market_listing_their_price .market_table_value span').text().replace(/[^\d]+/g, ''), 10);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -108,6 +108,8 @@ function CSteamUser(community, userData, customurl) {
|
||||
CSteamUser.getAvatarURL = function(hash, size, protocol) {
|
||||
size = size || '';
|
||||
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') {
|
||||
@@ -150,10 +152,33 @@ CSteamUser.prototype.inviteToGroup = function(groupID, callback) {
|
||||
this._community.inviteUserToGroup(this.steamID, groupID, callback);
|
||||
};
|
||||
|
||||
CSteamUser.prototype.getAliases = function(callback) {
|
||||
this._community.getUserAliases(this.steamID, callback);
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
@@ -84,7 +84,8 @@ SteamCommunity.prototype.chatLogon = function(interval, uiMode) {
|
||||
"umqid": body.umqid,
|
||||
"message": body.message,
|
||||
"accessToken": token,
|
||||
"interval": interval
|
||||
"interval": interval,
|
||||
"uiMode": uiMode
|
||||
};
|
||||
|
||||
self.chatFriends = {};
|
||||
@@ -188,11 +189,19 @@ SteamCommunity.prototype._chatPoll = function() {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -225,6 +234,13 @@ SteamCommunity.prototype._chatPoll = function() {
|
||||
}, "steamcommunity");
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
SteamCommunity.prototype._chatUpdatePersona = function(steamID) {
|
||||
this.emit('debug', 'Updating persona data for ' + steamID);
|
||||
var self = this;
|
||||
|
||||
@@ -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(),
|
||||
@@ -108,15 +110,15 @@ SteamCommunity.prototype.getConfirmationOfferID = function(confID, time, key, ca
|
||||
|
||||
/**
|
||||
* Confirm or cancel a given confirmation.
|
||||
* @param {int} confID - The ID of the confirmation in question
|
||||
* @param {string} confKey - The confirmation key associated with the confirmation in question (not a TOTP key, the `key` property of CConfirmation)
|
||||
* @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
|
||||
*/
|
||||
SteamCommunity.prototype.respondToConfirmation = function(confID, confKey, time, key, accept, callback) {
|
||||
request(this, "ajaxop", key, time, accept ? "allow" : "cancel", {
|
||||
request(this, (confID instanceof Array) ? "multiajaxop" : "ajaxop", key, time, accept ? "allow" : "cancel", {
|
||||
"op": accept ? "allow" : "cancel",
|
||||
"cid": confID,
|
||||
"ck": confKey
|
||||
@@ -144,6 +146,102 @@ 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;
|
||||
|
||||
this.getConfirmations(time, confKey, function(err, confs) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (confs.length == 0) {
|
||||
callback(null, []);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
callback(err, confs);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function request(community, url, key, time, tag, params, json, callback) {
|
||||
params = params || {};
|
||||
params.p = SteamTotp.getDeviceID(community.steamID);
|
||||
@@ -153,11 +251,19 @@ function request(community, url, key, time, tag, params, json, callback) {
|
||||
params.m = "android";
|
||||
params.tag = tag;
|
||||
|
||||
community.httpRequestGet({
|
||||
var req = {
|
||||
"method": url == 'multiajaxop' ? 'POST' : 'GET',
|
||||
"uri": "https://steamcommunity.com/mobileconf/" + url,
|
||||
"qs": params,
|
||||
"json": !!json
|
||||
}, function(err, response, body) {
|
||||
};
|
||||
|
||||
if (req.method == "GET") {
|
||||
req.qs = params;
|
||||
} else {
|
||||
req.form = params;
|
||||
}
|
||||
|
||||
community.httpRequest(req, function(err, response, body) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
@@ -223,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
|
||||
});
|
||||
@@ -261,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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -287,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) {
|
||||
|
||||
@@ -11,3 +11,13 @@ exports.isSteamID = function(input) {
|
||||
|
||||
return keys.length == 4;
|
||||
};
|
||||
|
||||
exports.decodeSteamTime = function(time) {
|
||||
var parts = time.split('@');
|
||||
if (!parts[0].match(/,/)) {
|
||||
// no year, assume current year
|
||||
parts[0] += ", " + (new Date()).getFullYear();
|
||||
}
|
||||
|
||||
return new Date(parts.join('@').replace(/(am|pm)/, ' $1') + " UTC"); // add a space so JS can decode it
|
||||
};
|
||||
|
||||
@@ -47,19 +47,25 @@ 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 && options.checkJsonError !== false && !body ? new Error("Malformed JSON response") : null;
|
||||
|
||||
self.emit('postHttpRequest', requestID, source, options, httpError || communityError || tradeError || null, response, body, {
|
||||
self.emit('postHttpRequest', requestID, source, options, httpError || communityError || tradeError || jsonError || null, response, body, {
|
||||
"hasCallback": hasCallback,
|
||||
"httpError": httpError,
|
||||
"communityError": communityError,
|
||||
"tradeError": tradeError
|
||||
"tradeError": tradeError,
|
||||
"jsonError": jsonError
|
||||
});
|
||||
|
||||
if(hasCallback && !(httpError || communityError || tradeError)) {
|
||||
callback.apply(self, arguments);
|
||||
if (hasCallback && !(httpError || communityError || tradeError)) {
|
||||
if (jsonError) {
|
||||
callback.call(self, jsonError, response);
|
||||
} else {
|
||||
callback.apply(self, arguments);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -79,23 +85,29 @@ SteamCommunity.prototype._notifySessionExpired = function(err) {
|
||||
this.emit('sessionExpired', err);
|
||||
};
|
||||
|
||||
SteamCommunity.prototype._checkHttpError = function(err, response, callback) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
SteamCommunity.prototype._checkHttpError = function(err, response, callback, body) {
|
||||
if (err) {
|
||||
callback(err, response, body);
|
||||
return err;
|
||||
}
|
||||
|
||||
if(response.statusCode >= 300 && response.statusCode <= 399 && response.headers.location.indexOf('/login') != -1) {
|
||||
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 >= 400) {
|
||||
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, 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,118 +5,158 @@ 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)
|
||||
*/
|
||||
|
||||
SteamCommunity.prototype.getInventoryHistory = function(options, callback) {
|
||||
if(typeof options === 'function') {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.page = options.page || 1;
|
||||
|
||||
this.httpRequest("https://steamcommunity.com/my/inventoryhistory?l=english&p=" + options.page, function(err, response, body) {
|
||||
if(err) {
|
||||
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);
|
||||
var html = $('.inventory_history_pagingrow').html();
|
||||
if(!html) {
|
||||
callback("Malformed page: no paging row found");
|
||||
if (!$('.inventory_history_pagingrow').html()) {
|
||||
callback(new Error("Malformed page: no paging row found"));
|
||||
return;
|
||||
}
|
||||
|
||||
var match = html.match(/(\d+) - (\d+) of (\d+) History Items/);
|
||||
|
||||
output.first = parseInt(match[1], 10);
|
||||
output.last = parseInt(match[2], 10);
|
||||
output.totalTrades = parseInt(match[3], 10);
|
||||
|
||||
|
||||
// Load the inventory item data
|
||||
var match2 = body.match(/var g_rgHistoryInventory = (.*);/);
|
||||
if(!match2) {
|
||||
if (!match2) {
|
||||
callback(new Error("Malformed page: no trade found"));
|
||||
return;
|
||||
}
|
||||
var historyInventory = JSON.parse(match2[1]);
|
||||
|
||||
|
||||
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(var i = 0; i < trades.length; i++) {
|
||||
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') {
|
||||
if (timeMatch[1] == 12 && timeMatch[3] == 'am') {
|
||||
timeMatch[1] = 0;
|
||||
}
|
||||
|
||||
if(timeMatch[1] < 12 && timeMatch[3] == 'pm') {
|
||||
|
||||
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);
|
||||
|
||||
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) {
|
||||
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) {
|
||||
if (options.resolveVanityURLs && vanityURLs.indexOf(trade.partnerVanityURL) == -1) {
|
||||
vanityURLs.push(trade.partnerVanityURL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
items = item.find('.history_item');
|
||||
for(j = 0; j < items.length; j++) {
|
||||
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) {
|
||||
|
||||
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) {
|
||||
|
||||
if (options.resolveVanityURLs) {
|
||||
Async.map(vanityURLs, resolveVanityURL, function(err, results) {
|
||||
if(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
for(i = 0; i < output.trades.length; i++) {
|
||||
if(output.trades[i].partnerSteamID || !output.trades[i].partnerVanityURL) {
|
||||
|
||||
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) {
|
||||
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 {
|
||||
@@ -127,17 +167,17 @@ SteamCommunity.prototype.getInventoryHistory = function(options, callback) {
|
||||
|
||||
function resolveVanityURL(vanityURL, callback) {
|
||||
request("https://steamcommunity.com/id/" + vanityURL + "/?xml=1", function(err, response, body) {
|
||||
if(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var match = body.match(/<steamID64>(\d+)<\/steamID64>/);
|
||||
if(!match || !match[1]) {
|
||||
if (!match || !match[1]) {
|
||||
callback(new Error("Couldn't find Steam ID"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
callback(null, {"vanityURL": vanityURL, "steamID": match[1]});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
var SteamID = require('steamid');
|
||||
var CEconItem = require('../classes/CEconItem.js');
|
||||
var Helpers = require('./helpers.js');
|
||||
|
||||
SteamCommunity.prototype.addFriend = function(userID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
@@ -190,6 +191,32 @@ SteamCommunity.prototype.inviteUserToGroup = function(userID, groupID, callback)
|
||||
}, "steamcommunity");
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.getUserAliases = function(userID, callback) {
|
||||
if (typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
this.httpRequestGet({
|
||||
"uri": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/ajaxaliases",
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof body !== 'object') {
|
||||
callback(new Error("Malformed response"));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, body.map(function(entry) {
|
||||
entry.timechanged = Helpers.decodeSteamTime(entry.timechanged);
|
||||
return entry;
|
||||
}));
|
||||
}, "steamcommunity");
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
@@ -230,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);
|
||||
}
|
||||
|
||||
@@ -243,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
|
||||
@@ -254,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];
|
||||
@@ -294,3 +333,118 @@ 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;
|
||||
}
|
||||
|
||||
if (err.message == "HTTP error 500" && body && body.error) {
|
||||
err = new Error(body.error);
|
||||
|
||||
var match = body.error.match(/^(.+) \((\d+)\)$/);
|
||||
if (match) {
|
||||
err.message = match[1];
|
||||
err.eresult = match[2];
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (body && body.success && body.total_inventory_count === 0) {
|
||||
// Empty inventory
|
||||
callback(null, [], [], 0);
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
|
||||
95
index.js
95
index.js
@@ -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,27 @@ 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) {
|
||||
if (!details.accountName || !details.password) {
|
||||
throw new Error("Missing either accountName or password to login; both are needed");
|
||||
}
|
||||
|
||||
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 +78,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 +153,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 +171,8 @@ SteamCommunity.prototype.login = function(details, callback) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.setCookies(cookies);
|
||||
|
||||
callback(null, sessionID, cookies, steamguard, oAuth.oauth_token);
|
||||
}
|
||||
@@ -167,11 +182,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 +224,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 +240,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 +255,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 +297,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");
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "steamcommunity",
|
||||
"version": "3.21.0",
|
||||
"version": "3.30.4",
|
||||
"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",
|
||||
@@ -17,8 +17,8 @@
|
||||
"node-bignumber": "^1.2.1",
|
||||
"steamid": "^1.0.0",
|
||||
"xml2js": "^0.4.11",
|
||||
"cheerio": "0.19.0",
|
||||
"async": "^1.4.2",
|
||||
"cheerio": "0.22.0",
|
||||
"async": "^2.1.4",
|
||||
"steam-totp": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
Reference in New Issue
Block a user