Compare commits

..

44 Commits

Author SHA1 Message Date
Alexander Corn
0bb2cb6137 3.3.2 2015-07-29 22:29:23 -04:00
Alexander Corn
c9538137d5 Fixed crashes when using various CSteamUser methods 2015-07-29 22:29:14 -04:00
Alexander Corn
df05fd04c4 3.3.1 2015-07-29 22:22:57 -04:00
Alexander Corn
dee52bf7e2 Fixed crashes when using various class methods 2015-07-29 22:22:43 -04:00
Alexander Corn
6d455e8797 3.3.0 2015-07-27 22:56:42 -04:00
Alexander Corn
a0b776ca8b Added setupProfile method 2015-07-27 22:55:25 -04:00
Alexander Corn
3f0db21e02 3.2.1 2015-07-25 01:58:00 -04:00
Alexander Corn
6798b73a17 Made profile callbacks optional 2015-07-25 01:57:53 -04:00
Alexander Corn
b98451a4b6 3.2.0 2015-07-25 01:55:45 -04:00
Alexander Corn
af2ba3f56b Added editProfile method 2015-07-25 01:55:33 -04:00
Alexander Corn
9c2662ae16 Updated profileSettings to accept only some options 2015-07-25 01:39:19 -04:00
Alexander Corn
9ee49d4f15 Added profileSettings method 2015-07-25 01:24:28 -04:00
Alexander Corn
911ae86379 3.1.1 2015-07-22 11:59:43 -04:00
Alexander Corn
a12e8cb67c Added 50-second HTTP timeout 2015-07-22 11:59:18 -04:00
Alexander Corn
edd95cc93f 3.1.0 2015-07-17 22:57:51 -04:00
Alexander Corn
4f1f265c4d Added support for app-based Steam Guard 2015-07-17 22:57:40 -04:00
Alexander Corn
80c0c3ebbc 3.0.0 2015-07-17 22:26:49 -04:00
Alexander Corn
a1ef4b5646 Added captcha support 2015-07-17 22:26:29 -04:00
Alexander Corn
06f7f10dfe All callbacks now consistently return Error objects instead of strings 2015-07-17 22:23:20 -04:00
Alexander Corn
484c049940 Fixed error when SteamGuard is needed not being 'SteamGuard' 2015-07-17 22:07:16 -04:00
Alexander Corn
36038daa1b 2.6.0 2015-07-17 21:21:59 -04:00
Alexander Corn
12cffdd62c Added Family View status to loggedIn method callback 2015-07-17 21:19:43 -04:00
Alexander Corn
eaacef66bb Added loggedIn method 2015-07-17 21:10:08 -04:00
Alexander Corn
0ef652cbac 2.5.2 2015-07-10 16:24:59 -04:00
Alexander Corn
608377277c Sort market results by price to try to avoid duplicates 2015-07-10 16:24:42 -04:00
Alexander Corn
bcaa740b54 2.5.1 2015-07-10 16:01:10 -04:00
Alexander Corn
2674287b43 Make sure everything is in English 2015-07-10 16:00:56 -04:00
Alexander Corn
8b6bed5527 2.5.0 2015-07-10 15:46:42 -04:00
Alexander Corn
6def665b64 Added SteamCommunity#marketSearch 2015-07-10 15:46:29 -04:00
Alexander Corn
4f618c0a27 2.4.0 2015-06-29 16:38:36 -04:00
Alexander Corn
19fe951d96 Added ability to spoof UI mode 2015-06-29 16:38:24 -04:00
Alexander Corn
a348112ccb 2.3.0 2015-06-28 22:18:48 -04:00
Alexander Corn
dff2727f85 Don't fire callback if not provided 2015-06-28 22:16:49 -04:00
Alexander Corn
1fd12dd4d0 Added CMarketItem 2015-06-28 20:19:42 -04:00
Alexander Corn
31093da1a8 Use better dependency semver 2015-06-28 19:40:00 -04:00
Alexander Corn
1e609524e8 2.2.1 2015-06-24 13:31:17 -04:00
Alexander Corn
2a758fc180 Remember Steam login so that cookies don't expire if logged in elsewhere 2015-06-24 13:31:01 -04:00
Alexander Corn
eb2081c325 2.2.0 2015-06-18 00:57:23 -04:00
Alexander Corn
c60d4942e0 Removed useless timestamp parameter from chatMessage event 2015-06-18 00:41:55 -04:00
Alexander Corn
1aa42b89f7 Added public static CSteamUser.getAvatarURL method 2015-06-18 00:41:26 -04:00
Alexander Corn
e01d05c140 Added PersonaStateFlag enum 2015-06-18 00:36:26 -04:00
Alexander Corn
8ec0ef84c2 Added PersonaState enum 2015-06-18 00:33:34 -04:00
Alexander Corn
0e5929df24 Lowered chat poll interval to 500ms 2015-06-18 00:24:54 -04:00
Alexander Corn
74bf2dcfca Added webchat 2015-06-18 00:22:05 -04:00
8 changed files with 804 additions and 73 deletions

