Compare commits

...

41 Commits

Author SHA1 Message Date
Alex Corn
fa8d897590 3.50.3 2026-07-13 12:50:33 -04:00
Alex Corn
f7fe587312 Send correct user-agent for mobileconf requests
(fixes #371)
2026-07-13 12:50:15 -04:00
Alex Corn
08d14f19e6 3.50.2 2026-07-12 17:18:42 -04:00
DoctorMcKay
cfed757eab Merge pull request #369 from lrftw/fix/login-timeout
Fix login hanging indefinitely on steam-session timeout
2026-07-12 17:09:58 -04:00
DoctorMcKay
63defbb643 Merge pull request #370 from GVRLNKMK/fix-ceconitem-tradable-after-crash
Fix CEconItem crash when "Tradable/Marketable After" date isn't day-first
2026-07-12 17:09:19 -04:00
GVRLNKMK
6e20fbe299 Fix CEconItem crash when Tradable/Marketable After date isn't day-first
CEconItem destructured the day-first date regex match without a null
check, throwing 'match is not iterable' for CS2 items whose
owner_descriptions use the legacy month-first format (e.g. 'Tradable/
Marketable After Jul 11, 2025 (2:00:00)'). Running in the constructor,
this made getInventoryContents()/getUserInventoryContents() reject the
whole inventory.

Guard the null match, fall back to parsing the legacy month-first
format, and only set cache_expiration for a valid date. Day-first
strings are unchanged; backwards-compatible.
2026-07-10 17:25:18 +03:00
Lucas
59090242f9 Fix login hanging indefinitely on steam-session timeout
Modern login in components/login.js listens for 'authenticated' and
'error' events from steam-session's LoginSession, but not the 'timeout'
event. When loginTimeout (default 30s) elapses, steam-session emits
'timeout' and calls cancelLoginAttempt() without emitting an 'error'
event, causing the Promise to never settle and the login callback to
never be called.

Add a 'timeout' event listener that rejects the Promise with a
descriptive error.
2026-07-03 10:02:04 +02:00
Alex Corn
00e82f7e79 3.50.1 2026-06-28 17:48:51 -04:00
DoctorMcKay
7b901b147b Merge pull request #367 from ricardocefet2018/master
fix: Invalid time value
2026-06-28 17:44:21 -04:00
Ricardo Rocha
0f7fc98ab5 fix: Invalid time value 2026-06-26 19:14:28 -03:00
Alex Corn
2a12174a7c 3.50.0 2026-03-27 04:32:44 -04:00
DoctorMcKay
caa76ee84b Merge pull request #365 from DarkGL/feat/asset-properties
feat: Add asset_properties support to getInventoryContents
2026-03-27 04:29:47 -04:00
Rafal Wiecek
bc8893fedd feat: add asset properties to inventory items 2026-03-16 15:52:32 +01:00
Alex Corn
d3e90f6fd3 3.49.0 2025-07-19 05:00:47 -04:00
Alex Corn
a61b50b7a2 Added acknowledgeTradeProtection method 2025-07-19 04:59:15 -04:00
Alex Corn
39fe4a4106 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	package.json
2025-07-19 04:49:23 -04:00
Alex Corn
0d991c2655 3.48.8 2025-05-28 05:23:39 -04:00
Alex Corn
0a307de9b9 Prevent descriptions overwriting base econ item properties 2025-05-28 05:23:25 -04:00
Alex Corn
c0b8dcf3a2 3.48.7 2025-05-27 20:27:10 -04:00
DoctorMcKay
dcf00b4ea1 Merge pull request #354 from metzz1/master
fix: steam now only accepts 1000 <
2025-05-27 20:26:40 -04:00
metzz
ee0cc34512 fix: steam now only accepts 1000 < 2025-05-28 03:08:12 +03:00
Alex Corn
c52533ea06 3.48.6 2025-02-12 03:16:04 -05:00
Alex Corn
67774f86f7 Handle "empty" CS2 inventories that aren't actually empty 2025-02-12 03:15:54 -05:00
Alex Corn
2f03e09f50 Require steam-session 1.9.1 2025-02-12 03:05:15 -05:00
Alex Corn
2868b1f45f Fixed malformed sessionid returned in login callback 2025-02-12 03:05:07 -05:00
Alex Corn
ac222ef8c3 Add origin header to all non-GET requests 2025-02-12 03:04:45 -05:00
Alex Corn
4b8799ddd4 3.48.5 2024-11-02 02:54:44 -04:00
Alex Corn
f02f85a2aa Don't error out if a CS inventory has no visible items 2024-11-02 02:54:16 -04:00
Alex Corn
4948cbeac0 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	package.json
2024-11-02 02:49:14 -04:00
Alex Corn
1c1ff82543 3.48.4 2024-10-05 18:50:47 -04:00
Alex Corn
63015259af Updated readme 2024-10-05 18:47:05 -04:00
Alex Corn
ad67805ad9 Prevent committing twofactor files to git 2024-10-05 18:45:26 -04:00
DoctorMcKay
3347d87f29 Merge pull request #347 from benschool/master
Fixes cache_expiration restore for CS2 Items
2024-10-05 18:41:48 -04:00
Benjamin Tyler
c97351543a Fixes cache_expiration restore for CS2 Items
As of Oct 2024, CS2 Item owner_description has changed from "Tradable After" to "Tradable/Marketable After".
2024-10-04 16:04:47 +01:00
DoctorMcKay
1067d4572e Merge pull request #341 from makss/reduce_redirects
Reduce Steam redirects
2024-04-29 10:21:41 -04:00
makss
49a7165052 Reduce Steam redirects 2024-04-29 16:40:01 +03:00
Alex Corn
0fb2798424 3.48.3 2024-04-18 06:04:25 -04:00
DoctorMcKay
e16136866c Merge pull request #340 from nolddor/patch-1
fix: getUserInventoryContents() make unnecesary calls
2024-04-18 06:04:06 -04:00
Jack Nolddor
cae85433f9 fix: getUserInventoryContents() make unnecesary calls
Steam inventory limits are back to normal there is no reason to just gather 2k items instead 5k max allowed by API
2024-04-18 08:14:14 +02:00
Alex Corn
ba6e29c935 3.48.2 2024-01-18 06:12:30 -05:00
Alex Corn
de95e25867 Fixed steamID not being properly set when cookies have a domain attribute 2024-01-18 06:12:18 -05:00
11 changed files with 92 additions and 20 deletions

View File

@@ -4,9 +4,8 @@
[![license](https://img.shields.io/npm/l/steamcommunity.svg)](https://github.com/DoctorMcKay/node-steamcommunity/blob/master/LICENSE)
[![paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=N36YVAT42CZ4G&item_name=node%2dsteamcommunity&currency_code=USD)
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 and CAPTCHAs.
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.
**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/).

View File

@@ -1,6 +1,6 @@
module.exports = CEconItem;
function CEconItem(item, description, contextID) {
function CEconItem(item, description, contextID, assetProperties) {
var thing;
for (thing in item) {
if (item.hasOwnProperty(thing)) {
@@ -28,7 +28,7 @@ function CEconItem(item, description, contextID) {
}
for (thing in description) {
if (description.hasOwnProperty(thing)) {
if (description.hasOwnProperty(thing) && !this.hasOwnProperty(thing)) {
this[thing] = description[thing];
}
}
@@ -68,10 +68,26 @@ function CEconItem(item, description, contextID) {
// 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);
let description = this.owner_descriptions.find((d) => d.value && d.value.indexOf("Tradable/Marketable After ") == 0);
if (description) {
let date = new Date(description.value.substring(15).replace(/[,()]/g, ''));
if (date) {
let date;
const match = description.value.match(/(\d{1,2}) (\w+) @ (\d{1,2}:\d{2})(am|pm)/i);
if (match) {
// New day-first format, e.g. "Tradable/Marketable After 17 Jul @ 3:00am"
const [, day, month, time, meridian] = match;
const now = new Date();
let year = now.getFullYear();
date = new Date(`${day} ${month} ${year} ${time} ${meridian}`);
if (date.getTime() < now.getTime()) {
date = new Date(`${day} ${month} ${year + 1} ${time} ${meridian}`);
}
} else {
// Legacy month-first format, e.g. "Tradable/Marketable After Jul 11, 2025 (2:00:00)"
date = new Date(description.value.substring("Tradable/Marketable After ".length).replace(/[,()]/g, ""));
}
if (date && !isNaN(date.getTime())) {
this.cache_expiration = date.toISOString();
}
}
@@ -86,6 +102,11 @@ function CEconItem(item, description, contextID) {
this.actions = [];
}
// Assign asset_properties if provided
if (assetProperties) {
this.asset_properties = assetProperties;
}
// 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) {

View File

@@ -13,7 +13,7 @@ SteamCommunity.prototype.getSteamUser = function(id, callback) {
}
var self = this;
this.httpRequest("http://steamcommunity.com/" + (typeof id === 'string' ? "id/" + id : "profiles/" + id.toString()) + "/?xml=1", function(err, response, body) {
this.httpRequest("https://steamcommunity.com/" + (typeof id === 'string' ? "id/" + id : "profiles/" + id.toString()) + "/?xml=1", function(err, response, body) {
if (err) {
callback(err);
return;

View File

@@ -261,7 +261,10 @@ function request(community, url, key, time, tag, params, json, callback) {
var req = {
method: url == 'multiajaxop' ? 'POST' : 'GET',
uri: 'https://steamcommunity.com/mobileconf/' + url,
json: !!json
json: !!json,
headers: {
'user-agent': 'okhttp/4.9.2'
}
};
if (req.method == 'GET') {
@@ -392,6 +395,22 @@ SteamCommunity.prototype.checkConfirmations = function() {
}
};
SteamCommunity.prototype.acknowledgeTradeProtection = function(callback) {
this.httpRequestPost({
uri: 'https://steamcommunity.com//trade/new/acknowledge',
form: {
sessionid: this.getSessionID(),
message: 1
}
}, (err, res, body) => {
if (err) {
return callback && callback(err);
}
callback && callback(null);
}, 'steamcommunity');
}
SteamCommunity.prototype._confirmationCheckerGetKey = function(tag, callback) {
if(this._identitySecret) {
if(tag == 'details') {

View File

@@ -11,17 +11,17 @@ SteamCommunity.prototype.getGroupMembers = function(gid, callback, members, link
if (!link) {
if (typeof gid !== 'string') {
// It's a SteamID object
link = "http://steamcommunity.com/gid/" + gid.toString() + "/memberslistxml/?xml=1";
link = "https://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";
link = "https://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";
link = "https://steamcommunity.com/groups/" + gid + "/memberslistxml/?xml=1";
}
}
}

View File

@@ -1,3 +1,5 @@
var URL = require('url');
var SteamCommunity = require('../index.js');
SteamCommunity.prototype.httpRequest = function(uri, options, callback, source) {
@@ -19,6 +21,16 @@ SteamCommunity.prototype.httpRequest = function(uri, options, callback, source)
delete this._httpRequestConvenienceMethod;
}
// Add origin header if necessary
// https://github.com/DoctorMcKay/node-steamcommunity/issues/351
if ((options.method || 'GET').toUpperCase() != 'GET') {
options.headers = options.headers || {};
if (!options.headers.origin) {
var parsedUrl = URL.parse(options.url);
options.headers.origin = parsedUrl.protocol + '//' + parsedUrl.host;
}
}
var requestID = ++this._httpRequestID;
source = source || "";

View File

@@ -54,7 +54,7 @@ SteamCommunity.prototype._modernLogin = function(logOnDetails) {
let webCookies = await session.getWebCookies();
let sessionIdCookie = webCookies.find(c => c.startsWith('sessionid='));
resolve({
sessionID: sessionIdCookie.split('=')[1],
sessionID: sessionIdCookie.split('=')[1].split(';')[0].trim(),
cookies: webCookies,
steamguard: session.steamGuardMachineToken,
mobileAccessToken: logOnDetails.disableMobile ? null : session.accessToken
@@ -68,6 +68,10 @@ SteamCommunity.prototype._modernLogin = function(logOnDetails) {
reject(err);
});
session.on('timeout', () => {
reject(new Error('Login timed out'));
});
try {
let startResult = await session.startWithCredentials({
accountName: logOnDetails.accountName,

View File

@@ -590,7 +590,7 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont
},
"qs": {
"l": language, // Default language
"count": 2000, // Max items per 'page'
"count": 1000, // Max items per 'page'
"start_assetid": start
},
"json": true
@@ -629,6 +629,13 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont
return;
}
if (appID == 730 && body && body.success && !body.assets) {
// CS inventory has no visible items. We need a special case for this because Valve is incapable of
// doing anything not dumb.
callback(null, [], [], body.total_inventory_count);
return;
}
if (!body || !body.success || !body.assets || !body.descriptions) {
if (body) {
// Dunno if the error/Error property even exists on this new endpoint
@@ -640,12 +647,21 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont
return;
}
// Build asset_properties lookup by assetid
var assetPropertiesLookup = {};
if (body.asset_properties) {
for (var j = 0; j < body.asset_properties.length; j++) {
assetPropertiesLookup[body.asset_properties[j].assetid] = body.asset_properties[j].asset_properties;
}
}
for (var i = 0; i < body.assets.length; i++) {
var description = getDescription(body.descriptions, body.assets[i].classid, body.assets[i].instanceid);
if (!tradableOnly || (description && description.tradable)) {
body.assets[i].pos = pos++;
(body.assets[i].currencyid ? currency : inventory).push(new CEconItem(body.assets[i], description, contextID));
var assetProperties = assetPropertiesLookup[body.assets[i].assetid] || null;
(body.assets[i].currencyid ? currency : inventory).push(new CEconItem(body.assets[i], description, contextID, assetProperties));
}
}

1
examples/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
twofactor_*.json

View File

@@ -165,9 +165,9 @@ SteamCommunity.prototype._setCookie = function(cookie, secure) {
SteamCommunity.prototype.setCookies = function(cookies) {
cookies.forEach((cookie) => {
var cookieName = cookie.match(/(.+)=/)[1];
var cookieName = cookie.trim().split('=')[0];
if (cookieName == 'steamLogin' || cookieName == 'steamLoginSecure') {
this.steamID = new SteamID(cookie.match(/=(\d+)/)[1]);
this.steamID = new SteamID(cookie.match(/steamLogin(Secure)?=(\d+)/)[2]);
}
this._setCookie(Request.cookie(cookie), !!(cookieName.match(/^steamMachineAuth/) || cookieName.match(/Secure$/)));

View File

@@ -1,6 +1,6 @@
{
"name": "steamcommunity",
"version": "3.48.1",
"version": "3.50.3",
"description": "Provides an interface for logging into and interacting with the Steam Community website",
"files": [
"/classes",
@@ -33,7 +33,7 @@
"cheerio": "0.22.0",
"image-size": "^0.8.2",
"request": "^2.88.0",
"steam-session": "^1.7.2",
"steam-session": "^1.9.1",
"steam-totp": "^1.5.0",
"steamid": "^1.1.3",
"xml2js": "^0.6.2"