mirror of
https://github.com/DoctorMcKay/node-steamcommunity.git
synced 2026-08-19 13:13:28 +08:00
Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9175a44828 | ||
|
|
deeeb633cf | ||
|
|
a50bd35a54 | ||
|
|
5bf51a2e3e | ||
|
|
448b32b9c6 | ||
|
|
01428e6007 | ||
|
|
43d1147fd7 | ||
|
|
479cc47bf5 | ||
|
|
01217a3659 | ||
|
|
0e34bceb1f | ||
|
|
c4ea8eeb41 | ||
|
|
0f83fe8c64 | ||
|
|
5624611421 | ||
|
|
49d2bb4252 | ||
|
|
b54ccff7c8 | ||
|
|
b82f7ca045 | ||
|
|
2526539266 | ||
|
|
fc75036974 | ||
|
|
ee1e819812 | ||
|
|
fceea0737d | ||
|
|
f3ac556922 | ||
|
|
2e02c14455 | ||
|
|
8f580fc231 | ||
|
|
6f596b5b16 | ||
|
|
1249e28d83 | ||
|
|
85bcaa70be | ||
|
|
2a5419513c | ||
|
|
deec5c52a4 | ||
|
|
2399a5b783 | ||
|
|
efea8c6265 | ||
|
|
2be414b20c | ||
|
|
25ebb4c5a3 | ||
|
|
ac87fd3a40 | ||
|
|
c67e2e04f1 | ||
|
|
83eeb0f190 | ||
|
|
e09cf24fbf | ||
|
|
9d2b5ade67 | ||
|
|
be4e5c9449 | ||
|
|
5926197f23 | ||
|
|
2d86f81142 | ||
|
|
f37bff4a57 | ||
|
|
c700b7663b | ||
|
|
c26e548c64 | ||
|
|
658d8fb708 | ||
|
|
cd68841d1d | ||
|
|
5853968661 | ||
|
|
10d899f7a7 | ||
|
|
4e1453e702 | ||
|
|
f3cf2a4361 | ||
|
|
2890b70add | ||
|
|
1810727a2f | ||
|
|
3d566ed0fb | ||
|
|
80979bc387 | ||
|
|
4e326e3295 | ||
|
|
cff35ba863 | ||
|
|
d975fdfba8 | ||
|
|
4e3c319ee1 | ||
|
|
e5cbd23110 | ||
|
|
82f1d490f9 | ||
|
|
c3f5b492b3 | ||
|
|
09cd81b607 | ||
|
|
8a6d912374 | ||
|
|
c392f2ac74 | ||
|
|
8e6f859269 | ||
|
|
84dce9872a | ||
|
|
c2ad457e87 | ||
|
|
0116842deb | ||
|
|
3d0522a560 | ||
|
|
3db3d47e92 | ||
|
|
f341dbe72a | ||
|
|
22f388e110 | ||
|
|
115f13f686 | ||
|
|
a30739067e | ||
|
|
bd87f27c9c | ||
|
|
07c8c724e7 | ||
|
|
ea88c1cda4 | ||
|
|
c291f0fab3 | ||
|
|
88063ebf27 | ||
|
|
8485b447d8 | ||
|
|
df20c1ac25 | ||
|
|
49b038d3d2 | ||
|
|
d1efe5f203 | ||
|
|
b16a8144ff | ||
|
|
019e33d360 | ||
|
|
01093f38fa | ||
|
|
62c2f1b965 | ||
|
|
21895a221c | ||
|
|
f3d88d6b83 | ||
|
|
09327af220 | ||
|
|
0bb2cb6137 | ||
|
|
c9538137d5 | ||
|
|
df05fd04c4 | ||
|
|
dee52bf7e2 | ||
|
|
6d455e8797 | ||
|
|
a0b776ca8b | ||
|
|
3f0db21e02 | ||
|
|
6798b73a17 | ||
|
|
b98451a4b6 | ||
|
|
af2ba3f56b | ||
|
|
9c2662ae16 | ||
|
|
9ee49d4f15 | ||
|
|
911ae86379 | ||
|
|
a12e8cb67c | ||
|
|
edd95cc93f | ||
|
|
4f1f265c4d | ||
|
|
80c0c3ebbc | ||
|
|
a1ef4b5646 | ||
|
|
06f7f10dfe | ||
|
|
484c049940 | ||
|
|
36038daa1b | ||
|
|
12cffdd62c | ||
|
|
eaacef66bb | ||
|
|
0ef652cbac | ||
|
|
608377277c | ||
|
|
bcaa740b54 | ||
|
|
2674287b43 | ||
|
|
8b6bed5527 | ||
|
|
6def665b64 | ||
|
|
4f618c0a27 | ||
|
|
19fe951d96 | ||
|
|
a348112ccb | ||
|
|
dff2727f85 | ||
|
|
1fd12dd4d0 | ||
|
|
31093da1a8 | ||
|
|
1e609524e8 | ||
|
|
2a758fc180 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
node_modules/*
|
||||
test.js
|
||||
.idea/
|
||||
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Alexander Corn
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1,8 +1,13 @@
|
||||
# Steam Community for Node.js
|
||||
[](https://npmjs.com/package/steamcommunity)
|
||||
[](https://npmjs.com/package/steamcommunity)
|
||||
[](https://david-dm.org/DoctorMcKay/node-steamcommunity)
|
||||
[](https://github.com/DoctorMcKay/node-steamcommunity/blob/master/LICENSE)
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=N36YVAT42CZ4G&item_name=node%2dsteamcommunity¤cy_code=USD)
|
||||
|
||||
This lightweight module provides an easy interface for the Steam Community website. This module can be used to simply login to steamcommunity.com for use with other libraries, or to interact with steamcommunity.com.
|
||||
This module provides an easy interface for the Steam Community website. This module can be used to simply login to steamcommunity.com for use with other libraries, or to interact with steamcommunity.com.
|
||||
|
||||
It supports Steam Guard but doesn't support CAPTCHAs, so make sure you don't provide Steam with an incorrect password.
|
||||
It supports Steam Guard and CAPTCHAs.
|
||||
|
||||
# Installation
|
||||
|
||||
|
||||
20
classes/CConfirmation.js
Normal file
20
classes/CConfirmation.js
Normal file
@@ -0,0 +1,20 @@
|
||||
module.exports = CConfirmation;
|
||||
|
||||
function CConfirmation(community, data) {
|
||||
this._community = community;
|
||||
|
||||
this.id = data.id;
|
||||
this.key = data.key;
|
||||
this.title = data.title;
|
||||
this.receiving = data.receiving;
|
||||
this.time = data.time;
|
||||
this.icon = data.icon;
|
||||
}
|
||||
|
||||
CConfirmation.prototype.getOfferID = function(time, key, callback) {
|
||||
this._community.getConfirmationOfferID(this.id, time, key, callback);
|
||||
};
|
||||
|
||||
CConfirmation.prototype.respond = function(time, key, accept, callback) {
|
||||
this._community.respondToConfirmation(this.id, this.key, time, key, accept, callback);
|
||||
};
|
||||
59
classes/CEconItem.js
Normal file
59
classes/CEconItem.js
Normal file
@@ -0,0 +1,59 @@
|
||||
module.exports = CEconItem;
|
||||
|
||||
function CEconItem(item, descriptions, contextID) {
|
||||
var thing;
|
||||
for(thing in item) {
|
||||
if(item.hasOwnProperty(thing)) {
|
||||
this[thing] = item[thing];
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.tradable = !!this.tradable;
|
||||
this.marketable = !!this.marketable;
|
||||
this.commodity = !!this.commodity;
|
||||
this.market_tradable_restriction = (this.market_tradable_restriction ? parseInt(this.market_tradable_restriction, 10) : 0);
|
||||
this.market_marketable_restriction = (this.market_marketable_restriction ? parseInt(this.market_marketable_restriction, 10) : 0);
|
||||
}
|
||||
|
||||
CEconItem.prototype.getImageURL = function() {
|
||||
return "https://steamcommunity-a.akamaihd.net/economy/image/" + this.icon_url + "/";
|
||||
};
|
||||
|
||||
CEconItem.prototype.getLargeImageURL = function() {
|
||||
if(!this.icon_url_large) {
|
||||
return this.getImageURL();
|
||||
}
|
||||
|
||||
return "https://steamcommunity-a.akamaihd.net/economy/image/" + this.icon_url_large + "/";
|
||||
};
|
||||
|
||||
CEconItem.prototype.getTag = function(category) {
|
||||
if(!this.tags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for(var i = 0; i < this.tags.length; i++) {
|
||||
if(this.tags[i].category == category) {
|
||||
return this.tags[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
126
classes/CMarketItem.js
Normal file
126
classes/CMarketItem.js
Normal 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¤cy=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);
|
||||
}
|
||||
});
|
||||
};
|
||||
85
classes/CMarketSearchResult.js
Normal file
85
classes/CMarketSearchResult.js
Normal 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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -60,229 +59,39 @@ CSteamGroup.prototype.getAvatarURL = function(size, protocol) {
|
||||
}
|
||||
};
|
||||
|
||||
CSteamGroup.prototype.getMembers = function(callback, members, link) {
|
||||
members = members || [];
|
||||
link = link || "http://steamcommunity.com/gid/" + this.steamID.toString() + "/memberslistxml/?xml=1";
|
||||
|
||||
var self = this;
|
||||
this._community.request(link, function(err, response, body) {
|
||||
if(err || response.statusCode != 200) {
|
||||
callback(err || "HTTP error " + response.statusCode);
|
||||
return;
|
||||
}
|
||||
|
||||
xml2js.parseString(body, function(err, result) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
members = members.concat(result.memberList.members[0].steamID64.map(function(steamID) {
|
||||
return new SteamID(steamID);
|
||||
}));
|
||||
|
||||
if(result.memberList.nextPageLink) {
|
||||
self.getMembers(callback, members, result.memberList.nextPageLink[0]);
|
||||
} else {
|
||||
callback(null, members);
|
||||
}
|
||||
});
|
||||
});
|
||||
CSteamGroup.prototype.getMembers = function(addresses, callback) {
|
||||
if(typeof addresses === 'function') {
|
||||
callback = addresses;
|
||||
addresses = null;
|
||||
}
|
||||
|
||||
this._community.getGroupMembers(this.steamID, callback, null, null, addresses, 0);
|
||||
};
|
||||
|
||||
CSteamGroup.prototype.join = function(callback) {
|
||||
var form = {
|
||||
"action": "join",
|
||||
"sessionID": this._community.getSessionID()
|
||||
};
|
||||
|
||||
var self = this;
|
||||
this._community.request.post("https://steamcommunity.com/gid/" + this.steamID.toString(), {"form": form}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode >= 400) {
|
||||
callback(err || "HTTP error " + response.statusCode);
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._community._checkCommunityError(body, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
this._community.joinGroup(this.steamID, callback);
|
||||
};
|
||||
|
||||
CSteamGroup.prototype.leave = function(callback) {
|
||||
var form = {
|
||||
"sessionID": this._community.getSessionID(),
|
||||
"action": "leaveGroup",
|
||||
"groupId": this.steamID.toString()
|
||||
};
|
||||
|
||||
var self = this;
|
||||
this._community._myProfile("home_process", form, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode >= 400) {
|
||||
callback(err || "HTTP error " + response.statusCode);
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._community._checkCommunityError(body, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
this._community.leaveGroup(this.steamID, callback);
|
||||
};
|
||||
|
||||
CSteamGroup.prototype.postAnnouncement = function(headline, content, callback) {
|
||||
var form = {
|
||||
"sessionID": this._community.getSessionID(),
|
||||
"action": "post",
|
||||
"headline": headline,
|
||||
"body": content
|
||||
};
|
||||
|
||||
this._community.request.post("https://steamcommunity.com/gid/" + this.steamID.toString() + "/announcements", {"form": form}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode >= 400) {
|
||||
callback(err || "HTTP error " + response.statusCode);
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._community._checkCommunityError(body, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
this._community.postGroupAnnouncement(this.steamID, headline, content, callback);
|
||||
};
|
||||
|
||||
CSteamGroup.prototype.scheduleEvent = function(name, type, description, time, server, callback) {
|
||||
// Event types: ChatEvent - Chat, OtherEvent - A lil somethin somethin, PartyEvent - Party!, MeetingEvent - Important meeting, SpecialCauseEvent - Special cause (charity ball?), MusicAndArtsEvent - Music or Art type thing, SportsEvent - Sporting endeavor, TripEvent - Out of town excursion
|
||||
// Passing a number for type will make it a game event for that appid
|
||||
|
||||
if(typeof server === 'function') {
|
||||
callback = server;
|
||||
server = {"ip": "", "password": ""};
|
||||
} else if(typeof server === 'string') {
|
||||
server = {"ip": server, "password": ""};
|
||||
} else {
|
||||
server = {"ip": "", "password": ""};
|
||||
}
|
||||
|
||||
var form = {
|
||||
"sessionid": this._community.getSessionID(),
|
||||
"action": "newEvent",
|
||||
"tzOffset": new Date().getTimezoneOffset() * -60,
|
||||
"name": name,
|
||||
"type": (typeof type === 'number' || !isNaN(parseInt(type, 10)) ? "GameEvent" : type),
|
||||
"appID": (typeof type === 'number' || !isNaN(parseInt(type, 10)) ? type : ''),
|
||||
"serverIP": server.ip,
|
||||
"serverPassword": server.password,
|
||||
"notes": description,
|
||||
"eventQuickTime": "now"
|
||||
};
|
||||
|
||||
if(time === null) {
|
||||
form.startDate = 'MM/DD/YY';
|
||||
form.startHour = '12';
|
||||
form.startMinute = '00';
|
||||
form.startAMPM = 'PM';
|
||||
form.timeChoice = 'quick';
|
||||
} else {
|
||||
form.startDate = (time.getMonth() + 1 < 10 ? '0' : '') + (time.getMonth() + 1) + '/' + (time.getDate() < 10 ? '0' : '') + time.getDate() + '/' + time.getFullYear().toString().substring(2);
|
||||
form.startHour = (time.getHours() === 0 ? '12' : (time.getHours() > 12 ? time.getHours() - 12 : time.getHours()));
|
||||
form.startMinute = (time.getMinutes() < 10 ? '0' : '') + time.getMinutes();
|
||||
form.startAMPM = (time.getHours() <= 12 ? 'AM' : 'PM');
|
||||
form.timeChoice = 'specific';
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this._community.request.post("https://steamcommunity.com/gid/" + this.steamID.toString() + "/eventEdit", {"form": form}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode >= 400) {
|
||||
callback(err || "HTTP error " + response.statusCode);
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._community._checkCommunityError(body, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
this._community.scheduleGroupEvent(this.steamID, name, type, description, time, server, callback);
|
||||
};
|
||||
|
||||
CSteamGroup.prototype.setPlayerOfTheWeek = function(steamID, callback) {
|
||||
var form = {
|
||||
"xml": 1,
|
||||
"action": "potw",
|
||||
"memberId": steamID.getSteam3RenderedID(),
|
||||
"sessionid": this._community.getSessionID()
|
||||
};
|
||||
|
||||
var self = this;
|
||||
this._community.request.post("https://steamcommunity.com/gid/" + this.steamID.toString() + "/potwEdit", {"form": form}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode != 200) {
|
||||
callback(err || "HTTP error " + response.statusCode);
|
||||
return;
|
||||
}
|
||||
|
||||
xml2js.parseString(body, function(err, results) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
});
|
||||
});
|
||||
this._community.setGroupPlayerOfTheWeek(this.steamID, steamID, callback);
|
||||
};
|
||||
|
||||
CSteamGroup.prototype.kick = function(steamID, callback) {
|
||||
var form = {
|
||||
"sessionID": this._community.getSessionID(),
|
||||
"action": "kick",
|
||||
"memberId": steamID.toString(),
|
||||
"queryString": ""
|
||||
};
|
||||
|
||||
var self = this;
|
||||
this._community.request.post("https://steamcommunity.com/gid/" + this.steamID.toString() + "/membersManage", {"form": form}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode >= 400) {
|
||||
callback(err || "HTTP error " + response.statusCode);
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._community._checkCommunityError(body, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
this._community.kickGroupMember(this.steamID, steamID, callback);
|
||||
};
|
||||
|
||||
CSteamGroup.prototype.getHistory = function(page, callback) {
|
||||
this._community.getGroupHistory(this.steamID, page, callback);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -41,6 +40,11 @@ SteamCommunity.prototype.getSteamUser = function(id, callback) {
|
||||
customurl = match[1];
|
||||
}
|
||||
}
|
||||
|
||||
if(!result.profile.steamID64) {
|
||||
callback(new Error("No valid response"));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, new CSteamUser(self, result.profile, customurl));
|
||||
});
|
||||
@@ -51,22 +55,26 @@ function CSteamUser(community, userData, customurl) {
|
||||
this._community = community;
|
||||
|
||||
this.steamID = new SteamID(userData.steamID64[0]);
|
||||
this.name = userData.steamID[0];
|
||||
this.onlineState = userData.onlineState[0];
|
||||
this.stateMessage = userData.stateMessage[0];
|
||||
this.privacyState = userData.privacyState[0];
|
||||
this.visibilityState = userData.visibilityState[0];
|
||||
this.avatarHash = userData.avatarIcon[0].match(/([0-9a-f]+)\.[a-z]+$/)[1];
|
||||
this.vacBanned = !!userData.vacBanned[0];
|
||||
this.tradeBanState = userData.tradeBanState[0];
|
||||
this.isLimitedAccount = !!userData.isLimitedAccount[0];
|
||||
this.customURL = userData.customURL ? userData.customURL[0] : customurl;
|
||||
this.name = processItem('steamID');
|
||||
this.onlineState = processItem('onlineState');
|
||||
this.stateMessage = processItem('stateMessage');
|
||||
this.privacyState = processItem('privacyState', 'uncreated');
|
||||
this.visibilityState = processItem('visibilityState');
|
||||
this.avatarHash = processItem('avatarIcon', '').match(/([0-9a-f]+)\.[a-z]+$/);
|
||||
if(this.avatarHash) {
|
||||
this.avatarHash = this.avatarHash[1];
|
||||
}
|
||||
|
||||
this.vacBanned = processItem('vacBanned', false) == 1;
|
||||
this.tradeBanState = processItem('tradeBanState', 'None');
|
||||
this.isLimitedAccount = processItem('isLimitedAccount') == 1;
|
||||
this.customURL = processItem('customURL', customurl);
|
||||
|
||||
if(this.visibilityState == 3) {
|
||||
this.memberSince = new Date(userData.memberSince[0].replace(/(\d{1,2})(st|nd|th)/, "$1"));
|
||||
this.location = userData.location[0] || null;
|
||||
this.realName = userData.realname[0] || null;
|
||||
this.summary = userData.summary[0] || null;
|
||||
this.memberSince = new Date(processItem('memberSince', '0').replace(/(\d{1,2})(st|nd|th)/, "$1"));
|
||||
this.location = processItem('location');
|
||||
this.realName = processItem('realname');
|
||||
this.summary = processItem('summary');
|
||||
} else {
|
||||
this.memberSince = null;
|
||||
this.location = null;
|
||||
@@ -89,6 +97,14 @@ function CSteamUser(community, userData, customurl) {
|
||||
return new SteamID(group.groupID64[0]);
|
||||
});
|
||||
}
|
||||
|
||||
function processItem(name, defaultVal) {
|
||||
if(!userData[name]) {
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
return userData[name][0];
|
||||
}
|
||||
}
|
||||
|
||||
CSteamUser.getAvatarURL = function(hash, size, protocol) {
|
||||
@@ -108,124 +124,38 @@ CSteamUser.prototype.getAvatarURL = function(size, protocol) {
|
||||
};
|
||||
|
||||
CSteamUser.prototype.addFriend = function(callback) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
var json;
|
||||
try {
|
||||
json = JSON.parse(body);
|
||||
} catch(e) {
|
||||
callback(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if(json.success) {
|
||||
callback();
|
||||
} else {
|
||||
callback("Unknown error");
|
||||
}
|
||||
});
|
||||
this._community.addFriend(this.steamID, callback);
|
||||
};
|
||||
|
||||
CSteamUser.prototype.acceptFriendRequest = function(callback) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
this._community.acceptFriendRequest(this.steamID, callback);
|
||||
};
|
||||
|
||||
CSteamUser.prototype.removeFriend = function(callback) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
this._community.removeFriend(this.steamID, callback);
|
||||
|
||||
};
|
||||
|
||||
CSteamUser.prototype.blockCommunication = function(callback) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
this._community.blockCommunication(this.steamID, callback);
|
||||
};
|
||||
|
||||
CSteamUser.prototype.unblockCommunication = function(callback) {
|
||||
var form = {"action": "unignore"};
|
||||
form['friends[' + this.steamID.toString() + ']'] = 1;
|
||||
|
||||
this._community._myProfile('friends/blocked/', form, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode >= 400) {
|
||||
callback(err || "HTTP error " + response.statusCode);
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
this._community.unblockCommunication(this.steamID, callback);
|
||||
};
|
||||
|
||||
CSteamUser.prototype.comment = function(message, callback) {
|
||||
this._community.request.post('https://steamcommunity.com/comment/Profile/post/' + this.steamID.toString() + '/-1/', {"form": {
|
||||
"comment": message,
|
||||
"count": 6,
|
||||
"sessionid": this._community.getSessionID()
|
||||
}}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode != 200) {
|
||||
callback(err || "HTTP error " + response.statusCode);
|
||||
return;
|
||||
}
|
||||
|
||||
var json;
|
||||
try {
|
||||
json = JSON.parse(body);
|
||||
} catch(e) {
|
||||
callback(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if(json.success) {
|
||||
callback();
|
||||
} else if(json.error) {
|
||||
callback(json.error);
|
||||
} else {
|
||||
callback("Unknown error");
|
||||
}
|
||||
});
|
||||
this._community.postUserComment(this.steamID, message, callback);
|
||||
};
|
||||
|
||||
CSteamUser.prototype.inviteToGroup = function(groupID, callback) {
|
||||
this._community.inviteUserToGroup(this.steamID, groupID, callback);
|
||||
};
|
||||
|
||||
CSteamUser.prototype.getInventoryContexts = function(callback) {
|
||||
this._community.getUserInventoryContexts(this.steamID, callback);
|
||||
};
|
||||
|
||||
CSteamUser.prototype.getInventory = function(appID, contextID, tradableOnly, callback) {
|
||||
this._community.getUserInventory(this.steamID, appID, contextID, tradableOnly, callback);
|
||||
};
|
||||
|
||||
@@ -28,28 +28,21 @@ SteamCommunity.PersonaStateFlag = {
|
||||
"OnlineUsingBigPicture": 1024
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.chatLogon = function(interval) {
|
||||
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.');
|
||||
this.getWebApiOauthToken(function(err, token) {
|
||||
if(err) {
|
||||
self.emit('debug', "Cannot get oauth token: " + err.message);
|
||||
self.chatState = SteamCommunity.ChatState.LogOnFailed;
|
||||
setTimeout(self.chatLogon.bind(self), 5000);
|
||||
return;
|
||||
@@ -58,8 +51,8 @@ SteamCommunity.prototype.chatLogon = function(interval) {
|
||||
self.request.post({
|
||||
"uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Logon/v1",
|
||||
"form": {
|
||||
"ui_mode": "web",
|
||||
"access_token": match[0]
|
||||
"ui_mode": uiMode,
|
||||
"access_token": token
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
@@ -80,7 +73,7 @@ SteamCommunity.prototype.chatLogon = function(interval) {
|
||||
self._chat = {
|
||||
"umqid": body.umqid,
|
||||
"message": body.message,
|
||||
"accessToken": match[0],
|
||||
"accessToken": token,
|
||||
"interval": interval
|
||||
};
|
||||
|
||||
@@ -124,12 +117,14 @@ SteamCommunity.prototype.chatMessage = function(recipient, text, type, callback)
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode != 200) {
|
||||
callback(err ? err.message : "HTTP error " + response.statusCode);
|
||||
} else if(body.error != 'OK') {
|
||||
callback(body.error);
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.error != 'OK') {
|
||||
callback(new Error(body.error));
|
||||
} else {
|
||||
callback();
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
335
components/confirmations.js
Normal file
335
components/confirmations.js
Normal file
@@ -0,0 +1,335 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
var Cheerio = require('cheerio');
|
||||
var SteamTotp = require('steam-totp');
|
||||
var Async = require('async');
|
||||
|
||||
var CConfirmation = require('../classes/CConfirmation.js');
|
||||
|
||||
/**
|
||||
* Get a list of your account's currently outstanding confirmations.
|
||||
* @param {int} time - The unix timestamp with which the following key was generated
|
||||
* @param {string} key - The confirmation key that was generated using the preceeding time and the tag "conf" (this key can be reused)
|
||||
* @param {SteamCommunity~getConfirmations} callback - Called when the list of confirmations is received
|
||||
*/
|
||||
SteamCommunity.prototype.getConfirmations = function(time, key, callback) {
|
||||
var self = this;
|
||||
|
||||
request(this, "conf", key, time, "conf", null, false, function(err, body) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var $ = Cheerio.load(body);
|
||||
var empty = $('#mobileconf_empty');
|
||||
if(empty.length > 0) {
|
||||
if(!$(empty).hasClass('mobileconf_done')) {
|
||||
// An error occurred
|
||||
callback(new Error(empty.find('div:nth-of-type(2)').text()));
|
||||
} else {
|
||||
callback(null, []);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We have something to confirm
|
||||
var confirmations = $('#mobileconf_list');
|
||||
if(!confirmations) {
|
||||
callback(new Error("Malformed response"));
|
||||
return;
|
||||
}
|
||||
|
||||
var confs = [];
|
||||
Array.prototype.forEach.call(confirmations.find('.mobileconf_list_entry'), function(conf) {
|
||||
conf = $(conf);
|
||||
|
||||
var img = conf.find('.mobileconf_list_entry_icon img');
|
||||
confs.push(new CConfirmation(self, {
|
||||
"id": conf.data('confid'),
|
||||
"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(),
|
||||
"time": conf.find('.mobileconf_list_entry_description>div:nth-of-type(3)').text().trim(),
|
||||
"icon": img.length < 1 ? '' : $(img).attr('src')
|
||||
}));
|
||||
});
|
||||
|
||||
callback(null, confs);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @callback SteamCommunity~getConfirmations
|
||||
* @param {Error|null} err - An Error object on failure, or null on success
|
||||
* @param {CConfirmation[]} confirmations - An array of CConfirmation objects
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the trade offer ID associated with a particular confirmation
|
||||
* @param {int} confID - The ID of the confirmation in question
|
||||
* @param {int} time - The unix timestamp with which the following key was generated
|
||||
* @param {string} key - The confirmation key that was generated using the preceeding time and the tag "details" (this key can be reused)
|
||||
* @param {SteamCommunity~getConfirmationOfferID} callback
|
||||
*/
|
||||
SteamCommunity.prototype.getConfirmationOfferID = function(confID, time, key, callback) {
|
||||
request(this, "details/" + confID, key, time, "details", null, true, function(err, body) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!body.success) {
|
||||
callback(new Error("Cannot load confirmation details"));
|
||||
return;
|
||||
}
|
||||
|
||||
var $ = Cheerio.load(body.html);
|
||||
var offer = $('.tradeoffer');
|
||||
if(offer.length < 1) {
|
||||
callback(null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, offer.attr('id').split('_')[1]);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @callback SteamCommunity~getConfirmationOfferID
|
||||
* @param {Error|null} err - An Error object on failure, or null on success
|
||||
* @param {string} offerID - The trade offer ID associated with the specified confirmation, or null if not for an offer
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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} 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 {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", {
|
||||
"op": accept ? "allow" : "cancel",
|
||||
"cid": confID,
|
||||
"ck": confKey
|
||||
}, true, function(err, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.success) {
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.message) {
|
||||
callback(new Error(body.message));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(new Error("Could not act on confirmation"));
|
||||
});
|
||||
};
|
||||
|
||||
function request(community, url, key, time, tag, params, json, callback) {
|
||||
params = params || {};
|
||||
params.p = "android:" + Date.now();
|
||||
params.a = community.steamID.getSteamID64();
|
||||
params.k = key;
|
||||
params.t = time;
|
||||
params.m = "android";
|
||||
params.tag = tag;
|
||||
|
||||
community.request.get({
|
||||
"uri": "https://steamcommunity.com/mobileconf/" + url,
|
||||
"qs": params,
|
||||
"json": !!json
|
||||
}, function(err, response, body) {
|
||||
if(community._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, body);
|
||||
});
|
||||
}
|
||||
|
||||
// Confirmation checker
|
||||
|
||||
/**
|
||||
* Start automatically polling our confirmations for new ones. The `confKeyNeeded` event will be emitted when we need a confirmation key, or `newConfirmation` when we get a new confirmation
|
||||
* @param {int} pollInterval - The interval, in milliseconds, at which we will poll for confirmations. This shouldn't be any less than 10,000 probably.
|
||||
* @param {Buffer|string|null} [identitySecret=null] - Your identity_secret. If passed, all confirmations will be automatically accepted and nothing will be emitted.
|
||||
*/
|
||||
SteamCommunity.prototype.startConfirmationChecker = function(pollInterval, identitySecret) {
|
||||
this._confirmationPollInterval = pollInterval;
|
||||
this._knownConfirmations = this._knownConfirmations || {};
|
||||
this._confirmationKeys = this._confirmationKeys || {};
|
||||
this._identitySecret = identitySecret;
|
||||
|
||||
if(this._confirmationTimer) {
|
||||
clearTimeout(this._confirmationTimer);
|
||||
}
|
||||
|
||||
setTimeout(this.checkConfirmations.bind(this), 500);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop automatically polling our confirmations.
|
||||
*/
|
||||
SteamCommunity.prototype.stopConfirmationChecker = function() {
|
||||
if(this._confirmationPollInterval) {
|
||||
delete this._confirmationPollInterval;
|
||||
}
|
||||
|
||||
if(this._identitySecret) {
|
||||
delete this._identitySecret;
|
||||
}
|
||||
|
||||
if(this._confirmationTimer) {
|
||||
clearTimeout(this._confirmationTimer);
|
||||
delete this._confirmationTimer;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Run the confirmation checker right now instead of waiting for the next poll.
|
||||
* Useful to call right after you send/accept an offer that needs confirmation.
|
||||
*/
|
||||
SteamCommunity.prototype.checkConfirmations = function() {
|
||||
if(this._confirmationTimer) {
|
||||
clearTimeout(this._confirmationTimer);
|
||||
delete this._confirmationTimer;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
if(!this._confirmationQueue) {
|
||||
this._confirmationQueue = Async.queue(function(conf, callback) {
|
||||
// Worker to process new confirmations
|
||||
if(self._identitySecret) {
|
||||
// We should accept this
|
||||
self.emit('debug', "Accepting confirmation #" + conf.id);
|
||||
var time = Math.floor(Date.now() / 1000);
|
||||
conf.respond(time, SteamTotp.getConfirmationKey(self._identitySecret, time, "allow"), true, function() {
|
||||
// If there was an error and it wasn't actually accepted, we'll pick it up again
|
||||
delete self._knownConfirmations[conf.id];
|
||||
setTimeout(callback, 1000); // Call the callback in 1 second, to make sure the time changes
|
||||
});
|
||||
} else {
|
||||
self.emit('newConfirmation', conf);
|
||||
setTimeout(callback, 1000); // Call the callback in 1 second, to make sure the time changes
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
|
||||
this.emit('debug', 'Checking confirmations');
|
||||
|
||||
this._confirmationCheckerGetKey('conf', function(err, key) {
|
||||
if(err) {
|
||||
resetTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
self.getConfirmations(key.time, key.key, function(err, confirmations) {
|
||||
if(err) {
|
||||
resetTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
var known = self._knownConfirmations;
|
||||
|
||||
var newOnes = confirmations.filter(function(conf) {
|
||||
return !known[conf.id];
|
||||
});
|
||||
|
||||
if(newOnes.length < 1) {
|
||||
resetTimer();
|
||||
return; // No new ones
|
||||
}
|
||||
|
||||
// We have new confirmations! 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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function resetTimer() {
|
||||
if(self._confirmationPollInterval) {
|
||||
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() {
|
||||
delete self._knownConfirmations[conf.id];
|
||||
});
|
||||
} else {
|
||||
self.emit('newConfirmation', conf);
|
||||
}
|
||||
}, handleNumber * 1000);
|
||||
}
|
||||
};
|
||||
|
||||
SteamCommunity.prototype._confirmationCheckerGetKey = function(tag, callback) {
|
||||
if(this._identitySecret) {
|
||||
if(tag == 'details') {
|
||||
// We don't care about details
|
||||
callback(new Error("Disabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
var time = Math.floor(Date.now() / 1000);
|
||||
callback(null, {"time": time, "key": SteamTotp.getConfirmationKey(this._identitySecret, time, tag)});
|
||||
return;
|
||||
}
|
||||
|
||||
var existing = this._confirmationKeys[tag];
|
||||
var reusable = ['conf', 'details'];
|
||||
|
||||
// See if we already have a key that we can reuse.
|
||||
if(reusable.indexOf(tag) != -1 && existing && (Date.now() - (existing.time * 1000) < (1000 * 60 * 5))) {
|
||||
callback(null, existing);
|
||||
return;
|
||||
}
|
||||
|
||||
// We need a fresh one
|
||||
var self = this;
|
||||
this.emit('confKeyNeeded', tag, function(err, time, key) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
self._confirmationKeys[tag] = {"time": time, "key": key};
|
||||
callback(null, {"time": time, "key": key});
|
||||
});
|
||||
};
|
||||
386
components/groups.js
Normal file
386
components/groups.js
Normal file
@@ -0,0 +1,386 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
var SteamID = require('steamid');
|
||||
var xml2js = require('xml2js');
|
||||
var Cheerio = require('cheerio');
|
||||
|
||||
SteamCommunity.prototype.getGroupMembers = function(gid, callback, members, link, addresses, addressIdx) {
|
||||
members = members || [];
|
||||
|
||||
if (!link) {
|
||||
if (typeof gid !== 'string') {
|
||||
// It's a SteamID object
|
||||
link = "http://steamcommunity.com/gid/" + gid.toString() + "/memberslistxml/?xml=1";
|
||||
} else {
|
||||
try {
|
||||
var sid = new SteamID(gid);
|
||||
if (sid.type == SteamID.Type.CLAN && sid.isValid()) {
|
||||
link = "http://steamcommunity.com/gid/" + sid.getSteamID64() + "/memberslistxml/?xml=1";
|
||||
} else {
|
||||
throw new Error("Doesn't particularly matter what this message is");
|
||||
}
|
||||
} catch (e) {
|
||||
link = "http://steamcommunity.com/groups/" + gid + "/memberslistxml/?xml=1";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addressIdx = addressIdx || 0;
|
||||
|
||||
var options = {};
|
||||
options.uri = link;
|
||||
|
||||
if(addresses) {
|
||||
if(addressIdx >= addresses.length) {
|
||||
addressIdx = 0;
|
||||
}
|
||||
|
||||
options.localAddress = addresses[addressIdx];
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.request(options, function(err, response, body) {
|
||||
if (self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
xml2js.parseString(body, function(err, result) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
members = members.concat(result.memberList.members[0].steamID64.map(function(steamID) {
|
||||
return new SteamID(steamID);
|
||||
}));
|
||||
|
||||
if (result.memberList.nextPageLink) {
|
||||
addressIdx++;
|
||||
self.getGroupMembers(gid, callback, members, result.memberList.nextPageLink[0], addresses, addressIdx);
|
||||
} else {
|
||||
callback(null, members);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.getGroupMembersEx = function(gid, addresses, callback) {
|
||||
this.getGroupMembers(gid, callback, null, null, addresses, 0);
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.joinGroup = function(gid, callback) {
|
||||
if(typeof gid === 'string') {
|
||||
gid = new SteamID(gid);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.request.post({
|
||||
"uri": "https://steamcommunity.com/gid/" + gid.getSteamID64(),
|
||||
"form": {
|
||||
"action": "join",
|
||||
"sessionID": this.getSessionID()
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode >= 400) {
|
||||
callback(err || new Error("HTTP error " + response.statusCode));
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._checkCommunityError(body, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.leaveGroup = function(gid, callback) {
|
||||
if(typeof gid === 'string') {
|
||||
gid = new SteamID(gid);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this._myProfile("home_process", {
|
||||
"sessionID": this.getSessionID(),
|
||||
"action": "leaveGroup",
|
||||
"groupId": gid.getSteamID64()
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode >= 400) {
|
||||
callback(err || new Error("HTTP error " + response.statusCode));
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._checkCommunityError(body, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content, callback) {
|
||||
if(typeof gid === 'string') {
|
||||
gid = new SteamID(gid);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.request.post({
|
||||
"uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/announcements",
|
||||
"form": {
|
||||
"sessionID": this.getSessionID(),
|
||||
"action": "post",
|
||||
"headline": headline,
|
||||
"body": content
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode >= 400) {
|
||||
callback(err || new Error("HTTP error " + response.statusCode));
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._checkCommunityError(body, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
})
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.scheduleGroupEvent = function(gid, name, type, description, time, server, callback) {
|
||||
if(typeof gid === 'string') {
|
||||
gid = new SteamID(gid);
|
||||
}
|
||||
|
||||
// Event types: ChatEvent - Chat, OtherEvent - A lil somethin somethin, PartyEvent - Party!, MeetingEvent - Important meeting, SpecialCauseEvent - Special cause (charity ball?), MusicAndArtsEvent - Music or Art type thing, SportsEvent - Sporting endeavor, TripEvent - Out of town excursion
|
||||
// Passing a number for type will make it a game event for that appid
|
||||
|
||||
if(typeof server === 'function') {
|
||||
callback = server;
|
||||
server = {"ip": "", "password": ""};
|
||||
} else if(typeof server === 'string') {
|
||||
server = {"ip": server, "password": ""};
|
||||
} else {
|
||||
server = {"ip": "", "password": ""};
|
||||
}
|
||||
|
||||
var form = {
|
||||
"sessionid": this.getSessionID(),
|
||||
"action": "newEvent",
|
||||
"tzOffset": new Date().getTimezoneOffset() * -60,
|
||||
"name": name,
|
||||
"type": (typeof type === 'number' || !isNaN(parseInt(type, 10)) ? "GameEvent" : type),
|
||||
"appID": (typeof type === 'number' || !isNaN(parseInt(type, 10)) ? type : ''),
|
||||
"serverIP": server.ip,
|
||||
"serverPassword": server.password,
|
||||
"notes": description,
|
||||
"eventQuickTime": "now"
|
||||
};
|
||||
|
||||
if(time === null) {
|
||||
form.startDate = 'MM/DD/YY';
|
||||
form.startHour = '12';
|
||||
form.startMinute = '00';
|
||||
form.startAMPM = 'PM';
|
||||
form.timeChoice = 'quick';
|
||||
} else {
|
||||
form.startDate = (time.getMonth() + 1 < 10 ? '0' : '') + (time.getMonth() + 1) + '/' + (time.getDate() < 10 ? '0' : '') + time.getDate() + '/' + time.getFullYear().toString().substring(2);
|
||||
form.startHour = (time.getHours() === 0 ? '12' : (time.getHours() > 12 ? time.getHours() - 12 : time.getHours()));
|
||||
form.startMinute = (time.getMinutes() < 10 ? '0' : '') + time.getMinutes();
|
||||
form.startAMPM = (time.getHours() <= 12 ? 'AM' : 'PM');
|
||||
form.timeChoice = 'specific';
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.request.post({
|
||||
"uri": "https://steamcommunity.com/gid/" + gid.toString() + "/eventEdit",
|
||||
"form": form
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode >= 400) {
|
||||
callback(err || new Error("HTTP error " + response.statusCode));
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._checkCommunityError(body, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.setGroupPlayerOfTheWeek = function(gid, steamID, callback) {
|
||||
if(typeof gid === 'string') {
|
||||
gid = new SteamID(gid);
|
||||
}
|
||||
|
||||
if(typeof steamID === 'string') {
|
||||
steamID = new SteamID(steamID);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.request.post({
|
||||
"uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/potwEdit",
|
||||
"form": {
|
||||
"xml": 1,
|
||||
"action": "potw",
|
||||
"memberId": steamID.getSteam3RenderedID(),
|
||||
"sessionid": this.getSessionID()
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode != 200) {
|
||||
callback(err || new Error("HTTP error " + response.statusCode));
|
||||
return;
|
||||
}
|
||||
|
||||
xml2js.parseString(body, function(err, results) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if(results.response.results[0] == 'OK') {
|
||||
callback(null, new SteamID(results.response.oldPOTW[0]), new SteamID(results.response.newPOTW[0]));
|
||||
} else {
|
||||
callback(new Error(results.response.results[0]));
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.kickGroupMember = function(gid, steamID, callback) {
|
||||
if(typeof gid === 'string') {
|
||||
gid = new SteamID(gid);
|
||||
}
|
||||
|
||||
if(typeof steamID === 'string') {
|
||||
steamID = new SteamID(steamID);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.request.post({
|
||||
"uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/membersManage",
|
||||
"form": {
|
||||
"sessionID": this.getSessionID(),
|
||||
"action": "kick",
|
||||
"memberId": steamID.getSteamID64(),
|
||||
"queryString": ""
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode >= 400) {
|
||||
callback(err || new Error("HTTP error " + response.statusCode));
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._checkCommunityError(body, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.getGroupHistory = function(gid, page, callback) {
|
||||
if(typeof gid === 'string') {
|
||||
gid = new SteamID(gid);
|
||||
}
|
||||
|
||||
if(typeof page === 'function') {
|
||||
callback = page;
|
||||
page = 1;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.request("https://steamcommunity.com/gid/" + gid.getSteamID64() + "/history?p=" + page, function(err, response, body) {
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._checkCommunityError(body, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $ = Cheerio.load(body);
|
||||
var output = {};
|
||||
|
||||
var paging = $('.group_paging p').text();
|
||||
var match = paging.match(/(\d+) - (\d+) of (\d+)/);
|
||||
|
||||
if(match) {
|
||||
output.first = parseInt(match[1], 10);
|
||||
output.last = parseInt(match[2], 10);
|
||||
output.total = parseInt(match[3], 10);
|
||||
}
|
||||
|
||||
output.items = [];
|
||||
var currentYear = (new Date()).getFullYear();
|
||||
var lastDate = Date.now();
|
||||
|
||||
Array.prototype.forEach.call($('.historyItem, .historyItemb'), function(item) {
|
||||
var data = {};
|
||||
|
||||
var $item = $(item);
|
||||
data.type = $item.find('.historyShort').text().replace(/ /g, '');
|
||||
|
||||
var users = $item.find('.whiteLink[data-miniprofile]');
|
||||
var sid;
|
||||
if(users[0]) {
|
||||
sid = new SteamID();
|
||||
sid.universe = SteamID.Universe.PUBLIC;
|
||||
sid.type = SteamID.Type.INDIVIDUAL;
|
||||
sid.instance = SteamID.Instance.DESKTOP;
|
||||
sid.accountid = $(users[0]).data('miniprofile');
|
||||
data.user = sid;
|
||||
}
|
||||
|
||||
if(users[1]) {
|
||||
sid = new SteamID();
|
||||
sid.universe = SteamID.Universe.PUBLIC;
|
||||
sid.type = SteamID.Type.INDIVIDUAL;
|
||||
sid.instance = SteamID.Instance.DESKTOP;
|
||||
sid.accountid = $(users[0]).data('miniprofile');
|
||||
data.actor = sid;
|
||||
}
|
||||
|
||||
// Figure out the date. Of course there's no year, because Valve
|
||||
var dateParts = $item.find('.historyDate').text().split('@');
|
||||
var date = dateParts[0].trim().replace(/(st|nd|th)$/, '').trim() + ', ' + currentYear;
|
||||
var time = dateParts[1].trim().replace(/(am|pm)/, ' $1');
|
||||
|
||||
date = new Date(date + ' ' + time + ' UTC');
|
||||
|
||||
// If this date is in the future, or it's later than the previous one, decrement the year
|
||||
if(date.getTime() > lastDate) {
|
||||
date.setFullYear(date.getFullYear() - 1);
|
||||
}
|
||||
|
||||
data.date = date;
|
||||
|
||||
output.items.push(data);
|
||||
});
|
||||
|
||||
callback(null, output);
|
||||
});
|
||||
};
|
||||
141
components/inventoryhistory.js
Normal file
141
components/inventoryhistory.js
Normal file
@@ -0,0 +1,141 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
var CEconItem = require('../classes/CEconItem.js');
|
||||
var SteamID = require('steamid');
|
||||
var request = require('request');
|
||||
var Cheerio = require('cheerio');
|
||||
var Async = require('async');
|
||||
|
||||
SteamCommunity.prototype.getInventoryHistory = function(options, callback) {
|
||||
if(typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.page = options.page || 1;
|
||||
|
||||
this.request("https://steamcommunity.com/my/inventoryhistory?l=english&p=" + options.page, 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");
|
||||
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) {
|
||||
callback(new Error("Malformed page: no trade found"));
|
||||
return;
|
||||
}
|
||||
var historyInventory = JSON.parse(match2[1]);
|
||||
|
||||
output.trades = [];
|
||||
var trades = $('.tradehistoryrow');
|
||||
|
||||
var item, trade, profileLink, items, j, econItem, timeMatch, time;
|
||||
for(var i = 0; i < trades.length; i++) {
|
||||
item = $(trades[i]);
|
||||
trade = {};
|
||||
|
||||
timeMatch = item.find('.tradehistory_timestamp').html().match(/(\d+):(\d+)(am|pm)/);
|
||||
if(timeMatch[1] == 12 && timeMatch[3] == 'am') {
|
||||
timeMatch[1] = 0;
|
||||
}
|
||||
|
||||
if(timeMatch[1] < 12 && timeMatch[3] == 'pm') {
|
||||
timeMatch[1] = parseInt(timeMatch[1], 10) + 12;
|
||||
}
|
||||
|
||||
time = (timeMatch[1] < 10 ? '0' : '') + timeMatch[1] + ':' + timeMatch[2] + ':00';
|
||||
|
||||
trade.date = new Date(item.find('.tradehistory_date').html() + ' ' + time);
|
||||
trade.partnerName = item.find('.tradehistory_event_description a').html();
|
||||
trade.partnerSteamID = null;
|
||||
trade.partnerVanityURL = null;
|
||||
trade.itemsReceived = [];
|
||||
trade.itemsGiven = [];
|
||||
|
||||
profileLink = item.find('.tradehistory_event_description a').attr('href');
|
||||
if(profileLink.indexOf('/profiles/') != -1) {
|
||||
trade.partnerSteamID = new SteamID(profileLink.match(/(\d+)$/)[1]);
|
||||
} else {
|
||||
trade.partnerVanityURL = profileLink.match(/\/([^\/]+)$/)[1];
|
||||
if(options.resolveVanityURLs && vanityURLs.indexOf(trade.partnerVanityURL) == -1) {
|
||||
vanityURLs.push(trade.partnerVanityURL);
|
||||
}
|
||||
}
|
||||
|
||||
items = item.find('.history_item');
|
||||
for(j = 0; j < items.length; j++) {
|
||||
match = body.match(new RegExp("HistoryPageCreateItemHover\\( '" + $(items[j]).attr('id') + "', (\\d+), '(\\d+)', '(\\d+)', '(\\d+)' \\);"));
|
||||
econItem = historyInventory[match[1]][match[2]][match[3]];
|
||||
|
||||
if($(items[j]).attr('id').indexOf('received') != -1) {
|
||||
trade.itemsReceived.push(new CEconItem(econItem));
|
||||
} else {
|
||||
trade.itemsGiven.push(new CEconItem(econItem));
|
||||
}
|
||||
}
|
||||
|
||||
output.trades.push(trade);
|
||||
}
|
||||
|
||||
if(options.resolveVanityURLs) {
|
||||
Async.map(vanityURLs, resolveVanityURL, function(err, results) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
for(i = 0; i < output.trades.length; i++) {
|
||||
if(output.trades[i].partnerSteamID || !output.trades[i].partnerVanityURL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the vanity URL
|
||||
for(j = 0; j < results.length; j++) {
|
||||
if(results[j].vanityURL == output.trades[i].partnerVanityURL) {
|
||||
output.trades[i].partnerSteamID = new SteamID(results[j].steamID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
callback(null, output);
|
||||
});
|
||||
} else {
|
||||
callback(null, output);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function resolveVanityURL(vanityURL, callback) {
|
||||
request("https://steamcommunity.com/id/" + vanityURL + "/?xml=1", function(err, response, body) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
var match = body.match(/<steamID64>(\d+)<\/steamID64>/);
|
||||
if(!match || !match[1]) {
|
||||
callback(new Error("Couldn't find Steam ID"));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, {"vanityURL": vanityURL, "steamID": match[1]});
|
||||
});
|
||||
}
|
||||
25
components/market.js
Normal file
25
components/market.js
Normal file
@@ -0,0 +1,25 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
var Cheerio = require('cheerio');
|
||||
|
||||
SteamCommunity.prototype.getMarketApps = function(callback) {
|
||||
var self = this;
|
||||
this.request('https://steamcommunity.com/market/', function (err, response, body) {
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
var $ = Cheerio.load(body);
|
||||
if ($('.market_search_game_button_group')) {
|
||||
apps = {};
|
||||
$('.market_search_game_button_group > a').each(function (i, element) {
|
||||
var e = Cheerio.load(element);
|
||||
var name = e('.game_button_game_name').text().trim();
|
||||
var url = element.attribs.href;
|
||||
var appid = url.substr(url.indexOf('=') + 1);
|
||||
apps[appid] = name;
|
||||
});
|
||||
callback(null, apps);
|
||||
} else {
|
||||
callback(new Error("Malformed response"));
|
||||
}
|
||||
});
|
||||
}
|
||||
368
components/profile.js
Normal file
368
components/profile.js
Normal file
@@ -0,0 +1,368 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
var SteamID = require('steamid');
|
||||
var Cheerio = require('cheerio');
|
||||
var fs = require('fs');
|
||||
|
||||
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].getSteamID64) {
|
||||
values.primary_group_steamid = settings[i].getSteamID64();
|
||||
} else {
|
||||
values.primary_group_steamid = new SteamID(settings[i]).getSteamID64();
|
||||
}
|
||||
|
||||
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': // deprecated
|
||||
case 'tradeConfirmation':
|
||||
values.tradeConfirmationSetting = settings[i] ? 1 : 0;
|
||||
break;
|
||||
|
||||
case 'marketConfirmation':
|
||||
values.marketConfirmationSetting = 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);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.uploadAvatar = function(image, format, callback) {
|
||||
if(typeof format === 'function') {
|
||||
callback = format;
|
||||
format = null;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
if(image instanceof Buffer) {
|
||||
doUpload(image);
|
||||
} else if(image.match(/^https?:\/\//)) {
|
||||
this.request.get({
|
||||
"uri": image,
|
||||
"encoding": null
|
||||
}, function(err, response, body) {
|
||||
if(err || response.statusCode != 200) {
|
||||
if(callback) {
|
||||
callback(err ? new Error(err.message + " downloading image") : new Error("HTTP error " + response.statusCode + " downloading image"));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!format) {
|
||||
format = response.headers['content-type'];
|
||||
}
|
||||
|
||||
doUpload(body);
|
||||
})
|
||||
} else {
|
||||
if(!format) {
|
||||
format = image.match(/\.([^\.]+)$/);
|
||||
if(format) {
|
||||
format = format[1];
|
||||
}
|
||||
}
|
||||
|
||||
fs.readFile(image, function(err, file) {
|
||||
if(err) {
|
||||
if(callback) {
|
||||
callback(err);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
doUpload(file);
|
||||
})
|
||||
}
|
||||
|
||||
function doUpload(buffer) {
|
||||
if(!format) {
|
||||
if(callback) {
|
||||
callback(new Error("Unknown image format"));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(format.match(/^image\//)) {
|
||||
format = format.substring(6);
|
||||
}
|
||||
|
||||
var filename = '';
|
||||
var contentType = '';
|
||||
|
||||
switch(format.toLowerCase()) {
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
filename = 'avatar.jpg';
|
||||
contentType = 'image/jpeg';
|
||||
break;
|
||||
|
||||
case 'png':
|
||||
filename = 'avatar.png';
|
||||
contentType = 'image/png';
|
||||
break;
|
||||
|
||||
case 'gif':
|
||||
filename = 'avatar.gif';
|
||||
contentType = 'image/gif';
|
||||
break;
|
||||
|
||||
default:
|
||||
if(callback) {
|
||||
callback(new Error("Unknown or invalid image format"));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self.request.post({
|
||||
"uri": "https://steamcommunity.com/actions/FileUploader",
|
||||
"formData": {
|
||||
"MAX_FILE_SIZE": buffer.length,
|
||||
"type": "player_avatar_image",
|
||||
"sId": self.steamID.getSteamID64(),
|
||||
"sessionid": self.getSessionID(),
|
||||
"doSub": 1,
|
||||
"json": 1,
|
||||
"avatar": {
|
||||
"value": buffer,
|
||||
"options": {
|
||||
"filename": filename,
|
||||
"contentType": contentType
|
||||
}
|
||||
}
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(err) {
|
||||
if(callback) {
|
||||
callback(err);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(body && !body.success && body.message) {
|
||||
if(callback) {
|
||||
callback(new Error(body.message));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(response.statusCode != 200) {
|
||||
if(callback) {
|
||||
callback(new Error("HTTP error " + response.statusCode));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!body || !body.success) {
|
||||
if(callback) {
|
||||
callback(new Error("Malformed response"));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(callback) {
|
||||
callback(null, body.images.full);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
159
components/twofactor.js
Normal file
159
components/twofactor.js
Normal file
@@ -0,0 +1,159 @@
|
||||
var SteamTotp = require('steam-totp');
|
||||
var SteamCommunity = require('../index.js');
|
||||
|
||||
var ETwoFactorTokenType = {
|
||||
"None": 0, // No token-based two-factor authentication
|
||||
"ValveMobileApp": 1, // Tokens generated using Valve's special charset (5 digits, alphanumeric)
|
||||
"ThirdParty": 2 // Tokens generated using literally everyone else's standard charset (6 digits, numeric). This is disabled.
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.enableTwoFactor = function(callback) {
|
||||
var self = this;
|
||||
|
||||
this.getWebApiOauthToken(function(err, token) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a random device ID hash
|
||||
var hash = require('crypto').createHash('sha1');
|
||||
hash.update(Math.random().toString());
|
||||
hash = hash.digest('hex');
|
||||
|
||||
self.request.post({
|
||||
"uri": "https://api.steampowered.com/ITwoFactorService/AddAuthenticator/v1/",
|
||||
"form": {
|
||||
"steamid": self.steamID.getSteamID64(),
|
||||
"access_token": token,
|
||||
"authenticator_time": Math.floor(Date.now() / 1000),
|
||||
"authenticator_type": ETwoFactorTokenType.ValveMobileApp,
|
||||
"device_identifier": 'android:' + hash,
|
||||
"sms_phone_id": "1"
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!body.response) {
|
||||
callback(new Error("Malformed response"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.response.status != 1) {
|
||||
var error = new Error("Error " + body.response.status);
|
||||
error.eresult = body.response.status;
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, body.response);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.finalizeTwoFactor = function(secret, activationCode, callback) {
|
||||
var attemptsLeft = 30;
|
||||
var diff = 0;
|
||||
|
||||
var self = this;
|
||||
this.getWebApiOauthToken(function(err, token) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
finalize(token);
|
||||
});
|
||||
|
||||
function finalize(token) {
|
||||
var code = SteamTotp.generateAuthCode(secret, diff);
|
||||
|
||||
self.request.post({
|
||||
"uri": "https://api.steampowered.com/ITwoFactorService/FinalizeAddAuthenticator/v1/",
|
||||
"form": {
|
||||
"steamid": self.steamID.getSteamID64(),
|
||||
"access_token": token,
|
||||
"authenticator_code": code,
|
||||
"authenticator_time": Math.floor(Date.now() / 1000),
|
||||
"activation_code": activationCode
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!body.response) {
|
||||
callback(new Error("Malformed response"));
|
||||
return;
|
||||
}
|
||||
|
||||
body = body.response;
|
||||
|
||||
if(body.server_time) {
|
||||
diff = body.server_time - Math.floor(Date.now() / 1000);
|
||||
}
|
||||
|
||||
if(body.status == 89) {
|
||||
callback(new Error("Invalid activation code"));
|
||||
} else if(body.want_more) {
|
||||
attemptsLeft--;
|
||||
diff += 30;
|
||||
|
||||
finalize(token);
|
||||
} else if(!body.success) {
|
||||
callback(new Error("Error " + body.status));
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.disableTwoFactor = function(revocationCode, callback) {
|
||||
var self = this;
|
||||
|
||||
this.getWebApiOauthToken(function(err, token) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
self.request.post({
|
||||
"uri": "https://api.steampowered.com/ITwoFactorService/RemoveAuthenticator/v1/",
|
||||
"form": {
|
||||
"steamid": self.steamID.getSteamID64(),
|
||||
"access_token": token,
|
||||
"revocation_code": revocationCode,
|
||||
"steamguard_scheme": 1
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!body.response) {
|
||||
callback(new Error("Malformed response"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!body.response.success) {
|
||||
callback(new Error("Request failed"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.response.status == 1) {
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
var error = new Error("Cannot remove authenticator (" + body.response.status + ")");
|
||||
error.eresult = body.response.status;
|
||||
callback(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
298
components/users.js
Normal file
298
components/users.js
Normal file
@@ -0,0 +1,298 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
var SteamID = require('steamid');
|
||||
var CEconItem = require('../classes/CEconItem.js');
|
||||
|
||||
SteamCommunity.prototype.addFriend = function(userID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.request.post({
|
||||
"uri": "https://steamcommunity.com/actions/AddFriendAjax",
|
||||
"form": {
|
||||
"accept_invite": 0,
|
||||
"sessionID": this.getSessionID(),
|
||||
"steamid": userID.toString()
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.success) {
|
||||
callback(null);
|
||||
} else {
|
||||
callback(new Error("Unknown error"));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.acceptFriendRequest = function(userID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.request.post({
|
||||
"uri": "https://steamcommunity.com/actions/AddFriendAjax",
|
||||
"form": {
|
||||
"accept_invite": 1,
|
||||
"sessionID": this.getSessionID(),
|
||||
"steamid": userID.toString()
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.removeFriend = function(userID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.request.post({
|
||||
"uri": "https://steamcommunity.com/actions/RemoveFriendAjax",
|
||||
"form": {
|
||||
"sessionID": this.getSessionID(),
|
||||
"steamid": userID.toString()
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.blockCommunication = function(userID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.request.post({
|
||||
"uri": "https://steamcommunity.com/actions/BlockUserAjax",
|
||||
"form": {
|
||||
"sessionID": this.getSessionID(),
|
||||
"steamid": userID.toString()
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.unblockCommunication = function(userID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var form = {"action": "unignore"};
|
||||
form['friends[' + userID.toString() + ']'] = 1;
|
||||
|
||||
this._myProfile('friends/blocked/', form, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(err || response.statusCode >= 400) {
|
||||
callback(err || new Error("HTTP error " + response.statusCode));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.postUserComment = function(userID, message, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.request.post({
|
||||
"uri": "https://steamcommunity.com/comment/Profile/post/" + userID.toString() + "/-1",
|
||||
"form": {
|
||||
"comment": message,
|
||||
"count": 6,
|
||||
"sessionid": this.getSessionID()
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.success) {
|
||||
callback(null);
|
||||
} else if(body.error) {
|
||||
callback(new Error(body.error));
|
||||
} else {
|
||||
callback(new Error("Unknown error"));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.inviteUserToGroup = function(userID, groupID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.request.post({
|
||||
"uri": "https://steamcommunity.com/actions/GroupInvite",
|
||||
"form": {
|
||||
"group": groupID.toString(),
|
||||
"invitee": userID.toString(),
|
||||
"json": 1,
|
||||
"sessionID": this.getSessionID(),
|
||||
"type": "groupInvite"
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(!callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.results == 'OK') {
|
||||
callback(null);
|
||||
} else if(body.results) {
|
||||
callback(new Error(body.results));
|
||||
} else {
|
||||
callback(new Error("Unknown error"));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) {
|
||||
if(typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
if(typeof userID === 'function') {
|
||||
callback = userID;
|
||||
userID = this.steamID;
|
||||
}
|
||||
|
||||
if(!userID) {
|
||||
callback(new Error("No SteamID specified and not logged in"));
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.request("https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory/", function(err, response, body) {
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var match = body.match(/var g_rgAppContextData = ([^\n]+);\r?\n/);
|
||||
if(!match) {
|
||||
callback(new Error("Malformed response"));
|
||||
return;
|
||||
}
|
||||
|
||||
var data;
|
||||
try {
|
||||
data = JSON.parse(match[1]);
|
||||
} catch(e) {
|
||||
callback(new Error("Malformed response"));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, data);
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, tradableOnly, callback) {
|
||||
var self = this;
|
||||
|
||||
if(typeof userID === 'string') {
|
||||
userID = new SteamID(userID);
|
||||
}
|
||||
|
||||
var endpoint = "/profiles/" + userID.getSteamID64();
|
||||
get([], []);
|
||||
|
||||
function get(inventory, currency, start) {
|
||||
self.request({
|
||||
"uri": "https://steamcommunity.com" + endpoint + "/inventory/json/" + appID + "/" + contextID,
|
||||
"qs": {
|
||||
"start": start,
|
||||
"trading": tradableOnly ? 1 : undefined
|
||||
},
|
||||
"json": true
|
||||
}, function(err, response, body) {
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!body || !body.success || !body.rgInventory || !body.rgDescriptions || !body.rgCurrency) {
|
||||
callback(new Error(body.Error || "Malformed response"));
|
||||
return;
|
||||
}
|
||||
|
||||
var 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)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
currency.push(new CEconItem(body.rgInventory[i], body.rgDescriptions, contextID));
|
||||
}
|
||||
|
||||
if(body.more) {
|
||||
var match = response.request.uri.href.match(/\/(profiles|id)\/([^\/]+)\//);
|
||||
if(match) {
|
||||
endpoint = "/" + match[1] + "/" + match[2];
|
||||
}
|
||||
|
||||
get(inventory, currency, body.more_start);
|
||||
} else {
|
||||
callback(null, inventory, currency);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
66
components/webapi.js
Normal file
66
components/webapi.js
Normal file
@@ -0,0 +1,66 @@
|
||||
var SteamCommunity = require('../index.js');
|
||||
|
||||
SteamCommunity.prototype.getWebApiKey = function(domain, callback) {
|
||||
var self = this;
|
||||
this.request({
|
||||
"uri": "https://steamcommunity.com/dev/apikey",
|
||||
"followRedirect": false
|
||||
}, function(err, response, body) {
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(body.match(/<h2>Access Denied<\/h2>/)) {
|
||||
return callback(new Error("Access Denied"));
|
||||
}
|
||||
|
||||
var match = body.match(/<p>Key: ([0-9A-F]+)<\/p>/);
|
||||
if(match) {
|
||||
// We already have an API key registered
|
||||
callback(null, match[1]);
|
||||
} else {
|
||||
// We need to register a new API key
|
||||
self.request.post('https://steamcommunity.com/dev/registerkey', {
|
||||
"form": {
|
||||
"domain": domain,
|
||||
"agreeToTerms": "agreed",
|
||||
"sessionid": self.getSessionID(),
|
||||
"Submit": "Register"
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.getWebApiKey(domain, callback);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.getWebApiOauthToken = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if( this.oAuthToken ) {
|
||||
return callback( null, this.oAuthToken );
|
||||
}
|
||||
|
||||
// Pull an oauth token from the webchat UI
|
||||
this.request("https://steamcommunity.com/chat", function(err, response, body) {
|
||||
if(self._checkHttpError(err, response, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(self._checkCommunityError(body, callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var match = body.match(/"([0-9a-f]{32})"/);
|
||||
if (!match) {
|
||||
callback(new Error("Malformed response"));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, match[1]);
|
||||
});
|
||||
};
|
||||
215
index.js
215
index.js
@@ -9,10 +9,30 @@ module.exports = SteamCommunity;
|
||||
|
||||
SteamCommunity.SteamID = SteamID;
|
||||
|
||||
function SteamCommunity() {
|
||||
function SteamCommunity(localAddress) {
|
||||
this._jar = Request.jar();
|
||||
this.request = Request.defaults({"jar": this._jar});
|
||||
this._captchaGid = -1;
|
||||
this.chatState = SteamCommunity.ChatState.Offline;
|
||||
|
||||
var defaults = {
|
||||
"jar": this._jar,
|
||||
"timeout": 50000
|
||||
};
|
||||
|
||||
if(localAddress) {
|
||||
defaults.localAddress = localAddress;
|
||||
}
|
||||
|
||||
this.request = Request.defaults(defaults);
|
||||
|
||||
// English
|
||||
this._jar.setCookie(Request.cookie('Steam_Language=english'), 'https://steamcommunity.com');
|
||||
|
||||
// UTC
|
||||
this._jar.setCookie(Request.cookie('timezoneOffset=0,0'), 'https://steamcommunity.com');
|
||||
|
||||
this._jar.setCookie(Request.cookie("mobileClientVersion=0 (2.1.3)"), "https://steamcommunity.com");
|
||||
this._jar.setCookie(Request.cookie("mobileClient=android"), "https://steamcommunity.com");
|
||||
}
|
||||
|
||||
SteamCommunity.prototype.login = function(details, callback) {
|
||||
@@ -22,7 +42,21 @@ SteamCommunity.prototype.login = function(details, callback) {
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.request.post("https://steamcommunity.com/login/getrsakey/", {"form": {"username": details.accountName}}, function(err, response, body) {
|
||||
|
||||
// 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",
|
||||
"referer": "https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client",
|
||||
"user-agent": "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
|
||||
"accept": "text/javascript, text/html, application/xml, text/xml, */*"
|
||||
};
|
||||
|
||||
this.request.post("https://steamcommunity.com/login/getrsakey/", {
|
||||
"form": {
|
||||
"username": details.accountName
|
||||
},
|
||||
"headers": mobileHeaders
|
||||
}, function(err, response, body) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return;
|
||||
@@ -35,46 +69,66 @@ SteamCommunity.prototype.login = function(details, callback) {
|
||||
callback(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!json.publickey_mod || !json.publickey_exp) {
|
||||
callback(new Error("Invalid RSA key received"));
|
||||
return;
|
||||
}
|
||||
|
||||
var key = new RSA();
|
||||
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": "",
|
||||
"username": details.accountName
|
||||
"twofactorcode": details.twoFactorCode || "",
|
||||
"username": details.accountName,
|
||||
"oauth_client_id": "DE45CD61",
|
||||
"oauth_scope": "read_profile write_profile read_client write_client",
|
||||
"loginfriendlyname": "#login_emailauth_friendlyname_mobile"
|
||||
};
|
||||
|
||||
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,
|
||||
"headers": mobileHeaders
|
||||
}, 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();
|
||||
var oAuth = JSON.parse( body.oauth );
|
||||
self._jar.setCookie(Request.cookie('sessionid=' + sessionID), 'http://steamcommunity.com');
|
||||
|
||||
self.steamID = new SteamID(json.transfer_parameters.steamid);
|
||||
self.steamID = new SteamID(oAuth.steamid);
|
||||
self.oAuthToken = oAuth.oauth_token;
|
||||
|
||||
var cookies = self._jar.getCookieString("https://steamcommunity.com").split(';').map(function(cookie) {
|
||||
return cookie.trim();
|
||||
});
|
||||
@@ -123,42 +177,7 @@ SteamCommunity.prototype.getSessionID = function() {
|
||||
|
||||
function generateSessionID() {
|
||||
return Math.floor(Math.random() * 1000000000);
|
||||
};
|
||||
|
||||
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(body.match(/<h2>Access Denied<\/h2>/)) {
|
||||
return callback("Access Denied");
|
||||
}
|
||||
|
||||
var match = body.match(/<p>Key: ([0-9A-F]+)<\/p>/);
|
||||
if(match) {
|
||||
// We already have an API key registered
|
||||
callback(null, match[1]);
|
||||
} else {
|
||||
// We need to register a new API key
|
||||
self.request.post('https://steamcommunity.com/dev/registerkey', {
|
||||
"form": {
|
||||
"domain": domain,
|
||||
"agreeToTerms": "agreed",
|
||||
"sessionid": self.getSessionID(),
|
||||
"Submit": "Register"
|
||||
}
|
||||
}, function(err, response, body) {
|
||||
if(err || response.statusCode >= 400) {
|
||||
return callback(err.message || "HTTP error " + response.statusCode);
|
||||
}
|
||||
|
||||
self.getWebApiKey(domain, callback);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
SteamCommunity.prototype.parentalUnlock = function(pin, callback) {
|
||||
this.request.post("https://steamcommunity.com/parental/ajaxunlock", {
|
||||
@@ -171,8 +190,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') {
|
||||
@@ -188,9 +207,10 @@ SteamCommunity.prototype.parentalUnlock = function(pin, callback) {
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.getNotifications = function(callback) {
|
||||
var self = this;
|
||||
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 = {
|
||||
@@ -223,23 +243,40 @@ SteamCommunity.prototype.getNotifications = function(callback) {
|
||||
};
|
||||
|
||||
SteamCommunity.prototype.resetItemNotifications = function(callback) {
|
||||
var self = this;
|
||||
this.request.get("https://steamcommunity.com/my/inventory", function(err, response, body) {
|
||||
if(!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;
|
||||
}
|
||||
|
||||
@@ -264,6 +301,42 @@ SteamCommunity.prototype._myProfile = function(endpoint, form, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
SteamCommunity.prototype._checkHttpError = function(err, response, callback) {
|
||||
if(err) {
|
||||
callback(err);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(response.statusCode >= 300 && response.statusCode <= 399 && response.headers.location.indexOf('/login') != -1) {
|
||||
callback(new Error("Not Logged In"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if(response.statusCode >= 400) {
|
||||
var error = new Error("HTTP error " + response.statusCode);
|
||||
error.code = response.statusCode;
|
||||
callback(error);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
require('./components/chat.js');
|
||||
require('./components/profile.js');
|
||||
require('./components/market.js');
|
||||
require('./components/groups.js');
|
||||
require('./components/users.js');
|
||||
require('./components/inventoryhistory.js');
|
||||
require('./components/webapi.js');
|
||||
require('./components/twofactor.js');
|
||||
require('./components/confirmations.js');
|
||||
require('./classes/CMarketItem.js');
|
||||
require('./classes/CMarketSearchResult.js');
|
||||
require('./classes/CSteamGroup.js');
|
||||
require('./classes/CSteamUser.js');
|
||||
require('./components/chat.js');
|
||||
|
||||
/**
|
||||
@callback SteamCommunity~genericErrorCallback
|
||||
@param {Error|null} err - An Error object on failure, or null on success
|
||||
*/
|
||||
13
package.json
13
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "steamcommunity",
|
||||
"version": "2.2.0",
|
||||
"version": "3.14.0",
|
||||
"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,12 @@
|
||||
"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.61.0",
|
||||
"node-bignumber": "^1.2.1",
|
||||
"steamid": "^0.3.1",
|
||||
"xml2js": "^0.4.11",
|
||||
"cheerio": "^0.19.0",
|
||||
"async": "^1.4.2",
|
||||
"steam-totp": "^1.0.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user