126
classes/CMarketItem.js Normal file
View File

@@ -0,0 +1,126 @@
var SteamCommunity = require('../index.js');
var Cheerio = require('cheerio');
SteamCommunity.prototype.getMarketItem = function(appid, hashName, callback) {
var self = this;
this.request("https://steamcommunity.com/market/listings/" + appid + "/" + encodeURIComponent(hashName), function(err, response, body) {
if(self._checkHttpError(err, response, callback)) {
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."));
return;
}
var item = new CMarketItem(self, body, $);
if(item.commodity) {
item.updatePrice(function(err) {
if(err) {
callback(err);
} else {
callback(null, item);
}
});
} else {
callback(null, item);
}
});
};
function CMarketItem(community, body, $) {
this._community = community;
this._$ = $;
this._country = "US";
var match = body.match(/var g_strCountryCode = "([^"]+)";/);
if(match) {
this._country = match[1];
}
this.commodity = false;
var 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) {
try {
this.medianSalePrices = JSON.parse(match[1]);
this.medianSalePrices = this.medianSalePrices.map(function(item) {
return {
"hour": new Date(item[0]),
"price": item[1],
"quantity": parseInt(item[2], 10)
};
});
} catch(e) {
// ignore
}
}
this.quantity = 0;
this.lowestPrice = 0;
if(!this.commodity) {
var total = $('#searchResults_total');
if(total) {
this.quantity = parseInt(total.text().replace(/[^\d]/g, '').trim(), 10);
}
var lowest = $('.market_listing_price.market_listing_price_with_fee');
if(lowest[0]) {
this.lowestPrice = parseInt($(lowest[0]).text().replace(/[^\d]/g, '').trim(), 10);
}
}
// TODO: Buying listings and placing buy orders
}
CMarketItem.prototype.updatePrice = function(callback) {
if(!this.commodity) {
throw new Error("Cannot update price for non-commodity item");
}
// TODO: Currency option maybe?
var self = this;
this._community.request({
"uri": "https://steamcommunity.com/market/itemordershistogram?country=US&language=english&currency=1&item_nameid=" + this.commodityID,
"json": true,
}, function(err, response, body) {
if(self._community._checkHttpError(err, response, callback)) {
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

@@ -0,0 +1,85 @@
var SteamCommunity = require('../index.js');
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';
qs.sort_dir = 'asc';
performSearch.call(this, this.request, qs, [], callback);
};
function performSearch(request, qs, results, callback) {
var self = this;
request({
"uri": "https://steamcommunity.com/market/search/render/",
"qs": qs,
"headers": {
"referer": "https://steamcommunity.com/market/search"
},
"json": true
}, function(err, response, body) {
if(self._checkHttpError(err, response, callback)) {
return;
}
if(!body.success) {
callback(new Error("Success is not true"));
return;
}
if(!body.results_html) {
callback(new Error("No results_html in response"));
return;
}
var $ = Cheerio.load(body.results_html);
if($('.market_listing_table_message').length > 0) {
callback(new Error($('.market_listing_table_message').text()));
return;
}
var rows = $('.market_listing_row_link');
for(var i = 0; i < rows.length; i++) {
results.push(new CMarketSearchResult($(rows[i])));
}
if(body.start + body.pagesize >= body.total_count) {
callback(null, results);
} else {
qs.start += body.pagesize;
performSearch(request, qs, results, 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.price = parseInt(row.find('.market_listing_their_price .market_table_value span').text().replace(/[^\d]+/g, ''), 10);
this.quantity = parseInt(row.find('.market_listing_num_listings_qty').text().replace(/[^\d]+/g, ''), 10);
}

View File

@@ -13,8 +13,7 @@ SteamCommunity.prototype.getSteamGroup = function(id, callback) {
var self = this;
this.request("https://steamcommunity.com/" + (typeof id === 'string' ? "groups/" + id : "gid/" + id.toString()) + "/memberslistxml/?xml=1", function(err, response, body) {
if(err || response.statusCode != 200) {
callback(err || "HTTP error " + response.statusCode);
if(self._checkHttpError(err, response, callback)) {
return;
}
@@ -66,8 +65,7 @@ CSteamGroup.prototype.getMembers = function(callback, members, link) {
var self = this;
this._community.request(link, function(err, response, body) {
if(err || response.statusCode != 200) {
callback(err || "HTTP error " + response.statusCode);
if(self._community._checkHttpError(err, response, callback)) {
return;
}
@@ -103,7 +101,7 @@ CSteamGroup.prototype.join = function(callback) {
}
if(err || response.statusCode >= 400) {
callback(err || "HTTP error " + response.statusCode);
callback(err || new Error("HTTP error " + response.statusCode));
return;
}
@@ -129,7 +127,7 @@ CSteamGroup.prototype.leave = function(callback) {
}
if(err || response.statusCode >= 400) {
callback(err || "HTTP error " + response.statusCode);
callback(err || new Error("HTTP error " + response.statusCode));
return;
}
@@ -155,7 +153,7 @@ CSteamGroup.prototype.postAnnouncement = function(headline, content, callback) {
}
if(err || response.statusCode >= 400) {
callback(err || "HTTP error " + response.statusCode);
callback(err || new Error("HTTP error " + response.statusCode));
return;
}
@@ -214,7 +212,7 @@ CSteamGroup.prototype.scheduleEvent = function(name, type, description, time, se
}
if(err || response.statusCode >= 400) {
callback(err || "HTTP error " + response.statusCode);
callback(err || new Error("HTTP error " + response.statusCode));
return;
}
@@ -241,7 +239,7 @@ CSteamGroup.prototype.setPlayerOfTheWeek = function(steamID, callback) {
}
if(err || response.statusCode != 200) {
callback(err || "HTTP error " + response.statusCode);
callback(err || new Error("HTTP error " + response.statusCode));
return;
}
@@ -254,7 +252,7 @@ CSteamGroup.prototype.setPlayerOfTheWeek = function(steamID, callback) {
if(results.response.results[0] == 'OK') {
callback(null, new SteamID(results.response.oldPOTW[0]), new SteamID(results.response.newPOTW[0]));
} else {
callback(results.response.results[0]);
callback(new Error(results.response.results[0]));
}
});
});
@@ -275,7 +273,7 @@ CSteamGroup.prototype.kick = function(steamID, callback) {
}
if(err || response.statusCode >= 400) {
callback(err || "HTTP error " + response.statusCode);
callback(err || new Error("HTTP error " + response.statusCode));
return;
}

View File

@@ -13,8 +13,7 @@ SteamCommunity.prototype.getSteamUser = function(id, callback) {
var self = this;
this.request("http://steamcommunity.com/" + (typeof id === 'string' ? "id/" + id : "profiles/" + id.toString()) + "/?xml=1", function(err, response, body) {
if(err || response.statusCode != 200) {
callback(err || "HTTP error " + response.statusCode);
if(self._checkHttpError(err, response, callback)) {
return;
}
@@ -24,12 +23,12 @@ SteamCommunity.prototype.getSteamUser = function(id, callback) {
xml2js.parseString(body, function(err, result) {
if(err || (!result.response && !result.profile)) {
callback(err || "No valid response");
callback(err || new Error("No valid response"));
return;
}
if(result.response && result.response.error && result.response.error.length) {
callback(result.response.error[0]);
callback(new Error(result.response.error[0]));
return;
}
@@ -91,11 +90,11 @@ function CSteamUser(community, userData, customurl) {
}
}
CSteamUser.prototype.getAvatarURL = function(size, protocol) {
CSteamUser.getAvatarURL = function(hash, size, protocol) {
size = size || '';
protocol = protocol || 'http://';
var url = protocol + "steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/" + this.avatarHash.substring(0, 2) + "/" + this.avatarHash;
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";
} else {
@@ -103,14 +102,18 @@ CSteamUser.prototype.getAvatarURL = function(size, protocol) {
}
};
CSteamUser.prototype.getAvatarURL = function(size, protocol) {
return CSteamUser.getAvatarURL(this.avatarHash, size, protocol);
};
CSteamUser.prototype.addFriend = function(callback) {
var self = this;
this._community.request.post('https://steamcommunity.com/actions/AddFriendAjax', {"form": {"accept_invite": 0, "sessionID": this._community.getSessionID(), "steamid": this.steamID.toString()}}, function(err, response, body) {
if(!callback) {
return;
}
if(err || response.statusCode != 200) {
callback(err || "HTTP error " + response.statusCode);
if(self._community._checkHttpError(err, response, callback)) {
return;
}
@@ -123,55 +126,55 @@ CSteamUser.prototype.addFriend = function(callback) {
}
if(json.success) {
callback();
callback(null);
} else {
callback("Unknown error");
callback(new Error("Unknown error"));
}
});
};
CSteamUser.prototype.acceptFriendRequest = function(callback) {
var self = this;
this._community.request.post('https://steamcommunity.com/actions/AddFriendAjax', {"form": {"accept_invite": 1, "sessionID": this._community.getSessionID(), "steamid": this.steamID.toString()}}, function(err, response, body) {
if(!callback) {
return;
}
if(err || response.statusCode != 200) {
callback(err || "HTTP error " + response.statusCode);
if(self._community._checkHttpError(err, response, callback)) {
return;
}
callback();
callback(null);
});
};
CSteamUser.prototype.removeFriend = function(callback) {
var self = this;
this._community.request.post('https://steamcommunity.com/actions/RemoveFriendAjax', {"form": {"sessionID": this._community.getSessionID(), "steamid": this.steamID.toString()}}, function(err, response, body) {
if(!callback) {
return;
}
if(err || response.statusCode != 200) {
callback(err || "HTTP error " + response.statusCode);
if(self._community._checkHttpError(err, response, callback)) {
return;
}
callback();
callback(null);
});
};
CSteamUser.prototype.blockCommunication = function(callback) {
var self = this;
this._community.request.post('https://steamcommunity.com/actions/BlockUserAjax', {"form": {"sessionID": this._community.getSessionID(), "steamid": this.steamID.toString()}}, function(err, response, body) {
if(!callback) {
return;
}
if(err || response.statusCode != 200) {
callback(err || "HTTP error " + response.statusCode);
if(self._community._checkHttpError(err, response, callback)) {
return;
}
callback();
callback(null);
});
};
@@ -185,11 +188,11 @@ CSteamUser.prototype.unblockCommunication = function(callback) {
}
if(err || response.statusCode >= 400) {
callback(err || "HTTP error " + response.statusCode);
callback(err || new Error("HTTP error " + response.statusCode));
return;
}
callback();
callback(null);
});
};
@@ -204,7 +207,7 @@ CSteamUser.prototype.comment = function(message, callback) {
}
if(err || response.statusCode != 200) {
callback(err || "HTTP error " + response.statusCode);
callback(err || new Error("HTTP error " + response.statusCode));
return;
}
@@ -217,11 +220,11 @@ CSteamUser.prototype.comment = function(message, callback) {
}
if(json.success) {
callback();
callback(null);
} else if(json.error) {
callback(json.error);
callback(new Error(json.error));
} else {
callback("Unknown error");
callback(new Error("Unknown error"));
}
});
};

253
components/chat.js Normal file
View File

@@ -0,0 +1,253 @@
var SteamCommunity = require('../index.js');
var SteamID = require('steamid');
SteamCommunity.ChatState = {
"Offline": 0,
"LoggingOn": 1,
"LogOnFailed": 2,
"LoggedOn": 3
};
SteamCommunity.PersonaState = {
"Offline": 0,
"Online": 1,
"Busy": 2,
"Away": 3,
"Snooze": 4,
"LookingToTrade": 5,
"LookingToPlay": 6,
"Max": 7
};
SteamCommunity.PersonaStateFlag = {
"HasRichPresence": 1,
"InJoinableGame": 2,
"OnlineUsingWeb": 256,
"OnlineUsingMobile": 512,
"OnlineUsingBigPicture": 1024
};
SteamCommunity.prototype.chatLogon = function(interval, uiMode) {
if(this.chatState == SteamCommunity.ChatState.LoggingOn || this.chatState == SteamCommunity.ChatState.LoggedOn) {
return;
}
interval = interval || 500;
uiMode = uiMode || "web";
this.emit('debug', 'Requesting chat WebAPI token');
this.chatState = SteamCommunity.ChatState.LoggingOn;
var self = this;
this.request("https://steamcommunity.com/chat", function(err, response, body) {
if(err || response.statusCode != 200) {
self.emit('debug', 'Error requesting chat WebAPI token: ' + (err ? err.message : "HTTP error " + response.statusCode));
self.chatState = SteamCommunity.ChatState.LogOnFailed;
setTimeout(self.chatLogon.bind(self), 5000);
return;
}
var match = body.match(/[0-9a-f]{32}/);
if(!match) {
self.emit('debug', 'Couldn\'t find a WebAPI chat token in the response.');
self.chatState = SteamCommunity.ChatState.LogOnFailed;
setTimeout(self.chatLogon.bind(self), 5000);
return;
}
self.request.post({
"uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Logon/v1",
"form": {
"ui_mode": uiMode,
"access_token": match[0]
},
"json": true
}, function(err, response, body) {
if(err || response.statusCode != 200) {
self.emit('debug', 'Error logging into webchat: ' + (err ? err.message : "HTTP error " + response.statusCode));
self.chatState = SteamCommunity.ChatState.LogOnFailed;
setTimeout(self.chatLogon.bind(self), 5000);
return;
}
if(body.error != 'OK') {
self.emit('debug', 'Error logging into webchat: ' + body.error);
self.chatState = SteamCommunity.ChatState.LogOnFailed;
setTimeout(self.chatLogon.bind(self), 5000);
return;
}
self._chat = {
"umqid": body.umqid,
"message": body.message,
"accessToken": match[0],
"interval": interval
};
self.chatFriends = {};
self.chatState = SteamCommunity.ChatState.LoggedOn;
self.emit('chatLoggedOn');
self._chatPoll();
});
});
};
SteamCommunity.prototype.chatMessage = function(recipient, text, type, callback) {
if(this.chatState != SteamCommunity.ChatState.LoggedOn) {
throw new Error("Chat must be logged on before messages can be sent");
}
if(typeof recipient === 'string') {
recipient = new SteamID(recipient);
}
if(typeof type === 'function') {
callback = type;
type = 'saytext';
}
type = type || 'saytext';
this.request.post({
"uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Message/v1",
"form": {
"access_token": this._chat.accessToken,
"steamid_dst": recipient.toString(),
"text": text,
"type": type,
"umqid": this._chat.umqid
},
"json": true
}, function(err, response, body) {
if(!callback) {
return;
}
if(self._checkHttpError(err, response, callback)) {
return;
}
if(body.error != 'OK') {
callback(new Error(body.error));
} else {
callback(null);
}
});
};
SteamCommunity.prototype.chatLogoff = function() {
var self = this;
this.request.post({
"uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Logoff/v1",
"form": {
"access_token": this._chat.accessToken,
"umqid": this._chat.umqid
}
}, function(err, response, body) {
if(err || response.statusCode != 200) {
self.emit('debug', 'Error logging off of chat: ' + (err ? err.message : "HTTP error " + response.statusCode));
setTimeout(self.chatLogoff.bind(self), 1000);
} else {
self.emit('chatLoggedOff');
clearTimeout(self._chat.timer);
delete self._chat;
delete self.chatFriends;
self.chatState = SteamCommunity.ChatState.Offline;
}
});
};
SteamCommunity.prototype._chatPoll = function() {
this.emit('debug', 'Doing chat poll');
var self = this;
this.request.post({
"uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Poll/v1",
"form": {
"umqid": self._chat.umqid,
"message": self._chat.message,
"pollid": 1,
"sectimeout": 20,
"secidletime": 0,
"use_accountids": 1,
"access_token": self._chat.accessToken
},
"json": true
}, function(err, response, body) {
if(self.chatState == SteamCommunity.ChatState.Offline) {
return;
}
self._chat.timer = setTimeout(self._chatPoll.bind(self), self._chat.interval);
if(err || response.statusCode != 200) {
self.emit('debug', 'Error in chat poll: ' + (err ? err.message : "HTTP error " + response.statusCode));
return;
}
if(body.error != 'OK') {
self.emit('debug', 'Error in chat poll: ' + body.error);
return;
}
self._chat.message = body.messagelast;
(body.messages || []).forEach(function(message) {
var sender = new SteamID();
sender.universe = SteamID.Universe.PUBLIC;
sender.type = SteamID.Type.INDIVIDUAL;
sender.instance = SteamID.Instance.DESKTOP;
sender.accountid = message.accountid_from;
switch(message.type) {
case 'personastate':
self._chatUpdatePersona(sender);
break;
case 'saytext':
self.emit('chatMessage', sender, message.text);
break;
case 'typing':
self.emit('chatTyping', sender);
break;
default:
self.emit('debug', 'Unhandled chat message type: ' + message.type);
}
});
});
};
SteamCommunity.prototype._chatUpdatePersona = function(steamID) {
this.emit('debug', 'Updating persona data for ' + steamID);
var self = this;
this.request({
"uri": "https://steamcommunity.com/chat/friendstate/" + steamID.accountid,
"json": true
}, function(err, response, body) {
if(err || response.statusCode != 200) {
self.emit('debug', 'Chat update persona error: ' + (err ? err.message : "HTTP error " + response.statusCode));
setTimeout(function() {
self._chatUpdatePersona(steamID);
}, 2000);
return;
}
var persona = {
"steamID": steamID,
"personaName": body.m_strName,
"personaState": body.m_ePersonaState,
"personaStateFlags": body.m_nPersonaStateFlags || 0,
"avatarHash": body.m_strAvatarHash,
"inGame": !!body.m_bInGame,
"inGameAppID": body.m_nInGameAppID ? parseInt(body.m_nInGameAppID, 10) : null,
"inGameName": body.m_strInGameName || null
};
self.emit('chatPersonaState', steamID, persona);
self.chatFriends[steamID.getSteamID64()] = persona;
});
};

213
components/profile.js Normal file
View File

@@ -0,0 +1,213 @@
var SteamCommunity = require('../index.js');
var SteamID = require('steamid');
var Cheerio = require('cheerio');
SteamCommunity.PrivacyState = {
"Private": 1,
"FriendsOnly": 2,
"Public": 3
};
var CommentPrivacyState = {
"1": "commentselfonly",
"2": "commentfriendsonly",
"3": "commentanyone"
};
SteamCommunity.prototype.setupProfile = function(callback) {
var self = this;
this._myProfile("edit?welcomed=1", null, function(err, response, body) {
if(!callback) {
return;
}
if(err || response.statusCode != 200) {
callback(err || new Error("HTTP error " + response.statusCode));
} else {
callback(null);
}
});
};
SteamCommunity.prototype.editProfile = function(settings, callback) {
var self = this;
this._myProfile("edit", null, function(err, response, body) {
if(err || response.statusCode != 200) {
if(callback) {
callback(err || new Error("HTTP error " + response.statusCode));
}
return;
}
var $ = Cheerio.load(body);
var form = $('#editForm');
if(!form) {
if(callback) {
callback(new Error("Malformed response"));
}
return;
}
var values = {};
form.serializeArray().forEach(function(item) {
values[item.name] = item.value;
});
for(var i in settings) {
if(!settings.hasOwnProperty(i)) {
continue;
}
switch(i) {
case 'name':
values.personaName = settings[i];
break;
case 'realName':
values.real_name = settings[i];
break;
case 'summary':
values.summary = settings[i];
break;
case 'country':
values.country = settings[i];
break;
case 'state':
values.state = settings[i];
break;
case 'city':
values.city = settings[i];
break;
case 'customURL':
values.customURL = settings[i];
break;
case 'background':
// The assetid of our desired profile background
values.profile_background = settings[i];
break;
case 'featuredBadge':
// Currently, game badges aren't supported
values.favorite_badge_badgeid = settings[i];
break;
case 'primaryGroup':
if(typeof settings[i] === 'object' && settings[i].accountid) {
values.primary_group_steamid = settings[i].accountid;
} else {
values.primary_group_steamid = new SteamID(settings[i]).accountid;
}
break;
// TODO: profile showcases
}
}
self._myProfile("edit", values, function(err, response, body) {
if(err || response.statusCode != 200) {
if(callback) {
callback(err || new Error("HTTP error " + response.statusCode));
}
return;
}
// Check for an error
var $ = Cheerio.load(body);
var error = $('#errorText .formRowFields');
if(error) {
error = error.text().trim();
if(error) {
if(callback) {
callback(new Error(error));
}
return;
}
}
if(callback) {
callback(null);
}
});
});
};
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) {
callback(err || new Error("HTTP error " + response.statusCode));
}
return;
}
var $ = Cheerio.load(body);
var form = $('#editForm');
if(!form) {
if(callback) {
callback(new Error("Malformed response"));
}
return;
}
var values = {};
form.serializeArray().forEach(function(item) {
values[item.name] = item.value;
});
for(var i in settings) {
if(!settings.hasOwnProperty(i)) {
continue;
}
switch(i) {
case 'profile':
values.privacySetting = settings[i];
break;
case 'comments':
values.commentSetting = CommentPrivacyState[settings[i]];
break;
case 'inventory':
values.inventoryPrivacySetting = settings[i];
break;
case 'inventoryGifts':
values.inventoryGiftPrivacy = settings[i] ? 1 : 0;
break;
case 'emailConfirmation':
values.tradeConfirmationSetting = settings[i] ? 1 : 0;
break;
}
}
self._myProfile("edit/settings", values, function(err, response, body) {
if(err || response.statusCode != 200) {
if(callback) {
callback(err || new Error("HTTP error " + response.statusCode));
}
return;
}
if(callback) {
callback(null);
}
});
});
};

116
index.js
View File

@@ -3,13 +3,20 @@ var RSA = require('node-bignumber').Key;
var hex2b64 = require('node-bignumber').hex2b64;
var SteamID = require('steamid');
require('util').inherits(SteamCommunity, require('events').EventEmitter);
module.exports = SteamCommunity;
SteamCommunity.SteamID = SteamID;
function SteamCommunity() {
this._jar = Request.jar();
this.request = Request.defaults({"jar": this._jar});
this._captchaGid = -1;
this.request = Request.defaults({"jar": this._jar, "timeout": 50000});
this.chatState = SteamCommunity.ChatState.Offline;
// English
this._jar.setCookie(Request.cookie('Steam_Language=english'), 'https://steamcommunity.com');
}
SteamCommunity.prototype.login = function(details, callback) {
@@ -37,41 +44,50 @@ SteamCommunity.prototype.login = function(details, callback) {
key.setPublic(json.publickey_mod, json.publickey_exp);
var form = {
"captcha_text": "",
"captchagid": -1,
"captcha_text": details.captcha || "",
"captchagid": self._captchaGid,
"emailauth": details.authCode || "",
"emailsteamid": "",
"loginfriendlyname": "",
"password": hex2b64(key.encrypt(details.password)),
"remember_login": false,
"remember_login": "true",
"rsatimestamp": json.timestamp,
"twofactorcode": "",
"twofactorcode": details.twoFactorCode || "",
"username": details.accountName
};
self.request.post("https://steamcommunity.com/login/dologin/", {"form": form}, function(err, response, body) {
if(err) {
callback(err);
self.request.post({
"uri": "https://steamcommunity.com/login/dologin/",
"json": true,
"form": form
}, function(err, response, body) {
if(self._checkHttpError(err, response, callback)) {
return;
}
var json;
try {
json = JSON.parse(body);
} catch(e) {
callback(e);
return;
}
if(!json.success && json.emailauth_needed) {
callback("Please provide the authorization code sent to your address at " + json.emaildomain);
} else if(!json.success) {
callback(json.message || "Unknown error");
if(!body.success && body.emailauth_needed) {
// Steam Guard (email)
var error = new Error("SteamGuard");
error.emaildomain = body.emaildomain;
callback(error);
} else if(!body.success && body.requires_twofactor) {
// Steam Guard (app)
callback(new Error("SteamGuardMobile"));
} else if(!body.success && body.captcha_needed) {
var error = new Error("CAPTCHA");
error.captchaurl = "https://steamcommunity.com/public/captcha.php?gid=" + body.captcha_gid;
self._captchaGid = body.captcha_gid;
callback(error);
} else if(!body.success) {
callback(new Error(body.message || "Unknown error"));
} else {
var sessionID = generateSessionID();
self._jar.setCookie(Request.cookie('sessionid=' + sessionID), 'http://steamcommunity.com');
self.steamID = new SteamID(json.transfer_parameters.steamid);
self.steamID = new SteamID(body.transfer_parameters.steamid);
var cookies = self._jar.getCookieString("https://steamcommunity.com").split(';').map(function(cookie) {
return cookie.trim();
});
@@ -125,8 +141,8 @@ function generateSessionID() {
SteamCommunity.prototype.getWebApiKey = function(domain, callback) {
var self = this;
this.request("https://steamcommunity.com/dev/apikey", function(err, response, body) {
if(err || response.statusCode != 200) {
return callback(err.message || "HTTP error " + response.statusCode);
if(self._checkHttpError(err, response, callback)) {
return;
}
if(body.match(/<h2>Access Denied<\/h2>/)) {
@@ -168,8 +184,8 @@ SteamCommunity.prototype.parentalUnlock = function(pin, callback) {
return;
}
if(err || response.statusCode != 200) {
return callback(err.message || "HTTP error " + response.statusCode);
if(self._checkHttpError(err, response, callback)) {
return;
}
if(!body || typeof body.success !== 'boolean') {
@@ -186,8 +202,8 @@ SteamCommunity.prototype.parentalUnlock = function(pin, callback) {
SteamCommunity.prototype.getNotifications = function(callback) {
this.request.get("https://steamcommunity.com/actions/RefreshNotificationArea", function(err, response, body) {
if(err || response.statusCode != 200) {
return callback(err.message || "HTTP error " + response.statusCode);
if(self._checkHttpError(err, response, callback)) {
return;
}
var notifications = {
@@ -225,18 +241,34 @@ SteamCommunity.prototype.resetItemNotifications = function(callback) {
return;
}
if(err || response.statusCode != 200) {
callback(err.message || "HTTP error " + response.statusCode);
} else {
callback();
if(self._checkHttpError(err, response, callback)) {
return;
}
callback(null);
});
};
SteamCommunity.prototype.loggedIn = function(callback) {
this.request("https://steamcommunity.com/my", {"followRedirect": false}, function(err, response, body) {
if(err || (response.statusCode != 302 && response.statusCode != 403)) {
callback(err || new Error("HTTP error " + response.statusCode));
return;
}
if(response.statusCode == 403) {
callback(null, true, true);
return;
}
callback(null, !!response.headers.location.match(/steamcommunity\.com(\/(id|profiles)\/[^\/]+)\/?/), false);
});
};
SteamCommunity.prototype._checkCommunityError = function(html, callback) {
if(html.match(/<h1>Sorry!<\/h1>/)) {
var match = html.match(/<h3>(.+)<\/h3>/);
callback(match ? match[1] : "Unknown error occurred");
callback(new Error(match ? match[1] : "Unknown error occurred"));
return true;
}
@@ -261,5 +293,25 @@ SteamCommunity.prototype._myProfile = function(endpoint, form, callback) {
});
};
SteamCommunity.prototype._checkHttpError = function(err, response, callback) {
if(err) {
callback(err);
return true;
}
if(response.statusCode != 200) {
var error = new Error("HTTP error " + response.statusCode);
error.code = response.statusCode;
callback(error);
return true;
}
return false;
};
require('./classes/CMarketItem.js');
require('./classes/CMarketSearchResult.js');
require('./classes/CSteamGroup.js');
require('./classes/CSteamUser.js');
require('./components/chat.js');
require('./components/profile.js');

View File

@@ -1,6 +1,6 @@
{
"name": "steamcommunity",
"version": "2.1.0",
"version": "3.3.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",
@@ -13,9 +13,10 @@
"url": "https://github.com/DoctorMcKay/node-steamcommunity.git"
},
"dependencies": {
"request": "2.51.x",
"node-bignumber": "1.2.x",
"steamid": "0.1.x",
"xml2js": "0.4.x"
"request": "^2.58.0",
"node-bignumber": "^1.2.1",
"steamid": "^0.3.0",
"xml2js": "^0.4.9",
"cheerio": "^0.19.0"
}
}