Compare commits

...

74 Commits

Author SHA1 Message Date
Alex Corn
1031a4dbd7 3.44.1 2022-05-13 22:49:06 -04:00
Alex Corn
e28fe5ca05 Merge pull request #286 from Heartz66/patch-1
Fix Not Logged In
2022-05-13 22:44:56 -04:00
Heartz66
90a9ee1aa9 Fix Not Logged In
Current version doesn't correctly identify login state. This change should correctly identify the state.
2022-05-13 17:10:52 +02:00
Alex Corn
9a069e80fb 3.44.0 2022-02-02 03:36:23 -05:00
Alex Corn
640c47b933 Updated comment 2022-02-02 03:35:44 -05:00
Alex Corn
f5d65a9ad4 Added getBoosterPackCatalog and createBoosterPack 2022-02-02 03:15:50 -05:00
Alex Corn
7ae8a97a0f Slight refactor for help.js component 2021-07-22 23:58:47 -04:00
Alex Corn
ec1284e961 Make callback optional in removePackage/restorePackage 2021-07-22 03:30:42 -04:00
Alex Corn
dc4bb1b554 3.43.1 2021-07-22 03:25:57 -04:00
Alex Corn
cd90d7a563 Trim comments before returning them 2021-07-22 03:25:47 -04:00
Alex Corn
851c14bd93 3.43.0 2021-07-22 03:09:56 -04:00
Alex Corn
24a42e9658 Export EFriendRelationship with the E prefix 2021-07-22 03:05:23 -04:00
Alex Corn
8ec671d952 Reformat 2021-07-22 03:04:38 -04:00
Alex Corn
2821a5900c Merge pull request #267 from Revadike/feature-get-friends-list
Added getFriendsList
2021-07-22 03:04:16 -04:00
Alex Corn
2da6725283 Merge branch 'master' into feature-get-friends-list 2021-07-22 03:04:06 -04:00
Alex Corn
23a7940064 Merge branch 'master' into feature-get-friends-list 2021-07-22 03:03:24 -04:00
Alex Corn
165c963b1a Create new help component and move restore/removePackage to it 2021-07-22 03:01:57 -04:00
Alex Corn
429a76edcc Reformat 2021-07-22 02:56:03 -04:00
Alex Corn
3a224ff45f Merge pull request #262 from Revadike/feature-package-control
Added restorePackage & removePackage
2021-07-22 02:55:01 -04:00
Alex Corn
dfad525e4e Merge pull request #220 from Revadike/feature/profile-comments
Added deleteUserComment and getUserComments
2021-07-22 02:34:32 -04:00
Revadike
81877f4bb7 Use object.assign instead 2021-07-22 07:48:46 +02:00
Alex Corn
f6a3186865 Merge pull request #268 from Revadike/enums-update
Updated Enums
2021-06-05 02:41:02 -04:00
Revadike
e0687f8828 Updated Enums 2021-06-04 04:24:11 +02:00
Revadike
d54dd2c892 Added getFriendsList 2021-06-04 04:08:07 +02:00
Revadike
b4818d6d67 Bugfix 2021-06-04 03:26:24 +02:00
Revadike
d8eb07f046 re-use variable
Co-authored-by: Mr-VIT <vit@v1t.su>
2021-05-13 01:14:52 +02:00
Revadike
e6995e337e Added restorePackage & removePackage 2021-03-19 15:22:41 +01:00
Alex Corn
2bbdbf36ad 3.42.0 2020-11-05 02:55:53 -05:00
Alex Corn
e6311c0fcb Removed attempt to get oauth token from webchat 2020-11-05 02:42:31 -05:00
Alex Corn
2fc8d1f4aa Refactored gem sack code 2020-11-05 02:19:31 -05:00
Jack Nolddor
b00b871a5b Added gems/sack transformations (#260)
Added Sack of Gems <---> Gems transformations
2020-11-05 01:31:35 -05:00
Alex Corn
15079621d2 3.41.8 2020-10-03 04:54:58 -04:00
Alex Corn
d91c600eae Merge pull request #258 from theanht1/master
Fix parentalUnlock
2020-10-02 02:17:35 -04:00
Anh Mai
0f98403c16 feat: Add sessionid to form of parentalUnlock 2020-09-30 17:43:00 +07:00
Alex Corn
d9b6b55efc 3.41.7 2020-09-23 18:11:33 -04:00
Alex Corn
0fc62d2966 Merge pull request #257 from timi-owo/master
Fixed 'language' was missing from the 'CSteamUser.getInventoryContents'
2020-09-23 17:46:59 -04:00
timi-owo
5daf5f712a Fixed param 'language' was missing when calling 'CSteamUser.getInventoryContents' method 2020-09-24 01:44:32 +08:00
Alex Corn
737aa9dc41 Added comment 2020-07-01 00:52:15 -04:00
Alex Corn
07fb26047b 3.41.6 2020-06-30 22:36:48 -04:00
Alex Corn
2f9b24c447 Only attempt to delete this.currency if it actually exists 2020-06-30 22:24:04 -04:00
Alex Corn
211c625da0 3.41.5 2020-06-26 03:45:17 -04:00
Alex Corn
2e61a6ed70 Fixed editProfile and profileSettings for Steam update 2020-06-26 03:44:58 -04:00
Alex Corn
f0a3cb27f8 Merge pull request #250 from itsjfx/open-booster-pack-patch
Add openBoosterPack
2020-05-28 22:30:35 -04:00
Alex Corn
6ed33a100e 3.41.4 2020-05-28 00:15:39 -04:00
Alex Corn
d41a52638b Prevent timeOffset delete timeout from holding app open 2020-05-28 00:15:20 -04:00
jfx
58516d4ed1 Add openBoosterPack 2020-05-23 16:24:20 +10:00
Alex Corn
3f99c718d4 Modernized disable_twofactor example script 2020-04-04 21:53:34 -04:00
Alex Corn
b92c5d852d Modernized enable_twofactor example script 2020-04-04 21:53:32 -04:00
Alex Corn
f5da011cde Create FUNDING.yml 2020-03-10 03:51:16 -04:00
Alex Corn
3633ab032c 3.41.3 2019-11-08 03:33:06 -05:00
Alex Corn
76df74d724 Added hidden parameter to CSteamGroup#postAnnouncement 2019-11-08 03:33:00 -05:00
Alex Corn
8b5cd59021 v3.41.2 2019-11-08 03:27:57 -05:00
Alex Corn
25adcb101b 3.41.1 2019-11-08 03:24:42 -05:00
Alex Corn
d7ecad670d Merge pull request #237 from Heartz66/patch-1
Fix wrong date
2019-11-08 03:23:27 -05:00
Heartz66
8225ea5127 Fix wrong date
https://github.com/DoctorMcKay/node-steamcommunity/issues/236
2019-11-06 19:43:45 +01:00
Alex Corn
59bf4f1e92 Merge pull request #233 from Revadike/patch-1
Optional hidden parameter to postGroupAnnouncement
2019-10-15 01:13:03 +02:00
Revadike
a3468bf99b Optional hidden parameter to postGroupAnnouncement 2019-10-14 04:28:53 +02:00
Alex Corn
e299623452 Use different filenames for different images, similar to how Steam does it 2019-09-25 05:33:14 -04:00
Alex Corn
29b31009ce Merge remote-tracking branch 'origin/master' 2019-09-25 05:30:14 -04:00
Alex Corn
d4d0d7da26 3.41.0 2019-09-24 04:07:34 -04:00
Alex Corn
3c8488d441 Added method sendImageToUser 2019-09-24 04:06:37 -04:00
Alex Corn
3ea0995415 3.40.2 2019-09-21 04:27:55 -04:00
Alex Corn
21c4ba3e27 Restore cache_expiration from game-specific data for backwards compatibility
(Fixes #229)
2019-09-21 04:27:39 -04:00
Alex Corn
4d4df7c2a5 3.40.1 2019-09-07 05:06:06 -04:00
Alex Corn
aa0d4d32b7 Removed stats-reporter 2019-09-07 05:05:46 -04:00
Alex Corn
444a6d238d Merge pull request #225 from Revadike/patch-1
Added userID guard
2019-06-17 03:18:06 -04:00
Revadike
98373708e5 Added userID guard
Because I got this:
```
 2019-06-12 | 01:22:30.151 | ERROR | crashBot                       | Fatal error: TypeError: Cannot read property 'getSteamID64' of null
    at get (node_modules\steamcommunity\components\users.js:416:60)
    at SteamCommunity.getUserInventoryContents (node_modules\steamcommunity\components\users.js:412:2)
    at unpackSteamBoosterPacks (index.js:3588:21)
    at cleanupSteamInventory (index.js:3583:5)
    at SteamUser.refreshSteamWebSession (index.js:857:38)
    at Object.onceWrapper (events.js:273:13)
    at SteamUser.emit (events.js:182:13)
    at SteamUser.gotSteamWebSession (index.js:1754:16)
    at SteamUser.emit (events.js:182:13)
    at _apiRequest (node_modules\steam-user\components\web.js:61:9)
    at IncomingMessage.<anonymous> (node_modules\steam-user\components\webapi.js:82:4)
    at IncomingMessage.emit (events.js:187:15)
    at endReadableNT (_stream_readable.js:1094:12)
    at process._tickCallback (internal/process/next_tick.js:63:19)
```
2019-06-17 02:43:07 +02:00
Revadike
9ed377e843 Oopsy 2019-04-05 00:13:02 +02:00
Revadike
868ce9cdb4 Added missing functions 2019-04-05 00:10:00 +02:00
Revadike
8edc5020e1 Added getUserComments(userID, options, callback)
https://github.com/DoctorMcKay/node-steamcommunity/issues/208
2019-04-05 00:06:48 +02:00
Revadike
f2be027a63 Updated postUserComment and added deleteUserComment
https://github.com/DoctorMcKay/node-steamcommunity/issues/219
2019-04-04 23:30:28 +02:00
Alex Corn
fbfff1398b Merge remote-tracking branch 'origin/master'
# Conflicts:
#	.idea/encodings.xml
2019-02-14 00:29:44 -05:00
Alex Corn
6cd06c3adf Updated module type 2019-02-06 19:54:04 -05:00
Alex Corn
d35e3c107c Make sure CConfirmation.{id, creator} are always strings 2019-02-06 19:36:01 -05:00
27 changed files with 853 additions and 184 deletions

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: DoctorMcKay
custom: 'https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=N36YVAT42CZ4G&item_name=node%2dsteamcommunity&currency_code=USD'

View File

@@ -1,8 +1,5 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="JSON"> <codeStyleSettings language="JSON">
<indentOptions> <indentOptions>
<option name="USE_TAB_CHARACTER" value="true" /> <option name="USE_TAB_CHARACTER" value="true" />

View File

@@ -1,5 +1,6 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<state> <state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (1)" /> <option name="USE_PER_PROJECT_SETTINGS" value="true" />
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state> </state>
</component> </component>

1
.idea/modules.xml generated
View File

@@ -2,6 +2,7 @@
<project version="4"> <project version="4">
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/../node-steamcommunity Wiki/.idea/node-steamcommunity Wiki.iml" filepath="$PROJECT_DIR$/../node-steamcommunity Wiki/.idea/node-steamcommunity Wiki.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/steamcommunity.iml" filepath="$PROJECT_DIR$/.idea/steamcommunity.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/steamcommunity.iml" filepath="$PROJECT_DIR$/.idea/steamcommunity.iml" />
</modules> </modules>
</component> </component>

View File

@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4"> <module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true"> <component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$" /> <content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="node-steamcommunity Wiki" />
</component> </component>
</module> </module>

1
.idea/vcs.xml generated
View File

@@ -2,5 +2,6 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" /> <mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/../node-steamcommunity Wiki" vcs="Git" />
</component> </component>
</project> </project>

View File

@@ -9,8 +9,6 @@ This module provides an easy interface for the Steam Community website. This mod
It supports Steam Guard and CAPTCHAs. It supports Steam Guard and CAPTCHAs.
This reports anonymous usage statistics to the author. [See here](https://github.com/DoctorMcKay/node-stats-reporter) for more information.
**Have a question about the module or coding in general? *Do not create a GitHub issue.* GitHub issues are for feature **Have a question about the module or coding in general? *Do not create a GitHub issue.* GitHub issues are for feature
requests and bug reports. Instead, post in the [dedicated forum](https://dev.doctormckay.com/forum/8-node-steamcommunity/). requests and bug reports. Instead, post in the [dedicated forum](https://dev.doctormckay.com/forum/8-node-steamcommunity/).
Such issues may be ignored!** Such issues may be ignored!**

View File

@@ -5,9 +5,9 @@ module.exports = CConfirmation;
function CConfirmation(community, data) { function CConfirmation(community, data) {
Object.defineProperty(this, "_community", {"value": community}); Object.defineProperty(this, "_community", {"value": community});
this.id = data.id; this.id = data.id.toString();
this.type = data.type; this.type = data.type;
this.creator = data.creator; this.creator = data.creator.toString();
this.key = data.key; this.key = data.key;
this.title = data.title; this.title = data.title;
this.receiving = data.receiving; this.receiving = data.receiving;

View File

@@ -66,11 +66,31 @@ function CEconItem(item, description, contextID) {
this.market_fee_app = parseInt(match[1], 10); this.market_fee_app = parseInt(match[1], 10);
} }
// Restore cache_expiration, if we can (for CS:GO items)
if (this.appid == 730 && this.contextid == 2 && this.owner_descriptions) {
let description = this.owner_descriptions.find(d => d.value && d.value.indexOf('Tradable After ') == 0);
if (description) {
let date = new Date(description.value.substring(15).replace(/[,()]/g, ''));
if (date) {
this.cache_expiration = date.toISOString();
}
}
}
// If we have item_expiration, also set cache_expiration to the same value
if (this.item_expiration) {
this.cache_expiration = this.item_expiration;
}
if (this.actions === "") { if (this.actions === "") {
this.actions = []; this.actions = [];
} }
delete this.currency; // One wouldn't think that we need this if statement, but apparently v8 has a weird bug/quirk where deleting a
// property results in greatly increased memory usage. Because that makes sense.
if (this.currency) {
delete this.currency;
}
} }
CEconItem.prototype.getImageURL = function() { CEconItem.prototype.getImageURL = function() {

View File

@@ -78,8 +78,8 @@ CSteamGroup.prototype.getAllAnnouncements = function(time, callback) {
this._community.getAllGroupAnnouncements(this.steamID, time, callback); this._community.getAllGroupAnnouncements(this.steamID, time, callback);
}; };
CSteamGroup.prototype.postAnnouncement = function(headline, content, callback) { CSteamGroup.prototype.postAnnouncement = function(headline, content, hidden, callback) {
this._community.postGroupAnnouncement(this.steamID, headline, content, callback); this._community.postGroupAnnouncement(this.steamID, headline, content, hidden, callback);
}; };
CSteamGroup.prototype.editAnnouncement = function(annoucementID, headline, content, callback) { CSteamGroup.prototype.editAnnouncement = function(annoucementID, headline, content, callback) {

View File

@@ -69,7 +69,13 @@ function CSteamUser(community, userData, customurl) {
this.customURL = processItem('customURL', customurl); this.customURL = processItem('customURL', customurl);
if(this.visibilityState == 3) { if(this.visibilityState == 3) {
this.memberSince = new Date(processItem('memberSince', '0').replace(/(\d{1,2})(st|nd|th)/, "$1")); let memberSinceValue = processItem('memberSince', '0').replace(/(\d{1,2})(st|nd|th)/, "$1");
if (memberSinceValue.indexOf(',') === -1) {
memberSinceValue += ', ' + new Date().getFullYear();
}
this.memberSince = new Date(memberSinceValue);
this.location = processItem('location'); this.location = processItem('location');
this.realName = processItem('realname'); this.realName = processItem('realname');
this.summary = processItem('summary'); this.summary = processItem('summary');
@@ -148,6 +154,14 @@ CSteamUser.prototype.comment = function(message, callback) {
this._community.postUserComment(this.steamID, message, callback); this._community.postUserComment(this.steamID, message, callback);
}; };
CSteamUser.prototype.deleteComment = function(commentID, callback) {
this._community.deleteUserComment(this.steamID, commentID, callback);
};
CSteamUser.prototype.getComments = function(options, callback) {
this._community.getUserComments(this.steamID, options, callback);
};
CSteamUser.prototype.inviteToGroup = function(groupID, callback) { CSteamUser.prototype.inviteToGroup = function(groupID, callback) {
this._community.inviteUserToGroup(this.steamID, groupID, callback); this._community.inviteUserToGroup(this.steamID, groupID, callback);
}; };
@@ -177,10 +191,11 @@ CSteamUser.prototype.getInventory = function(appID, contextID, tradableOnly, cal
* @param {int} appID - The Steam application ID of the game for which you want an inventory * @param {int} appID - The Steam application ID of the game for which you want an inventory
* @param {int} contextID - The ID of the "context" within the game you want to retrieve * @param {int} contextID - The ID of the "context" within the game you want to retrieve
* @param {boolean} tradableOnly - true to get only tradable items and currencies * @param {boolean} tradableOnly - true to get only tradable items and currencies
* @param {string} [language] - The language of item descriptions to return. Omit for default (which may either be English or your account's chosen language)
* @param callback * @param callback
*/ */
CSteamUser.prototype.getInventoryContents = function(appID, contextID, tradableOnly, callback) { CSteamUser.prototype.getInventoryContents = function(appID, contextID, tradableOnly, language, callback) {
this._community.getUserInventoryContents(this.steamID, appID, contextID, tradableOnly, callback); this._community.getUserInventoryContents(this.steamID, appID, contextID, tradableOnly, language, callback);
}; };
/** /**
@@ -190,3 +205,13 @@ CSteamUser.prototype.getInventoryContents = function(appID, contextID, tradableO
CSteamUser.prototype.getProfileBackground = function(callback) { CSteamUser.prototype.getProfileBackground = function(callback) {
this._community.getUserProfileBackground(this.steamID, callback); this._community.getUserProfileBackground(this.steamID, callback);
}; };
/**
* Upload an image to Steam and send it to the target user over chat.
* @param {Buffer} imageContentsBuffer - The image contents, as a Buffer
* @param {{spoiler?: boolean}} [options]
* @param {function} callback
*/
CSteamUser.prototype.sendImage = function(imageContentsBuffer, options, callback) {
this._community.sendImageToUser(this.steamID, imageContentsBuffer, options, callback);
};

View File

@@ -172,7 +172,7 @@ SteamCommunity.prototype.acceptConfirmationForObject = function(identitySecret,
setTimeout(function() { setTimeout(function() {
// Delete the saved time offset after 12 hours because why not // Delete the saved time offset after 12 hours because why not
delete self._timeOffset; delete self._timeOffset;
}, 1000 * 60 * 60 * 12); }, 1000 * 60 * 60 * 12).unref();
}); });
} }

View File

@@ -156,22 +156,33 @@ SteamCommunity.prototype.getAllGroupAnnouncements = function(gid, time, callback
}, "steamcommunity"); }, "steamcommunity");
}; };
SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content, callback) { SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content, hidden, callback) {
if(typeof gid === 'string') { if(typeof gid === 'string') {
gid = new SteamID(gid); gid = new SteamID(gid);
} }
if(typeof hidden === 'function') {
callback = hidden;
hidden = false;
}
var self = this; var self = this;
var form = {
"sessionID": this.getSessionID(),
"action": "post",
"headline": headline,
"body": content,
"languages[0][headline]": headline,
"languages[0][body]": content
};
if(hidden) {
form.is_hidden = "is_hidden"
}
this.httpRequestPost({ this.httpRequestPost({
"uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/announcements", "uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/announcements",
"form": { form
"sessionID": this.getSessionID(),
"action": "post",
"headline": headline,
"body": content,
"languages[0][headline]": headline,
"languages[0][body]": content
}
}, function(err, response, body) { }, function(err, response, body) {
if(!callback) { if(!callback) {
return; return;

64
components/help.js Normal file
View File

@@ -0,0 +1,64 @@
const SteamCommunity = require('../index.js');
const Helpers = require('./helpers.js');
const HELP_SITE_DOMAIN = 'https://help.steampowered.com';
/**
* Restore a previously removed steam package from your steam account.
* @param {int|string} packageID
* @param {function} callback
*/
SteamCommunity.prototype.restorePackage = function(packageID, callback) {
this.httpRequestPost({
uri: HELP_SITE_DOMAIN + '/wizard/AjaxDoPackageRestore',
form: {
packageid: packageID,
sessionid: this.getSessionID(HELP_SITE_DOMAIN),
wizard_ajax: 1
},
json: true
}, wizardAjaxHandler(callback));
};
/**
* Remove a steam package from your steam account.
* @param {int|string} packageID
* @param {function} callback
*/
SteamCommunity.prototype.removePackage = function(packageID, callback) {
this.httpRequestPost({
uri: HELP_SITE_DOMAIN + '/wizard/AjaxDoPackageRemove',
form: {
packageid: packageID,
sessionid: this.getSessionID(HELP_SITE_DOMAIN),
wizard_ajax: 1
},
json: true
}, wizardAjaxHandler(callback));
};
/**
* Returns a handler for wizard ajax HTTP requests.
* @param {function} callback
* @returns {(function(*=, *, *): void)|*}
*/
function wizardAjaxHandler(callback) {
return (err, res, body) => {
if (!callback) {
return;
}
if (err) {
callback(err);
return;
}
if (!body.success) {
callback(body.errorMsg ? new Error(body.errorMsg) : Helpers.eresultError(body.success));
return;
}
callback(null);
};
}

View File

@@ -124,7 +124,7 @@ SteamCommunity.prototype._checkCommunityError = function(html, callback) {
return err; return err;
} }
if (typeof html === 'string' && html.match(/g_steamID = false;/) && html.match(/<h1>Sign In<\/h1>/)) { if (typeof html === 'string' && html.indexOf('g_steamID = false;') > -1 && html.indexOf('<div class="login_title">Sign In</div>') > -1) {
err = new Error("Not Logged In"); err = new Error("Not Logged In");
callback(err); callback(err);
this._notifySessionExpired(err); this._notifySessionExpired(err);

View File

@@ -1,5 +1,7 @@
var SteamCommunity = require('../index.js'); const SteamCommunity = require('../index.js');
var Cheerio = require('cheerio'); const Cheerio = require('cheerio');
const Helpers = require('./helpers.js');
/** /**
* Get a list of all apps on the market * Get a list of all apps on the market
@@ -109,6 +111,161 @@ SteamCommunity.prototype.turnItemIntoGems = function(appid, assetid, expectedGem
}) })
}; };
/**
* Open a booster pack.
* @param {int} appid
* @param {int|string} assetid
* @param {function} callback
*/
SteamCommunity.prototype.openBoosterPack = function(appid, assetid, callback) {
this._myProfile({
"endpoint": "ajaxunpackbooster/",
"json": true,
"checkHttpError": false
}, {
"appid": appid,
"communityitemid": assetid,
"sessionid": this.getSessionID()
}, (err, res, body) => {
if (err) {
callback(err);
return;
}
if (body.success && body.success != SteamCommunity.EResult.OK) {
let err = new Error(body.message || SteamCommunity.EResult[body.success]);
err.eresult = err.code = body.success;
callback(err);
return;
}
if (!body.rgItems) {
callback(new Error("Malformed response"));
return;
}
callback(null, body.rgItems);
})
};
/**
* Get the booster pack catalog to see what booster packs you can create
* @param {function} callback
*/
SteamCommunity.prototype.getBoosterPackCatalog = function(callback) {
this.httpRequestGet('https://steamcommunity.com/tradingcards/boostercreator/', (err, res, body) => {
if (err) {
callback(err);
return;
}
let idx = body.indexOf('CBoosterCreatorPage.Init(');
if (idx == -1) {
callback(new Error('Malformed response'));
return;
}
let lines = body.slice(idx).split('\n').map(l => l.trim());
for (let i = 1; i <= 4; i++) {
if (typeof lines[i] != 'string' || !lines[i].match(/,$/)) {
let err = new Error('Malformed response');
err.line = i;
callback(err);
return;
}
lines[i] = lines[i].replace(/,$/, '');
}
let boosterPackCatalog, totalGems, tradableGems, untradableGems;
try {
boosterPackCatalog = JSON.parse(lines[1]);
totalGems = parseInt(lines[2].match(/\d+/)[0], 10);
tradableGems = parseInt(lines[3].match(/\d+/)[0], 10);
untradableGems = parseInt(lines[4].match(/\d+/)[0], 10);
} catch (ex) {
let err = new Error('Malformed response');
err.inner = ex;
callback(err);
return;
}
let keyedCatalog = {};
boosterPackCatalog.forEach((app) => {
app.price = parseInt(app.price, 10);
app.unavailable = app.unavailable || false;
app.availableAtTime = app.available_at_time || null;
if (typeof app.availableAtTime == 'string') {
app.availableAtTime = Helpers.decodeSteamTime(app.availableAtTime);
}
delete app.available_at_time;
keyedCatalog[app.appid] = app;
});
callback(null, {
totalGems,
tradableGems,
untradableGems,
catalog: keyedCatalog
});
});
};
/**
* Create a booster pack using gems.
* @param {int} appid
* @param {boolean} [useUntradableGems=false]
* @param callback
*/
SteamCommunity.prototype.createBoosterPack = function(appid, useUntradableGems, callback) {
if (typeof useUntradableGems == 'function') {
callback = useUntradableGems;
useUntradableGems = false;
}
this.httpRequestPost({
uri: 'https://steamcommunity.com/tradingcards/ajaxcreatebooster/',
form: {
sessionid: this.getSessionID(),
appid,
series: 1,
// tradability_preference can be a value 1-3
// 1: Prefer using tradable gems, but use untradable if necessary
// 2: Only use tradable gems
// 3: Prefer using untradable gems, but use tradable if necessary
tradability_preference: useUntradableGems ? 3 : 2
},
json: true,
checkHttpError: false
}, (err, res, body) => {
if (err) {
callback(err);
return;
}
if (body.purchase_eresult && body.purchase_eresult != 1) {
callback(Helpers.eresultError(body.purchase_eresult));
return;
}
// We can now check HTTP status codes
if (this._checkHttpError(err, res, callback, body)) {
return;
}
callback(null, {
totalGems: parseInt(body.goo_amount, 10),
tradableGems: parseInt(body.tradable_goo_amount, 10),
untradableGems: parseInt(body.untradable_goo_amount, 10),
resultItem: body.purchase_result
});
});
};
/** /**
* Get details about a gift in your inventory. * Get details about a gift in your inventory.
* @param {string} giftID * @param {string} giftID
@@ -166,6 +323,7 @@ SteamCommunity.prototype.redeemGift = function(giftID, callback) {
} }
if (body.success && body.success != SteamCommunity.EResult.OK) { if (body.success && body.success != SteamCommunity.EResult.OK) {
let err = new Error(body.message || SteamCommunity.EResult[body.success]); let err = new Error(body.message || SteamCommunity.EResult[body.success]);
err.eresult = err.code = body.success; err.eresult = err.code = body.success;
callback(err); callback(err);
@@ -175,3 +333,55 @@ SteamCommunity.prototype.redeemGift = function(giftID, callback) {
callback(null); callback(null);
}); });
}; };
/**
* @param {int|string} assetid
* @param {int} denominationIn
* @param {int} denominationOut
* @param {int} quantityIn
* @param {int} quantityOut
* @param {function} callback
* @private
*/
SteamCommunity.prototype._gemExchange = function(assetid, denominationIn, denominationOut, quantityIn, quantityOut, callback) {
this._myProfile({
endpoint: 'ajaxexchangegoo/',
json: true,
checkHttpError: false
}, {
appid: 753,
assetid,
goo_denomination_in: denominationIn,
goo_amount_in: quantityIn,
goo_denomination_out: denominationOut,
goo_amount_out_expected: quantityOut,
sessionid: this.getSessionID()
}, (err, res, body) => {
if (err) {
callback(err);
return;
}
callback(Helpers.eresultError(body.success));
});
};
/**
* Pack gems into sack of gems.
* @param {int|string} assetid - ID of gem stack you want to pack into sacks
* @param {int} desiredSackCount - How many sacks you want. You must have at least this amount * 1000 gems in the stack you're packing
* @param {function} callback
*/
SteamCommunity.prototype.packGemSacks = function(assetid, desiredSackCount, callback) {
this._gemExchange(assetid, 1, 1000, desiredSackCount * 1000, desiredSackCount, callback);
};
/**
* Unpack sack of gems into gems.
* @param {int|string} assetid - ID of sack stack you want to unpack (say that 5 times fast)
* @param {int} sacksToUnpack
* @param {function} callback
*/
SteamCommunity.prototype.unpackGemSacks = function(assetid, sacksToUnpack, callback) {
this._gemExchange(assetid, 1000, 1, sacksToUnpack, sacksToUnpack * 1000, callback);
};

View File

@@ -34,31 +34,45 @@ SteamCommunity.prototype.setupProfile = function(callback) {
SteamCommunity.prototype.editProfile = function(settings, callback) { SteamCommunity.prototype.editProfile = function(settings, callback) {
var self = this; var self = this;
this._myProfile("edit", null, function(err, response, body) { this._myProfile('edit/info', null, function(err, response, body) {
if(err || response.statusCode != 200) { if (err || response.statusCode != 200) {
if(callback) { if (callback) {
callback(err || new Error("HTTP error " + response.statusCode)); callback(err || new Error('HTTP error ' + response.statusCode));
} }
return; return;
} }
var $ = Cheerio.load(body); var $ = Cheerio.load(body);
var form = $('#editForm'); var existingSettings = $('#profile_edit_config').data('profile-edit');
if(!form) { if (!existingSettings || !existingSettings.strPersonaName) {
if(callback) { if (callback) {
callback(new Error("Malformed response")); callback(new Error('Malformed response'));
} }
return; return;
} }
var values = {}; var values = {
form.serializeArray().forEach(function(item) { sessionID: self.getSessionID(),
values[item.name] = item.value; type: 'profileSave',
}); weblink_1_title: '',
weblink_1_url: '',
weblink_2_title: '',
weblink_2_url: '',
weblink_3_title: '',
weblink_3_url: '',
personaName: existingSettings.strPersonaName,
real_name: existingSettings.strRealName,
summary: existingSettings.strSummary,
country: existingSettings.LocationData.locCountryCode,
state: existingSettings.LocationData.locStateCode,
city: existingSettings.LocationData.locCityCode,
customURL: existingSettings.strCustomURL,
json: 1
};
for(var i in settings) { for (var i in settings) {
if(!settings.hasOwnProperty(i)) { if(!settings.hasOwnProperty(i)) {
continue; continue;
} }
@@ -92,6 +106,8 @@ SteamCommunity.prototype.editProfile = function(settings, callback) {
values.customURL = settings[i]; values.customURL = settings[i];
break; break;
// These don't work right now
/*
case 'background': case 'background':
// The assetid of our desired profile background // The assetid of our desired profile background
values.profile_background = settings[i]; values.profile_background = settings[i];
@@ -110,60 +126,55 @@ SteamCommunity.prototype.editProfile = function(settings, callback) {
} }
break; break;
*/
// TODO: profile showcases // TODO: profile showcases
} }
} }
self._myProfile("edit", values, function(err, response, body) { self._myProfile('edit', values, function(err, response, body) {
if (settings.customURL) { if (settings.customURL) {
delete self._profileURL; delete self._profileURL;
} }
if(err || response.statusCode != 200) { if (!callback) {
if(callback) {
callback(err || new Error("HTTP error " + response.statusCode));
}
return; return;
} }
// Check for an error if (err || response.statusCode != 200) {
var $ = Cheerio.load(body); callback(err || new Error('HTTP error ' + response.statusCode));
var error = $('#errorText .formRowFields'); return;
if(error) {
error = error.text().trim();
if(error) {
if(callback) {
callback(new Error(error));
}
return;
}
} }
if(callback) { try {
var json = JSON.parse(body);
if (!json.success || json.success != 1) {
callback(new Error(json.errmsg || 'Request was not successful'));
return;
}
callback(null); callback(null);
} catch (ex) {
callback(ex);
} }
}); });
}); });
}; };
SteamCommunity.prototype.profileSettings = function(settings, callback) { SteamCommunity.prototype.profileSettings = function(settings, callback) {
this._myProfile("edit/settings", null, (err, response, body) => { this._myProfile('edit/settings', null, (err, response, body) => {
if (err || response.statusCode != 200) { if (err || response.statusCode != 200) {
if (callback) { if (callback) {
callback(err || new Error("HTTP error " + response.statusCode)); callback(err || new Error('HTTP error ' + response.statusCode));
} }
return; return;
} }
var $ = Cheerio.load(body); var $ = Cheerio.load(body);
var existingSettings = $('.ProfileReactRoot[data-privacysettings]').data('privacysettings'); var existingSettings = $('#profile_edit_config').data('profile-edit');
if (!existingSettings) { if (!existingSettings || !existingSettings.Privacy) {
if(callback) { if (callback) {
callback(new Error("Malformed response")); callback(new Error('Malformed response'));
} }
return; return;
@@ -171,8 +182,8 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) {
// PrivacySettings => {PrivacyProfile, PrivacyInventory, PrivacyInventoryGifts, PrivacyOwnedGames, PrivacyPlaytime} // PrivacySettings => {PrivacyProfile, PrivacyInventory, PrivacyInventoryGifts, PrivacyOwnedGames, PrivacyPlaytime}
// eCommentPermission // eCommentPermission
var privacy = existingSettings.PrivacySettings; var privacy = existingSettings.Privacy.PrivacySettings;
var commentPermission = existingSettings.eCommentPermission; var commentPermission = existingSettings.Privacy.eCommentPermission;
for (var i in settings) { for (var i in settings) {
if (!settings.hasOwnProperty(i)) { if (!settings.hasOwnProperty(i)) {
@@ -211,18 +222,18 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) {
} }
this._myProfile({ this._myProfile({
"method": "POST", method: 'POST',
"endpoint": "ajaxsetprivacy/", endpoint: 'ajaxsetprivacy/',
"json": true, json: true,
"formData": { // it's multipart because lolvalve formData: { // it's multipart because lolvalve
"sessionid": this.getSessionID(), sessionid: this.getSessionID(),
"Privacy": JSON.stringify(privacy), Privacy: JSON.stringify(privacy),
"eCommentPermission": commentPermission eCommentPermission: commentPermission
} }
}, null, function(err, response, body) { }, null, function(err, response, body) {
if (err || response.statusCode != 200) { if (err || response.statusCode != 200) {
if (callback) { if (callback) {
callback(err || new Error("HTTP error " + response.statusCode)); callback(err || new Error('HTTP error ' + response.statusCode));
} }
return; return;
@@ -230,7 +241,7 @@ SteamCommunity.prototype.profileSettings = function(settings, callback) {
if (body.success != 1) { if (body.success != 1) {
if (callback) { if (callback) {
callback(new Error(body.success ? "Error " + body.success : "Request was not successful")); callback(new Error(body.success ? 'Error ' + body.success : 'Request was not successful'));
} }
return; return;

View File

@@ -1,4 +1,6 @@
const Cheerio = require('cheerio'); const Cheerio = require('cheerio');
const Crypto = require('crypto');
const imageSize = require('image-size');
const SteamID = require('steamid'); const SteamID = require('steamid');
const SteamCommunity = require('../index.js'); const SteamCommunity = require('../index.js');
@@ -134,7 +136,7 @@ SteamCommunity.prototype.postUserComment = function(userID, message, callback) {
"uri": "https://steamcommunity.com/comment/Profile/post/" + userID.toString() + "/-1", "uri": "https://steamcommunity.com/comment/Profile/post/" + userID.toString() + "/-1",
"form": { "form": {
"comment": message, "comment": message,
"count": 6, "count": 1,
"sessionid": this.getSessionID() "sessionid": this.getSessionID()
}, },
"json": true "json": true
@@ -149,9 +151,109 @@ SteamCommunity.prototype.postUserComment = function(userID, message, callback) {
} }
if(body.success) { if(body.success) {
const $ = Cheerio.load(body.comments_html);
const commentID = $('.commentthread_comment').attr('id').split('_')[1];
callback(null, commentID);
} else if(body.error) {
callback(new Error(body.error));
} else {
callback(new Error("Unknown error"));
}
}, "steamcommunity");
};
SteamCommunity.prototype.deleteUserComment = function(userID, commentID, callback) {
if(typeof userID === 'string') {
userID = new SteamID(userID);
}
var self = this;
this.httpRequestPost({
"uri": "https://steamcommunity.com/comment/Profile/delete/" + userID.toString() + "/-1",
"form": {
"gidcomment": commentID,
"start": 0,
"count": 1,
"sessionid": this.getSessionID(),
"feature2": -1
},
"json": true
}, function(err, response, body) {
if(!callback) {
return;
}
if (err) {
callback(err);
return;
}
if(body.success && !body.comments_html.includes(commentID)) {
callback(null); callback(null);
} else if(body.error) { } else if(body.error) {
callback(new Error(body.error)); callback(new Error(body.error));
} else if(body.comments_html.includes(commentID)) {
callback(new Error("Failed to delete comment"));
} else {
callback(new Error("Unknown error"));
}
}, "steamcommunity");
};
SteamCommunity.prototype.getUserComments = function(userID, options, callback) {
if(typeof userID === 'string') {
userID = new SteamID(userID);
}
if (typeof options === 'function') {
callback = options;
options = {};
}
var form = Object.assign({
"start": 0,
"count": 0,
"feature2": -1,
"sessionid": this.getSessionID()
}, options);
this.httpRequestPost({
"uri": "https://steamcommunity.com/comment/Profile/render/" + userID.toString() + "/-1",
"form": form,
"json": true
}, function(err, response, body) {
if(!callback) {
return;
}
if (err) {
callback(err);
return;
}
if(body.success) {
const $ = Cheerio.load(body.comments_html);
const comments = $(".commentthread_comment.responsive_body_text[id]").map((i, elem) => {
var $elem = $(elem),
$commentContent = $elem.find(".commentthread_comment_text");
return {
id: $elem.attr("id").split("_")[1],
author: {
steamID: new SteamID("[U:1:" + $elem.find("[data-miniprofile]").data("miniprofile") + "]"),
name: $elem.find("bdi").text(),
avatar: $elem.find(".playerAvatar img[src]").attr("src"),
state: $elem.find(".playerAvatar").attr("class").split(" ").pop()
},
date: new Date($elem.find(".commentthread_comment_timestamp").data("timestamp") * 1000),
text: $commentContent.text().trim(),
html: $commentContent.html().trim()
}
}).get();
callback(null, comments, body.total_count);
} else if(body.error) {
callback(new Error(body.error));
} else { } else {
callback(new Error("Unknown error")); callback(new Error("Unknown error"));
} }
@@ -397,17 +499,22 @@ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, t
* @param {function} callback * @param {function} callback
*/ */
SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, contextID, tradableOnly, language, callback) { SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, contextID, tradableOnly, language, callback) {
if (typeof language === 'function') {
callback = language;
language = "english";
}
if (!userID) {
callback(new Error("The user's SteamID is invalid or missing."));
return;
}
var self = this; var self = this;
if (typeof userID === 'string') { if (typeof userID === 'string') {
userID = new SteamID(userID); userID = new SteamID(userID);
} }
if (typeof language === 'function') {
callback = language;
language = "english";
}
var pos = 1; var pos = 1;
get([], []); get([], []);
@@ -503,3 +610,158 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont
return quickDescriptionLookup[key]; return quickDescriptionLookup[key];
} }
}; };
/**
* Upload an image to Steam and send it to another user over Steam chat.
* @param {SteamID|string} userID - Either a SteamID object or a string that can parse into one
* @param {Buffer} imageContentsBuffer - The image contents, as a Buffer
* @param {{spoiler?: boolean}} [options]
* @param {function} callback
*/
SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer, options, callback) {
if (typeof options == 'function') {
callback = options;
options = {};
}
options = options || {};
if (!userID) {
callback(new Error('The user\'s SteamID is invalid or missing'));
return;
}
if (typeof userID == 'string') {
userID = new SteamID(userID);
}
if (!Buffer.isBuffer(imageContentsBuffer)) {
callback(new Error('The image contents must be a Buffer containing an image'));
return;
}
var imageDetails = null;
try {
imageDetails = imageSize(imageContentsBuffer);
} catch (ex) {
callback(ex);
return;
}
var imageHash = Crypto.createHash('sha1');
imageHash.update(imageContentsBuffer);
imageHash = imageHash.digest('hex');
var filename = Date.now() + '_image.' + imageDetails.type;
this.httpRequestPost({
uri: 'https://steamcommunity.com/chat/beginfileupload/?l=english',
headers: {
referer: 'https://steamcommunity.com/chat/'
},
formData: { // it's multipart
sessionid: this.getSessionID(),
l: 'english',
file_size: imageContentsBuffer.length,
file_name: filename,
file_sha: imageHash,
file_image_width: imageDetails.width,
file_image_height: imageDetails.height,
file_type: 'image/' + (imageDetails.type == 'jpg' ? 'jpeg' : imageDetails.type)
},
json: true
}, (err, res, body) => {
if (err) {
if (body && body.success) {
var err2 = Helpers.eresultError(body.success);
if (body.message) {
err2.message = body.message;
}
callback(err2);
} else {
callback(err);
}
return;
}
if (body.success != 1) {
callback(Helpers.eresultError(body.success));
return;
}
var hmac = body.hmac;
var timestamp = body.timestamp;
var startResult = body.result;
if (!startResult || !startResult.ugcid || !startResult.url_host || !startResult.request_headers) {
callback(new Error('Malformed response'));
return;
}
// Okay, now we need to PUT the file to the provided URL
var uploadUrl = (startResult.use_https ? 'https' : 'http') + '://' + startResult.url_host + startResult.url_path;
var headers = {};
startResult.request_headers.forEach((header) => {
headers[header.name.toLowerCase()] = header.value;
});
this.httpRequest({
uri: uploadUrl,
method: 'PUT',
headers,
body: imageContentsBuffer
}, (err, res, body) => {
if (err) {
callback(err);
return;
}
// Now we need to commit the upload
this.httpRequestPost({
uri: 'https://steamcommunity.com/chat/commitfileupload/',
headers: {
referer: 'https://steamcommunity.com/chat/'
},
formData: { // it's multipart again
sessionid: this.getSessionID(),
l: 'english',
file_name: filename,
file_sha: imageHash,
success: '1',
ugcid: startResult.ugcid,
file_type: 'image/' + (imageDetails.type == 'jpg' ? 'jpeg' : imageDetails.type),
file_image_width: imageDetails.width,
file_image_height: imageDetails.height,
timestamp,
hmac,
friend_steamid: userID.getSteamID64(),
spoiler: options.spoiler ? '1' : '0'
},
json: true
}, (err, res, body) => {
if (err) {
callback(err);
return;
}
if (body.success != 1) {
callback(Helpers.eresultError(body.success));
return;
}
if (body.result.success != 1) {
// lol valve
callback(Helpers.eresultError(body.result.success));
return;
}
if (!body.result.details || !body.result.details.url) {
callback(new Error('Malformed response'));
return;
}
callback(null, body.result.details.url);
}, 'steamcommunity');
}, 'steamcommunity');
}, 'steamcommunity');
};

View File

@@ -49,25 +49,9 @@ SteamCommunity.prototype.getWebApiKey = function(domain, callback) {
* @param {function} callback * @param {function} callback
*/ */
SteamCommunity.prototype.getWebApiOauthToken = function(callback) { SteamCommunity.prototype.getWebApiOauthToken = function(callback) {
var self = this; if (this.oAuthToken) {
return callback(null, this.oAuthToken);
if( this.oAuthToken ) {
return callback( null, this.oAuthToken );
} }
// Pull an oauth token from the webchat UI callback(new Error('This operation requires an OAuth token, which can only be obtained from node-steamcommunity\'s `login` method.'));
this.httpRequest("https://steamcommunity.com/chat", function(err, response, body) {
if (err) {
callback(err);
return;
}
var match = body.match(/"([0-9a-f]{32})"/);
if (!match) {
callback(new Error("Malformed response"));
return;
}
callback(null, match[1]);
}, "steamcommunity");
}; };

View File

@@ -1,18 +1,19 @@
var SteamCommunity = require('../index.js'); // If you aren't running this script inside of the repository, replace the following line with:
var ReadLine = require('readline'); // const SteamCommunity = require('steamcommunity');
var fs = require('fs'); const SteamCommunity = require('../index.js');
const ReadLine = require('readline');
var community = new SteamCommunity(); let community = new SteamCommunity();
var rl = ReadLine.createInterface({ let rl = ReadLine.createInterface({
"input": process.stdin, input: process.stdin,
"output": process.stdout output: process.stdout
}); });
rl.question("Username: ", function(accountName) { rl.question('Username: ', (accountName) => {
rl.question("Password: ", function(password) { rl.question('Password: ', (password) => {
rl.question("Two-Factor Auth Code: ", function(authCode) { rl.question('Two-Factor Auth Code: ', (authCode) =>{
rl.question("Revocation Code: R", function(rCode) { rl.question('Revocation Code: R', (rCode) => {
doLogin(accountName, password, authCode, "", rCode); doLogin(accountName, password, authCode, '', rCode);
}); });
}); });
}); });
@@ -20,21 +21,21 @@ rl.question("Username: ", function(accountName) {
function doLogin(accountName, password, authCode, captcha, rCode) { function doLogin(accountName, password, authCode, captcha, rCode) {
community.login({ community.login({
"accountName": accountName, accountName: accountName,
"password": password, password: password,
"twoFactorCode": authCode, twoFactorCode: authCode,
"captcha": captcha captcha: captcha
}, function(err, sessionID, cookies, steamguard) { }, (err, sessionID, cookies, steamguard) => {
if(err) { if (err) {
if(err.message == 'SteamGuard') { if (err.message == 'SteamGuard') {
console.log("This account does not have two-factor authentication enabled."); console.log('This account does not have two-factor authentication enabled.');
process.exit(); process.exit();
return; return;
} }
if(err.message == 'CAPTCHA') { if (err.message == 'CAPTCHA') {
console.log(err.captchaurl); console.log(err.captchaurl);
rl.question("CAPTCHA: ", function(captchaInput) { rl.question('CAPTCHA: ', (captchaInput) => {
doLogin(accountName, password, authCode, captchaInput); doLogin(accountName, password, authCode, captchaInput);
}); });
@@ -46,15 +47,15 @@ function doLogin(accountName, password, authCode, captcha, rCode) {
return; return;
} }
console.log("Logged on!"); console.log('Logged on!');
community.disableTwoFactor("R" + rCode, function(err) { community.disableTwoFactor('R' + rCode, (err) => {
if(err) { if (err) {
console.log(err); console.log(err);
process.exit(); process.exit();
return; return;
} }
console.log("Two-factor authentication disabled!"); console.log('Two-factor authentication disabled!');
process.exit(); process.exit();
}); });
}); });

View File

@@ -1,45 +1,49 @@
var SteamCommunity = require('../index.js'); // If you aren't running this script inside of the repository, replace the following line with:
var ReadLine = require('readline'); // const SteamCommunity = require('steamcommunity');
var fs = require('fs'); const SteamCommunity = require('../index.js');
const ReadLine = require('readline');
const FS = require('fs');
var community = new SteamCommunity(); const EResult = SteamCommunity.EResult;
var rl = ReadLine.createInterface({
"input": process.stdin, let community = new SteamCommunity();
"output": process.stdout let rl = ReadLine.createInterface({
input: process.stdin,
output: process.stdout
}); });
rl.question("Username: ", function(accountName) { rl.question('Username: ', (accountName) => {
rl.question("Password: ", function(password) { rl.question('Password: ', (password) => {
doLogin(accountName, password); doLogin(accountName, password);
}); });
}); });
function doLogin(accountName, password, authCode, captcha) { function doLogin(accountName, password, authCode, captcha) {
community.login({ community.login({
"accountName": accountName, accountName: accountName,
"password": password, password: password,
"authCode": authCode, authCode: authCode,
"captcha": captcha captcha: captcha
}, function(err, sessionID, cookies, steamguard) { }, (err, sessionID, cookies, steamguard) => {
if(err) { if (err) {
if(err.message == 'SteamGuardMobile') { if (err.message == 'SteamGuardMobile') {
console.log("This account already has two-factor authentication enabled."); console.log('This account already has two-factor authentication enabled.');
process.exit(); process.exit();
return; return;
} }
if(err.message == 'SteamGuard') { if (err.message == 'SteamGuard') {
console.log("An email has been sent to your address at " + err.emaildomain); console.log(`An email has been sent to your address at ${err.emaildomain}`);
rl.question("Steam Guard Code: ", function (code) { rl.question('Steam Guard Code: ', (code) => {
doLogin(accountName, password, code); doLogin(accountName, password, code);
}); });
return; return;
} }
if(err.message == 'CAPTCHA') { if (err.message == 'CAPTCHA') {
console.log(err.captchaurl); console.log(err.captchaurl);
rl.question("CAPTCHA: ", function(captchaInput) { rl.question('CAPTCHA: ', (captchaInput) => {
doLogin(accountName, password, authCode, captchaInput); doLogin(accountName, password, authCode, captchaInput);
}); });
@@ -51,17 +55,17 @@ function doLogin(accountName, password, authCode, captcha) {
return; return;
} }
console.log("Logged on!"); console.log('Logged on!');
community.enableTwoFactor(function(err, response) { community.enableTwoFactor((err, response) => {
if(err) { if (err) {
if(err.eresult == 2) { if (err.eresult == EResult.Fail) {
console.log("Error: Failed to enable two-factor authentication. Do you have a phone number attached to your account?"); console.log('Error: Failed to enable two-factor authentication. Do you have a phone number attached to your account?');
process.exit(); process.exit();
return; return;
} }
if(err.eresult == 84) { if (err.eresult == EResult.RateLimitExceeded) {
console.log("Error: RateLimitExceeded. Try again later."); console.log('Error: RateLimitExceeded. Try again later.');
process.exit(); process.exit();
return; return;
} }
@@ -71,15 +75,16 @@ function doLogin(accountName, password, authCode, captcha) {
return; return;
} }
if(response.status != 1) { if (response.status != EResult.OK) {
console.log("Error: Status " + response.status); console.log(`Error: Status ${response.status}`);
process.exit(); process.exit();
return; return;
} }
console.log("Writing secrets to twofactor_" + community.steamID.getSteamID64() + ".json"); let filename = `twofactor_${community.steamID.getSteamID64()}.json`;
console.log("Revocation code: " + response.revocation_code); console.log(`Writing secrets to ${filename}`);
fs.writeFileSync("twofactor_" + community.steamID.getSteamID64() + ".json", JSON.stringify(response, null, "\t")); console.log(`Revocation code: ${response.revocation_code}`);
FS.writeFileSync(filename, JSON.stringify(response, null, '\t'));
promptActivationCode(response); promptActivationCode(response);
}); });
@@ -87,10 +92,10 @@ function doLogin(accountName, password, authCode, captcha) {
} }
function promptActivationCode(response) { function promptActivationCode(response) {
rl.question("SMS Code: ", function(smsCode) { rl.question('SMS Code: ', (smsCode) => {
community.finalizeTwoFactor(response.shared_secret, smsCode, function(err) { community.finalizeTwoFactor(response.shared_secret, smsCode, (err) => {
if(err) { if (err) {
if(err.message == "Invalid activation code") { if (err.message == 'Invalid activation code') {
console.log(err); console.log(err);
promptActivationCode(response); promptActivationCode(response);
return; return;
@@ -98,7 +103,7 @@ function promptActivationCode(response) {
console.log(err); console.log(err);
} else { } else {
console.log("Two-factor authentication enabled!"); console.log('Two-factor authentication enabled!');
} }
process.exit(); process.exit();

View File

@@ -1,5 +1,3 @@
require('@doctormckay/stats-reporter').setup(require('./package.json'));
const hex2b64 = require('node-bignumber').hex2b64; const hex2b64 = require('node-bignumber').hex2b64;
const Request = require('request'); const Request = require('request');
const RSA = require('node-bignumber').Key; const RSA = require('node-bignumber').Key;
@@ -16,6 +14,7 @@ module.exports = SteamCommunity;
SteamCommunity.SteamID = SteamID; SteamCommunity.SteamID = SteamID;
SteamCommunity.ConfirmationType = require('./resources/EConfirmationType.js'); SteamCommunity.ConfirmationType = require('./resources/EConfirmationType.js');
SteamCommunity.EResult = require('./resources/EResult.js'); SteamCommunity.EResult = require('./resources/EResult.js');
SteamCommunity.EFriendRelationship = require('./resources/EFriendRelationship.js');
function SteamCommunity(options) { function SteamCommunity(options) {
@@ -305,8 +304,8 @@ SteamCommunity.prototype.setCookies = function(cookies) {
}); });
}; };
SteamCommunity.prototype.getSessionID = function() { SteamCommunity.prototype.getSessionID = function(host = "http://steamcommunity.com") {
var cookies = this._jar.getCookieString("http://steamcommunity.com").split(';'); var cookies = this._jar.getCookieString(host).split(';');
for(var i = 0; i < cookies.length; i++) { for(var i = 0; i < cookies.length; i++) {
var match = cookies[i].trim().match(/([^=]+)=(.+)/); var match = cookies[i].trim().match(/([^=]+)=(.+)/);
if(match[1] == 'sessionid') { if(match[1] == 'sessionid') {
@@ -325,11 +324,13 @@ function generateSessionID() {
SteamCommunity.prototype.parentalUnlock = function(pin, callback) { SteamCommunity.prototype.parentalUnlock = function(pin, callback) {
var self = this; var self = this;
var sessionID = self.getSessionID();
this.httpRequestPost("https://steamcommunity.com/parental/ajaxunlock", { this.httpRequestPost("https://steamcommunity.com/parental/ajaxunlock", {
"json": true, "json": true,
"form": { "form": {
"pin": pin "pin": pin,
"sessionid": sessionID
} }
}, function(err, response, body) { }, function(err, response, body) {
if(!callback) { if(!callback) {
@@ -535,6 +536,37 @@ SteamCommunity.prototype._myProfile = function(endpoint, form, callback) {
} }
}; };
/**
* Returns an object whose keys are 64-bit SteamIDs, and whose values are values from the EFriendRelationship enum.
* Therefore, you can deduce your friends or blocked list from this object.
* @param {function} callback
*/
SteamCommunity.prototype.getFriendsList = function(callback) {
this.httpRequestGet({
"uri": "https://steamcommunity.com/textfilter/ajaxgetfriendslist",
"json": true
}, (err, res, body) => {
if (err) {
callback(err ? err : new Error('HTTP error ' + res.statusCode));
return;
}
if (body.success != 1) {
callback(Helpers.eresultError(body.success));
return;
}
if (!body.friendslist || !body.friendslist.friends) {
callback(new Error('Malformed response'));
return;
}
const friends = {};
body.friendslist.friends.forEach(friend => (friends[friend.ulfriendid] = friend.efriendrelationship));
callback(null, friends);
});
};
require('./components/http.js'); require('./components/http.js');
require('./components/chat.js'); require('./components/chat.js');
require('./components/profile.js'); require('./components/profile.js');
@@ -545,6 +577,7 @@ require('./components/inventoryhistory.js');
require('./components/webapi.js'); require('./components/webapi.js');
require('./components/twofactor.js'); require('./components/twofactor.js');
require('./components/confirmations.js'); require('./components/confirmations.js');
require('./components/help.js');
require('./classes/CMarketItem.js'); require('./classes/CMarketItem.js');
require('./classes/CMarketSearchResult.js'); require('./classes/CMarketSearchResult.js');
require('./classes/CSteamGroup.js'); require('./classes/CSteamGroup.js');

View File

@@ -1,6 +1,6 @@
{ {
"name": "steamcommunity", "name": "steamcommunity",
"version": "3.40.0", "version": "3.44.1",
"description": "Provides an interface for logging into and interacting with the Steam Community website", "description": "Provides an interface for logging into and interacting with the Steam Community website",
"keywords": [ "keywords": [
"steam", "steam",
@@ -21,14 +21,14 @@
"url": "https://github.com/DoctorMcKay/node-steamcommunity.git" "url": "https://github.com/DoctorMcKay/node-steamcommunity.git"
}, },
"dependencies": { "dependencies": {
"@doctormckay/stats-reporter": "^1.0.2", "async": "^2.6.3",
"request": "^2.86.0",
"node-bignumber": "^1.2.1",
"steamid": "^1.0.0",
"xml2js": "^0.4.11",
"cheerio": "0.22.0", "cheerio": "0.22.0",
"async": "^2.1.4", "image-size": "^0.8.2",
"steam-totp": "^1.3.0" "node-bignumber": "^1.2.1",
"request": "^2.88.0",
"steam-totp": "^1.5.0",
"steamid": "^1.1.3",
"xml2js": "^0.4.22"
}, },
"engines": { "engines": {
"node": ">=4.0.0" "node": ">=4.0.0"

View File

@@ -0,0 +1,23 @@
/**
* @enum EFriendRelationship
*/
module.exports = {
"None": 0,
"Blocked": 1,
"RequestRecipient": 2,
"Friend": 3,
"RequestInitiator": 4,
"Ignored": 5,
"IgnoredFriend": 6,
"SuggestedFriend": 7, // removed "was used by the original implementation of the facebook linking feature; but now unused."
// Value-to-name mapping for convenience
"0": "None",
"1": "Blocked",
"2": "RequestRecipient",
"3": "Friend",
"4": "RequestInitiator",
"5": "Ignored",
"6": "IgnoredFriend",
"7": "SuggestedFriend",
};

View File

@@ -9,7 +9,7 @@ module.exports = {
"Snooze": 4, "Snooze": 4,
"LookingToTrade": 5, "LookingToTrade": 5,
"LookingToPlay": 6, "LookingToPlay": 6,
"Max": 7, "Invisible": 7,
// Value-to-name mapping for convenience // Value-to-name mapping for convenience
"0": "Offline", "0": "Offline",
@@ -19,5 +19,5 @@ module.exports = {
"4": "Snooze", "4": "Snooze",
"5": "LookingToTrade", "5": "LookingToTrade",
"6": "LookingToPlay", "6": "LookingToPlay",
"7": "Max", "7": "Invisible",
}; };

View File

@@ -4,7 +4,8 @@
module.exports = { module.exports = {
"HasRichPresence": 1, "HasRichPresence": 1,
"InJoinableGame": 2, "InJoinableGame": 2,
"Golden": 4, // removed "no longer has any effect" "Golden": 4,
"RemotePlayTogether": 8,
"OnlineUsingWeb": 256, // removed "renamed to ClientTypeWeb" "OnlineUsingWeb": 256, // removed "renamed to ClientTypeWeb"
"ClientTypeWeb": 256, "ClientTypeWeb": 256,
"OnlineUsingMobile": 512, // removed "renamed to ClientTypeMobile" "OnlineUsingMobile": 512, // removed "renamed to ClientTypeMobile"
@@ -14,14 +15,18 @@ module.exports = {
"OnlineUsingVR": 2048, // removed "renamed to ClientTypeVR" "OnlineUsingVR": 2048, // removed "renamed to ClientTypeVR"
"ClientTypeVR": 2048, "ClientTypeVR": 2048,
"LaunchTypeGamepad": 4096, "LaunchTypeGamepad": 4096,
"LaunchTypeCompatTool": 8192,
// Value-to-name mapping for convenience // Value-to-name mapping for convenience
"1": "HasRichPresence", "1": "HasRichPresence",
"2": "InJoinableGame", "2": "InJoinableGame",
"4": "Golden", "4": "Golden",
"8": "RemotePlayTogether",
"256": "ClientTypeWeb", "256": "ClientTypeWeb",
"512": "ClientTypeMobile", "512": "ClientTypeMobile",
"1024": "ClientTypeTenfoot", "1024": "ClientTypeTenfoot",
"2048": "ClientTypeVR", "2048": "ClientTypeVR",
"4096": "LaunchTypeGamepad", "4096": "LaunchTypeGamepad",
"8192": "LaunchTypeCompatTool",
}; };

View File

@@ -123,6 +123,13 @@ module.exports = {
"WGNetworkSendExceeded": 110, "WGNetworkSendExceeded": 110,
"AccountNotFriends": 111, "AccountNotFriends": 111,
"LimitedUserAccount": 112, "LimitedUserAccount": 112,
"CantRemoveItem": 113,
"AccountHasBeenDeleted": 114,
"AccountHasAnExistingUserCancelledLicense": 115,
"DeniedDueToCommunityCooldown": 116,
"NoLauncherSpecified": 117,
"MustAgreeToSSA": 118,
"ClientNoLongerSupported": 119,
// Value-to-name mapping for convenience // Value-to-name mapping for convenience
"0": "Invalid", "0": "Invalid",
@@ -237,4 +244,11 @@ module.exports = {
"110": "WGNetworkSendExceeded", "110": "WGNetworkSendExceeded",
"111": "AccountNotFriends", "111": "AccountNotFriends",
"112": "LimitedUserAccount", "112": "LimitedUserAccount",
"113": "CantRemoveItem",
"114": "AccountHasBeenDeleted",
"115": "AccountHasAnExistingUserCancelledLicense",
"116": "DeniedDueToCommunityCooldown",
"117": "NoLauncherSpecified",
"118": "MustAgreeToSSA",
"119": "ClientNoLongerSupported",
}; };