Compare commits

...

44 Commits

Author SHA1 Message Date
Alexander Corn
57a3532860 3.18.6 2016-02-29 16:42:00 -05:00
Alexander Corn
facf335f0e Fixed captcha overriding more important errors (fixes #56) 2016-02-29 16:41:37 -05:00
Alexander Corn
1c3817b797 Fixed captcha URL (fixes #83) 2016-02-29 16:24:29 -05:00
Alexander Corn
74efd50f72 Added more informative error messages for RSA key retrieval failure 2016-02-06 23:48:26 -05:00
Alexander Corn
87e3665b01 Revised JSDoc language (#80) 2016-02-04 21:49:54 -05:00
Alexander Corn
0b94419de4 Merge pull request #76 from Aareksio/patch-1
Fix for issue #75
2016-01-25 11:47:34 -05:00
Arkadiusz Sygulski
defbd347cc Fix for issue #75
In received XML there's `isPrimary="0"`, which mostly likely causes the problem, which results the last group in xml to be set as primary. 
This should fix it.
2016-01-25 11:35:22 +01:00
Alexander Corn
7efc1f2fd0 3.18.5 2016-01-21 01:15:00 -05:00
Alexander Corn
05c2da89da Fixed crash when unable to check confirmations 2016-01-21 01:14:52 -05:00
Alexander Corn
b83c40419c 3.18.4 2016-01-20 23:05:07 -05:00
Alexander Corn
fb0097499d Added some more debug output to confirmation checker 2016-01-20 23:04:54 -05:00
Alexander Corn
c2983e3bcd RIP market and trade confirmation opt-out 2016-01-20 13:47:14 -05:00
Alexander Corn
80b3cc52d2 3.18.3 2016-01-19 11:30:02 -05:00
Alexander Corn
fa59b188a5 Use the standard device ID when enabling 2FA 2016-01-19 11:29:26 -05:00
Alexander Corn
e446cc3cd8 3.18.2 2016-01-18 18:47:02 -05:00
Alexander Corn
722ca6689b Removed excess whitespace 2016-01-18 18:46:45 -05:00
Alexander Corn
1276c71e4e Use same device ID for all trade confirmation requests 2016-01-18 18:38:50 -05:00
Alexander Corn
a51bd21153 3.18.1 2016-01-17 19:19:14 -05:00
Alexander Corn
f1bbfc4901 Removed unused arguments from CSteamGroup#deleteAnnouncement 2016-01-17 19:19:04 -05:00
Alexander Corn
eb8d094bf1 3.18.0 2016-01-17 19:10:02 -05:00
Alexander Corn
b1cca95ec0 Merge pull request #70 from LeCoffee/master
Adding ability to edit an annoucement.
2016-01-17 19:08:20 -05:00
Shaun Barratt
27ef78cc4b Added ability to deleteAnnoucement 2016-01-17 02:27:11 +00:00
Shaun Barratt
4223d88fd1 Catching HTTP errors properly
Specifying the Group in getAllGroupAnnouncements
Removing unnecessary comments
Made the callback for getAllGroupAnnouncements mandatory
2016-01-16 18:39:04 +00:00
Shaun Barratt
ac02f6cb49 Added getAllAnnouncements
Added example on how to use it
2016-01-16 00:06:42 +00:00
Shaun Barratt
d84601e256 Adding the whitespace back (blame atom.io!)
Converting spaces to tabs.
2016-01-15 21:28:43 +00:00
Shaun Barratt
4152865f5e Adding ability to edit an annoucement.
Added an example.
2016-01-15 17:48:57 +00:00
Alexander Corn
7627e6a3a0 3.17.2 2016-01-13 01:02:52 -05:00
Alexander Corn
bbcf0eb72f Merge pull request #65 from spddl/master
Error: You must fill out all fields in at least one language - fix
2016-01-13 01:01:58 -05:00
spddl
d51b6582e2 Error: You must fill out all fields in at least one language fix 2016-01-13 06:16:51 +01:00
Alexander Corn
bbd339b88b 3.17.1 2016-01-05 22:46:05 -05:00
Alexander Corn
fcaaa38085 Merge pull request #59 from Aareksio/master
Fixed chatMessage function
2016-01-05 22:44:54 -05:00
Mole
6d8d0c5fbb Fixed missing 'var self = this;' 2016-01-05 15:33:54 +01:00
Alexander Corn
08c9ab6809 3.17.0 2016-01-04 19:17:46 -05:00
Alexander Corn
c150425ee5 Use gzip for HTTP requests 2016-01-03 14:38:02 -05:00
Alexander Corn
5c2666e0f4 Merge pull request #55 from Mikxail/constructor-additional-options
additional options for SteamCommunity constructor
2015-12-28 00:24:11 -05:00
Alexander Corn
c64b6c7e99 Merge pull request #53 from Mikxail/bugfix-market-search
bugfix call performSearch
2015-12-28 00:17:26 -05:00
Mikhail Konovalov
318f9ab2b4 bugfix call performSearch 2015-12-27 16:53:15 +03:00
Mikhail Konovalov
3167034b97 additional options for SteamCommunity constructor 2015-12-27 16:08:06 +03:00
Alexander Corn
3bfe8c3713 3.16.0 2015-12-26 18:38:53 -05:00
Alexander Corn
0c4e026701 Require steamguard value in oAuthLogin method 2015-12-26 18:34:22 -05:00
Alexander Corn
c246011fe2 Added oauth token to login callback 2015-12-26 18:34:11 -05:00
Alexander Corn
c290e16dc5 Generate sessionids that follow the same pattern as Valve's 2015-12-26 18:19:35 -05:00
Alexander Corn
f8852ba6c4 Pretend to be Chrome 2015-12-26 18:17:01 -05:00
Alexander Corn
52ee65d9da Added oAuthLogin 2015-12-26 18:15:09 -05:00
11 changed files with 349 additions and 80 deletions

View File

@@ -69,7 +69,7 @@ function performSearch(request, qs, results, callback) {
callback(null, results);
} else {
qs.start += body.pagesize;
performSearch(request, qs, results, callback);
performSearch.call(self, request, qs, results, callback);
}
});
}

View File

@@ -64,7 +64,7 @@ CSteamGroup.prototype.getMembers = function(addresses, callback) {
callback = addresses;
addresses = null;
}
this._community.getGroupMembers(this.steamID, callback, null, null, addresses, 0);
};
@@ -76,10 +76,22 @@ CSteamGroup.prototype.leave = function(callback) {
this._community.leaveGroup(this.steamID, callback);
};
CSteamGroup.prototype.getAllAnnouncements = function(time, callback) {
this._community.getAllGroupAnnouncements(this.steamID, time, callback);
};
CSteamGroup.prototype.postAnnouncement = function(headline, content, callback) {
this._community.postGroupAnnouncement(this.steamID, headline, content, callback);
};
CSteamGroup.prototype.editAnnouncement = function(annoucementID, headline, content, callback) {
this._community.editGroupAnnouncement(this.steamID, annoucementID, headline, content, callback)
};
CSteamGroup.prototype.deleteAnnouncement = function(annoucementID, callback) {
this._community.deleteGroupAnnouncement(this.steamID, annoucementID, callback)
};
CSteamGroup.prototype.scheduleEvent = function(name, type, description, time, server, callback) {
this._community.scheduleGroupEvent(this.steamID, name, type, description, time, server, callback);
};

View File

@@ -90,7 +90,7 @@ function CSteamUser(community, userData, customurl) {
var self = this;
if(userData.groups && userData.groups[0] && userData.groups[0].group) {
this.groups = userData.groups[0].group.map(function(group) {
if(group['$'] && group['$'].isPrimary) {
if(group['$'] && group['$'].isPrimary === "1") {
self.primaryGroup = new SteamID(group.groupID64[0]);
}

View File

@@ -101,7 +101,8 @@ SteamCommunity.prototype.chatMessage = function(recipient, text, type, callback)
}
type = type || 'saytext';
var self = this;
this.request.post({
"uri": "https://api.steampowered.com/ISteamWebUserPresenceOAuth/Message/v1",
"form": {

View File

@@ -141,7 +141,7 @@ SteamCommunity.prototype.respondToConfirmation = function(confID, confKey, time,
function request(community, url, key, time, tag, params, json, callback) {
params = params || {};
params.p = "android:" + require('crypto').randomBytes(16).toString('hex');
params.p = SteamTotp.getDeviceID(community.steamID);
params.a = community.steamID.getSteamID64();
params.k = key;
params.t = time;
@@ -165,7 +165,7 @@ function request(community, url, key, time, tag, params, json, callback) {
/**
* 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 {int} pollInterval - The interval, in milliseconds, at which we will poll for confirmations. This should probably be at least 10,000 to avoid rate-limits.
* @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) {
@@ -239,6 +239,7 @@ SteamCommunity.prototype.checkConfirmations = function() {
self.getConfirmations(key.time, key.key, function(err, confirmations) {
if(err) {
self.emit('debug', "Can't check confirmations: " + err.message);
resetTimer();
return;
}
@@ -287,9 +288,14 @@ SteamCommunity.prototype.checkConfirmations = function() {
// 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);
self.emit('debug', 'Accepting confirmation #' + conf.id);
var time = Math.floor(Date.now() / 1000);
conf.respond(time, SteamTotp.getConfirmationKey(self._identitySecret, time, "allow"), true, function() {
conf.respond(time, SteamTotp.getConfirmationKey(self._identitySecret, time, "allow"), true, function(err) {
if (err) {
self.emit('debug', "Can't accept confirmation #" + conf.id + ": " + err.message);
}
// We'll just retry next time we poll
delete self._knownConfirmations[conf.id];
});
} else {

View File

@@ -125,6 +125,56 @@ SteamCommunity.prototype.leaveGroup = function(gid, callback) {
});
};
SteamCommunity.prototype.getAllGroupAnnouncements = function(gid, time, callback) {
if(typeof gid === 'string') {
gid = new SteamID(gid);
}
if(typeof time === 'function') {
callback = time;
time = new Date(0); // The beginnig of time...
}
var self = this;
this.request({
"uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/rss/"
}, function(err, response, body) {
if(self._checkHttpError(err, response, callback)) {
return;
}
if(self._checkCommunityError(body, callback)) {
return;
}
xml2js.parseString(body, function(err, results) {
if(err) {
return callback(err);
}
if(!results.rss.channel[0].item) {
return callback(null, []);
}
var announcements = results.rss.channel[0].item.map(function(announcement) {
var splitLink = announcement.link[0].split('/');
return {
headline: announcement.title[0],
content: announcement.description[0],
date: new Date(announcement.pubDate[0]),
author: announcement.author[0],
aid: splitLink[splitLink.length - 1]
}
}).filter(function(announcement) {
return (announcement.date > time);
});
return callback(null, announcements);
});
});
}
SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content, callback) {
if(typeof gid === 'string') {
gid = new SteamID(gid);
@@ -137,15 +187,82 @@ SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content
"sessionID": this.getSessionID(),
"action": "post",
"headline": headline,
"body": content
"body": content,
"languages[0][headline]": headline,
"languages[0][body]": content
}
}, function(err, response, body) {
if(!callback) {
return;
}
if(err || response.statusCode >= 400) {
callback(err || new Error("HTTP error " + response.statusCode));
if(self._checkHttpError(err, response, callback)) {
return;
}
if(self._checkCommunityError(body, callback)) {
return;
}
callback(null);
})
};
SteamCommunity.prototype.editGroupAnnouncement = function(gid, aid, headline, content, callback) {
if(typeof gid === 'string') {
gid = new SteamID(gid);
}
var self = this;
var submitData = {
"uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/announcements",
"form": {
"sessionID": this.getSessionID(),
"gid": aid,
"action": "update",
"headline": headline,
"body": content,
"languages[0][headline]": headline,
"languages[0][body]": content,
"languages[0][updated]": 1
}
}
this.request.post(submitData, function(err, response, body) {
if(!callback) {
return;
}
if(self._checkHttpError(err, response, callback)) {
return;
}
if(self._checkCommunityError(body, callback)) {
return;
}
callback(null);
})
};
SteamCommunity.prototype.deleteGroupAnnouncement = function(gid, aid, callback) {
if(typeof gid === 'string') {
gid = new SteamID(gid);
}
var self = this;
var submitData = {
"uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/announcements/delete/" + aid + "?sessionID=" + this.getSessionID(),
}
this.request.get(submitData, function(err, response, body) {
if(!callback) {
return;
}
if(self._checkHttpError(err, response, callback)) {
return;
}

View File

@@ -190,15 +190,6 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) {
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;
}
}

View File

@@ -16,11 +16,6 @@ SteamCommunity.prototype.enableTwoFactor = function(callback) {
return;
}
// Create a random device ID hash
var hash = require('crypto').createHash('sha1');
hash.update(self.steamID.getSteamID64());
hash = hash.digest('hex');
self.request.post({
"uri": "https://api.steampowered.com/ITwoFactorService/AddAuthenticator/v1/",
"form": {
@@ -28,7 +23,7 @@ SteamCommunity.prototype.enableTwoFactor = function(callback) {
"access_token": token,
"authenticator_time": Math.floor(Date.now() / 1000),
"authenticator_type": ETwoFactorTokenType.ValveMobileApp,
"device_identifier": 'android:' + hash,
"device_identifier": SteamTotp.getDeviceID(self.steamID),
"sms_phone_id": "1"
},
"json": true

View File

@@ -0,0 +1,109 @@
var SteamCommunity = require('../index.js');
var ReadLine = require('readline');
var fs = require('fs');
var community = new SteamCommunity();
var rl = ReadLine.createInterface({
"input": process.stdin,
"output": process.stdout
});
rl.question("Username: ", function(accountName) {
rl.question("Password: ", function(password) {
doLogin(accountName, password);
});
});
function doLogin(accountName, password, authCode, twoFactorCode) {
community.login({
"accountName": accountName,
"password": password,
"authCode": authCode,
"twoFactorCode": twoFactorCode
}, function(err, sessionID, cookies, steamguard) {
if(err) {
if(err.message == 'SteamGuardMobile') {
rl.question("Steam Authenticator Code: ", function(code) {
doLogin(accountName, password, null, code);
});
return;
}
if(err.message == 'SteamGuard') {
console.log("An email has been sent to your address at " + err.emaildomain);
rl.question("Steam Guard Code: ", function(code) {
doLogin(accountName, password, code);
});
return;
}
console.log(err);
process.exit();
return;
}
console.log("Logged on!");
rl.question("Group ID: ", function(gid) {
community.getSteamGroup(gid, function(err, group) {
if (err) {
console.log(err);
process.exit(1);
}
group.getAllAnnouncements(function(err, announcements) {
if(announcements.length === 0) {
return console.log("This group has no announcements");
}
for (var i = announcements.length - 1; i >= 0; i--) {
console.log("[%s] %s %s: %s", announcements[i].date, announcements[i].aid, announcements[i].author, announcements[i].content);
};
rl.question("Would you like to delete delete or edit an annoucement? (Type edit/delete): ", function(choice) {
rl.question("Annoucement ID: ", function(aid) {
if(choice === 'edit') {
rl.question("New title: ", function(header) {
rl.question("New body: ", function(content) {
// EW THE PYRAMID!
// Try replace this with delete!
editAnnouncement(group, aid, header, content);
});
});
} else {
deleteAnnouncement(group, aid);
}
});
});
});
});
});
});
}
function editAnnouncement(group, aid, header, content) {
// Actual community method.
group.editAnnouncement(aid, header, content, function(error) {
if(!error) {
console.log("Annoucement edited!");
} else {
console.log("Unable to edit annoucement! %j", error);
process.exit(1);
}
});
}
function deleteAnnouncement(group, aid) {
// group.deleteAnnouncement(aid);
// Or
group.deleteAnnouncement(aid, function(err) {
if(!err) {
console.log("Deleted");
} else {
console.log("Error deleting announcement.");
}
})
}

130
index.js
View File

@@ -3,28 +3,44 @@ var RSA = require('node-bignumber').Key;
var hex2b64 = require('node-bignumber').hex2b64;
var SteamID = require('steamid');
const USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36";
require('util').inherits(SteamCommunity, require('events').EventEmitter);
module.exports = SteamCommunity;
SteamCommunity.SteamID = SteamID;
function SteamCommunity(localAddress) {
function SteamCommunity(options) {
options = options || {};
this._jar = Request.jar();
this._captchaGid = -1;
this.chatState = SteamCommunity.ChatState.Offline;
var defaults = {
"jar": this._jar,
"timeout": 50000
"timeout": options.timeout || 50000,
"gzip": true,
"headers": {
"User-Agent": options.userAgent || USER_AGENT
}
};
if(localAddress) {
defaults.localAddress = localAddress;
if (typeof options == "string") {
options = {
localAddress: options
};
}
this._options = options;
if (options.localAddress) {
defaults.localAddress = options.localAddress;
}
this.request = Request.defaults(defaults);
this.request = options.request || Request.defaults();
this.request = this.request.defaults(defaults);
// English
this._jar.setCookie(Request.cookie('Steam_Language=english'), 'https://steamcommunity.com');
@@ -52,55 +68,43 @@ SteamCommunity.prototype.login = function(details, callback) {
this._jar.setCookie(Request.cookie("mobileClient=android"), "https://steamcommunity.com");
this.request.post("https://steamcommunity.com/login/getrsakey/", {
"form": {
"username": details.accountName
},
"headers": mobileHeaders
"form": {"username": details.accountName},
"headers": mobileHeaders,
"json": true
}, function(err, response, body) {
// Remove the mobile cookies
if(err) {
if (self._checkHttpError(err, response, callback)) {
deleteMobileCookies();
callback(err);
return;
}
var json;
try {
json = JSON.parse(body);
} catch(e) {
deleteMobileCookies();
callback(e);
return;
}
if(!json.publickey_mod || !json.publickey_exp) {
if(!body.publickey_mod || !body.publickey_exp) {
deleteMobileCookies();
callback(new Error("Invalid RSA key received"));
return;
}
var key = new RSA();
key.setPublic(json.publickey_mod, json.publickey_exp);
var form = {
"captcha_text": details.captcha || "",
"captchagid": self._captchaGid,
"emailauth": details.authCode || "",
"emailsteamid": "",
"password": hex2b64(key.encrypt(details.password)),
"remember_login": "true",
"rsatimestamp": json.timestamp,
"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"
};
key.setPublic(body.publickey_mod, body.publickey_exp);
self.request.post({
"uri": "https://steamcommunity.com/login/dologin/",
"json": true,
"form": form,
"form": {
"captcha_text": details.captcha || "",
"captchagid": self._captchaGid,
"emailauth": details.authCode || "",
"emailsteamid": "",
"password": hex2b64(key.encrypt(details.password)),
"remember_login": "true",
"rsatimestamp": body.timestamp,
"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",
"donotcache": Date.now()
},
"headers": mobileHeaders
}, function(err, response, body) {
deleteMobileCookies();
@@ -108,19 +112,20 @@ SteamCommunity.prototype.login = function(details, callback) {
if(self._checkHttpError(err, response, callback)) {
return;
}
var error;
if(!body.success && body.emailauth_needed) {
// Steam Guard (email)
var error = new Error("SteamGuard");
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;
} else if(!body.success && body.captcha_needed && body.message.match(/Please verify your humanity/)) {
error = new Error("CAPTCHA");
error.captchaurl = "https://steamcommunity.com/login/rendercaptcha/?gid=" + body.captcha_gid;
self._captchaGid = body.captcha_gid;
@@ -149,7 +154,7 @@ SteamCommunity.prototype.login = function(details, callback) {
}
}
callback(null, sessionID, cookies, steamguard);
callback(null, sessionID, cookies, steamguard, oAuth.oauth_token);
}
});
});
@@ -165,6 +170,39 @@ SteamCommunity.prototype.login = function(details, callback) {
}
};
SteamCommunity.prototype.oAuthLogin = function(steamguard, token, callback) {
steamguard = steamguard.split('||');
var steamID = new SteamID(steamguard[0]);
var self = this;
this.request.post({
"uri": "https://api.steampowered.com/IMobileAuthService/GetWGToken/v1/",
"form": {
"access_token": token
},
"json": true
}, function(err, response, body) {
if(self._checkHttpError(err, response, callback)) {
return;
}
if(!body.response || !body.response.token || !body.response.token_secure) {
callback(new Error("Malformed response"));
return;
}
var cookies = [
'steamLogin=' + encodeURIComponent(steamID.getSteamID64() + '||' + body.response.token),
'steamLoginSecure=' + encodeURIComponent(steamID.getSteamID64() + '||' + body.response.token_secure),
'steamMachineAuth' + steamID.getSteamID64() + '=' + steamguard[1],
'sessionid=' + self.getSessionID()
];
self.setCookies(cookies);
callback(null, self.getSessionID(), cookies);
});
};
SteamCommunity.prototype.setCookies = function(cookies) {
var self = this;
cookies.forEach(function(cookie) {
@@ -192,7 +230,7 @@ SteamCommunity.prototype.getSessionID = function() {
};
function generateSessionID() {
return Math.floor(Math.random() * 1000000000);
return require('crypto').randomBytes(12).toString('hex');
}
SteamCommunity.prototype.parentalUnlock = function(pin, callback) {

View File

@@ -1,6 +1,6 @@
{
"name": "steamcommunity",
"version": "3.15.0",
"version": "3.18.6",
"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,12 +13,12 @@
"url": "https://github.com/DoctorMcKay/node-steamcommunity.git"
},
"dependencies": {
"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"
"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.3.0"
}
}