Merge branch 'master' into groupcomments

This commit is contained in:
Jason Vaillancourt 2017-08-20 20:13:41 -04:00 committed by GitHub
commit dc2e5d09a7
14 changed files with 518 additions and 58 deletions

1
.npmrc Normal file
View File

@ -0,0 +1 @@
package-lock=false

View File

@ -9,6 +9,8 @@ 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

@ -114,10 +114,37 @@ CSteamGroup.prototype.getHistory = function(page, callback) {
this._community.getGroupHistory(this.steamID, page, callback); this._community.getGroupHistory(this.steamID, page, callback);
}; };
CSteamGroup.prototype.getAllComments = function(from, count, callback) { CSteamGroup.prototype.getAllComments = function(from, count, callback) {
this._community.getAllGroupComments(this.steamID, from, count, callback); this._community.getAllGroupComments(this.steamID, from, count, callback);
}; };
CSteamGroup.prototype.deleteComment = function(cid, callback) { CSteamGroup.prototype.deleteComment = function(cid, callback) {
this._community.deleteGroupComment(this.steamID, cid, callback); this._community.deleteGroupComment(this.steamID, cid, callback);
/**
* Get requests to join this restricted group.
* @param {function} callback - First argument is null/Error, second is array of SteamID objects
*/
CSteamGroup.prototype.getJoinRequests = function(callback) {
this._community.getGroupJoinRequests(this.steamID, callback);
};
/**
* Respond to one or more join requests to this restricted group.
* @param {SteamID|string|SteamID[]|string[]} steamIDs - The SteamIDs of the users you want to approve or deny membership for (or a single value)
* @param {boolean} approve - True to put them in the group, false to deny their membership
* @param {function} callback - Takes only an Error object/null as the first argument
*/
CSteamGroup.prototype.respondToJoinRequests = function(steamIDs, approve, callback) {
this._community.respondToGroupJoinRequests(this.steamID, steamIDs, approve, callback);
};
/**
* Respond to *ALL* pending group-join requests for this group.
* @param {boolean} approve - True to allow everyone who requested into the group, false to not
* @param {function} callback - Takes only an Error object/null as the first argument
*/
CSteamGroup.prototype.respondToAllJoinRequests = function(approve, callback) {
this._community.respondToAllGroupJoinRequests(this.steamID, approve, callback);
}; };

View File

@ -1,32 +1,9 @@
var SteamCommunity = require('../index.js'); var SteamCommunity = require('../index.js');
var SteamID = require('steamid'); var SteamID = require('steamid');
SteamCommunity.ChatState = { SteamCommunity.ChatState = require('../resources/EChatState.js');
"Offline": 0, SteamCommunity.PersonaState = require('../resources/EPersonaState.js');
"LoggingOn": 1, SteamCommunity.PersonaStateFlag = require('../resources/EPersonaStateFlag.js');
"LogOnFailed": 2,
"LoggedOn": 3
};
SteamCommunity.PersonaState = {
"Offline": 0,
"Online": 1,
"Busy": 2,
"Away": 3,
"Snooze": 4,
"LookingToTrade": 5,
"LookingToPlay": 6,
"Max": 7
};
SteamCommunity.PersonaStateFlag = {
"HasRichPresence": 1,
"InJoinableGame": 2,
"OnlineUsingWeb": 256,
"OnlineUsingMobile": 512,
"OnlineUsingBigPicture": 1024
};
SteamCommunity.prototype.chatLogon = function(interval, uiMode) { SteamCommunity.prototype.chatLogon = function(interval, uiMode) {
if(this.chatState == SteamCommunity.ChatState.LoggingOn || this.chatState == SteamCommunity.ChatState.LoggedOn) { if(this.chatState == SteamCommunity.ChatState.LoggingOn || this.chatState == SteamCommunity.ChatState.LoggedOn) {
@ -181,7 +158,7 @@ SteamCommunity.prototype._chatPoll = function() {
}, },
"json": true "json": true
}, function(err, response, body) { }, function(err, response, body) {
if(self.chatState == SteamCommunity.ChatState.Offline) { if (self.chatState == SteamCommunity.ChatState.Offline) {
return; return;
} }
@ -242,12 +219,20 @@ SteamCommunity.prototype._relogWebChat = function() {
}; };
SteamCommunity.prototype._chatUpdatePersona = function(steamID) { SteamCommunity.prototype._chatUpdatePersona = function(steamID) {
if (!this.chatFriends || this.chatState == SteamCommunity.ChatState.Offline) {
return; // we no longer care
}
this.emit('debug', 'Updating persona data for ' + steamID); this.emit('debug', 'Updating persona data for ' + steamID);
var self = this; var self = this;
this.httpRequest({ this.httpRequest({
"uri": "https://steamcommunity.com/chat/friendstate/" + steamID.accountid, "uri": "https://steamcommunity.com/chat/friendstate/" + steamID.accountid,
"json": true "json": true
}, function(err, response, body) { }, function(err, response, body) {
if (!self.chatFriends || self.chatState == SteamCommunity.ChatState.Offline) {
return; // welp
}
if(err || response.statusCode != 200) { if(err || response.statusCode != 200) {
self.emit('debug', 'Chat update persona error: ' + (err ? err.message : "HTTP error " + response.statusCode)); self.emit('debug', 'Chat update persona error: ' + (err ? err.message : "HTTP error " + response.statusCode));
setTimeout(function() { setTimeout(function() {

View File

@ -243,6 +243,10 @@ SteamCommunity.prototype.acceptAllConfirmations = function(time, confKey, allowK
}; };
function request(community, url, key, time, tag, params, json, callback) { function request(community, url, key, time, tag, params, json, callback) {
if (!community.steamID) {
throw new Error("Must be logged in before trying to do anything with confirmations");
}
params = params || {}; params = params || {};
params.p = SteamTotp.getDeviceID(community.steamID); params.p = SteamTotp.getDeviceID(community.steamID);
params.a = community.steamID.getSteamID64(); params.a = community.steamID.getSteamID64();

View File

@ -3,6 +3,7 @@ var SteamID = require('steamid');
var xml2js = require('xml2js'); var xml2js = require('xml2js');
var Cheerio = require('cheerio'); var Cheerio = require('cheerio');
var Helpers = require('./helpers.js'); var Helpers = require('./helpers.js');
var EResult = SteamCommunity.EResult;
SteamCommunity.prototype.getGroupMembers = function(gid, callback, members, link, addresses, addressIdx) { SteamCommunity.prototype.getGroupMembers = function(gid, callback, members, link, addresses, addressIdx) {
members = members || []; members = members || [];
@ -153,7 +154,7 @@ SteamCommunity.prototype.getAllGroupAnnouncements = function(gid, time, callback
return callback(null, announcements); return callback(null, announcements);
}); });
}, "steamcommunity"); }, "steamcommunity");
} };
SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content, callback) { SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content, callback) {
if(typeof gid === 'string') { if(typeof gid === 'string') {
@ -581,4 +582,109 @@ SteamCommunity.prototype.deleteGroupComment = function(gid, cid, callback) {
callback(err || null); callback(err || null);
}, "steamcommunity"); }, "steamcommunity");
}; };
/**
* Get requests to join a restricted group.
* @param {SteamID|string} gid - The SteamID of the group you want to manage
* @param {function} callback - First argument is null/Error, second is array of SteamID objects
*/
SteamCommunity.prototype.getGroupJoinRequests = function(gid, callback) {
if (typeof gid === 'string') {
gid = new SteamID(gid);
}
this.httpRequestGet("https://steamcommunity.com/gid/" + gid.getSteamID64() + "/joinRequestsManage", (err, res, body) => {
if (!body) {
callback(new Error("Malformed response"));
return;
}
var matches = body.match(/JoinRequests_ApproveDenyUser\(\W*['"](\d+)['"],\W0\W\)/g);
if (!matches) {
// no pending requests
callback(null, []);
return;
}
var requests = [];
for (var i = 0; i < matches.length; i++) {
requests.push(new SteamID("[U:1:" + matches[i].match(/JoinRequests_ApproveDenyUser\(\W*['"](\d+)['"],\W0\W\)/)[1] + "]"));
}
callback(null, requests);
}, "steamcommunity");
};
/**
* Respond to one or more join requests to a restricted group.
* @param {SteamID|string} gid - The SteamID of the group you want to manage
* @param {SteamID|string|SteamID[]|string[]} steamIDs - The SteamIDs of the users you want to approve or deny membership for (or a single value)
* @param {boolean} approve - True to put them in the group, false to deny their membership
* @param {function} callback - Takes only an Error object/null as the first argument
*/
SteamCommunity.prototype.respondToGroupJoinRequests = function(gid, steamIDs, approve, callback) {
if (typeof gid === 'string') {
gid = new SteamID(gid);
}
var rgAccounts = (!Array.isArray(steamIDs) ? [steamIDs] : steamIDs).map(sid => sid.toString());
this.httpRequestPost({
"uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/joinRequestsManage",
"form": {
"rgAccounts": rgAccounts,
"bapprove": approve ? "1" : "0",
"json": "1",
"sessionID": this.getSessionID()
},
"json": true
}, (err, res, body) => {
if (!callback) {
return;
}
if (body != EResult.OK) {
var err = new Error(EResult[body] || ("Error " + body));
err.eresult = body;
callback(err);
} else {
callback(null);
}
}, "steamcommunity");
};
/**
* Respond to *ALL* pending group-join requests for a particular group.
* @param {SteamID|string} gid - The SteamID of the group you want to manage
* @param {boolean} approve - True to allow everyone who requested into the group, false to not
* @param {function} callback - Takes only an Error object/null as the first argument
*/
SteamCommunity.prototype.respondToAllGroupJoinRequests = function(gid, approve, callback) {
if (typeof gid === 'string') {
gid = new SteamID(gid);
}
this.httpRequestPost({
"uri": "https://steamcommunity.com/gid/" + gid.getSteamID64() + "/joinRequestsManage",
"form": {
"bapprove": approve ? "1" : "0",
"json": "1",
"action": "bulkrespond",
"sessionID": this.getSessionID()
},
"json": true
}, (err, res, body) => {
if (!callback) {
return;
}
if (body != EResult.OK) {
var err = new Error(EResult[body] || ("Error " + body));
err.eresult = body;
callback(err);
} else {
callback(null);
}
}, "steamcommunity");
};

View File

@ -218,16 +218,16 @@ SteamCommunity.prototype.getUserAliases = function(userID, callback) {
}; };
SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) { SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) {
if(typeof userID === 'string') { if (typeof userID === 'string') {
userID = new SteamID(userID); userID = new SteamID(userID);
} }
if(typeof userID === 'function') { if (typeof userID === 'function') {
callback = userID; callback = userID;
userID = this.steamID; userID = this.steamID;
} }
if(!userID) { if (!userID) {
callback(new Error("No SteamID specified and not logged in")); callback(new Error("No SteamID specified and not logged in"));
return; return;
} }
@ -240,8 +240,19 @@ SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) {
} }
var match = body.match(/var g_rgAppContextData = ([^\n]+);\r?\n/); var match = body.match(/var g_rgAppContextData = ([^\n]+);\r?\n/);
if(!match) { if (!match) {
callback(new Error("Malformed response")); var errorMessage = "Malformed response";
if(body.match(/0 items in their inventory\./)){
callback(null, {});
return;
}else if(body.match(/inventory is currently private\./)){
errorMessage = "Private inventory";
}else if(body.match(/profile\_private\_info/)){
errorMessage = "Private profile";
}
callback(new Error(errorMessage));
return; return;
} }
@ -340,15 +351,21 @@ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, t
* @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 {function} callback * @param {function} callback
*/ */
SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, contextID, tradableOnly, callback) { SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, contextID, tradableOnly, language, callback) {
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([], []);
@ -359,7 +376,7 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont
"Referer": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory" "Referer": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory"
}, },
"qs": { "qs": {
"l": "english", // Default language "l": language, // Default language
"count": 5000, // Max items per 'page' "count": 5000, // Max items per 'page'
"start_assetid": start "start_assetid": start
}, },
@ -368,7 +385,7 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont
if (err) { if (err) {
if (err.message == "HTTP error 403" && body === null) { if (err.message == "HTTP error 403" && body === null) {
// 403 with a body of "null" means the inventory/profile is private. // 403 with a body of "null" means the inventory/profile is private.
if(userID.getSteamID64() == self.steamID.getSteamID64()) { if (self.steamID && userID.getSteamID64() == self.steamID.getSteamID64()) {
// We can never get private profile error for our own inventory! // We can never get private profile error for our own inventory!
self._notifySessionExpired(err); self._notifySessionExpired(err);
} }
@ -431,20 +448,16 @@ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, cont
var quickDescriptionLookup = {}; var quickDescriptionLookup = {};
function getDescription(descriptions, classID, instanceID) { function getDescription(descriptions, classID, instanceID) {
instanceID = instanceID || '0'; // instanceID can be undefined, in which case it's 0. var key = classID + '_' + (instanceID || '0'); // instanceID can be undefined, in which case it's 0.
var key = classID + '_' + instanceID;
if (quickDescriptionLookup[key]) { if (quickDescriptionLookup[key]) {
return quickDescriptionLookup[key]; return quickDescriptionLookup[key];
} }
for (var i = 0; i < descriptions.length; i++) { for (var i = 0; i < descriptions.length; i++) {
quickDescriptionLookup[key] = descriptions[i]; quickDescriptionLookup[descriptions[i].classid + '_' + (descriptions[i].instanceid || '0')] = descriptions[i];
if (descriptions[i].classid == classID && descriptions[i].instanceid == instanceID) {
return descriptions[i];
}
} }
return quickDescriptionLookup[key];
} }
}; };

View File

@ -1,3 +1,5 @@
require('@doctormckay/stats-reporter').setup(require('./package.json'));
var Request = require('request'); var Request = require('request');
var RSA = require('node-bignumber').Key; var RSA = require('node-bignumber').Key;
var hex2b64 = require('node-bignumber').hex2b64; var hex2b64 = require('node-bignumber').hex2b64;
@ -10,12 +12,8 @@ require('util').inherits(SteamCommunity, require('events').EventEmitter);
module.exports = SteamCommunity; module.exports = SteamCommunity;
SteamCommunity.SteamID = SteamID; SteamCommunity.SteamID = SteamID;
SteamCommunity.ConfirmationType = { SteamCommunity.ConfirmationType = require('./resources/EConfirmationType.js');
// 1 is unknown, possibly "Invalid" SteamCommunity.EResult = require('./resources/EResult.js');
"Trade": 2,
"MarketListing": 3
// 4 is opt-out or other like account confirmation?
};
function SteamCommunity(options) { function SteamCommunity(options) {
options = options || {}; options = options || {};
@ -281,13 +279,25 @@ SteamCommunity.prototype.parentalUnlock = function(pin, callback) {
return; return;
} }
if(!body || typeof body.success !== 'boolean') { if (!body || typeof body.success !== 'boolean') {
callback("Invalid response"); callback("Invalid response");
return; return;
} }
if(!body.success) { if (!body.success) {
callback("Incorrect PIN"); switch (body.eresult) {
case 15:
callback("Incorrect PIN");
break;
case 25:
callback("Too many invalid PIN attempts");
break;
default:
callback("Error " + body.eresult);
}
return; return;
} }

View File

@ -1,8 +1,11 @@
{ {
"name": "steamcommunity", "name": "steamcommunity",
"version": "3.30.6", "version": "3.32.2",
"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": ["steam", "steam community"], "keywords": [
"steam",
"steam community"
],
"homepage": "https://github.com/DoctorMcKay/node-steamcommunity", "homepage": "https://github.com/DoctorMcKay/node-steamcommunity",
"bugs": { "bugs": {
"url": "https://github.com/DoctorMcKay/node-steamcommunity/issues" "url": "https://github.com/DoctorMcKay/node-steamcommunity/issues"
@ -13,7 +16,8 @@
"url": "https://github.com/DoctorMcKay/node-steamcommunity.git" "url": "https://github.com/DoctorMcKay/node-steamcommunity.git"
}, },
"dependencies": { "dependencies": {
"request": "^2.61.0", "@doctormckay/stats-reporter": "^1.0.2",
"request": "^2.81.0",
"node-bignumber": "^1.2.1", "node-bignumber": "^1.2.1",
"steamid": "^1.0.0", "steamid": "^1.0.0",
"xml2js": "^0.4.11", "xml2js": "^0.4.11",

14
resources/EChatState.js Normal file
View File

@ -0,0 +1,14 @@
/**
* @enum EChatState
*/
module.exports = {
"Offline": 0,
"LoggingOn": 1,
"LogOnFailed": 2,
"LoggedOn": 3,
"0": "Offline",
"1": "LoggingOn",
"2": "LogOnFailed",
"3": "LoggedOn"
};

View File

@ -0,0 +1,12 @@
/**
* @enum EConfirmationType
*/
module.exports = {
// 1 is unknown, possibly "Invalid"
"Trade": 2,
"MarketListing": 3,
// 4 is opt-out or other like account confirmation?
"2": "Trade",
"3": "MarketListing"
};

View File

@ -0,0 +1,23 @@
/**
* @enum EPersonaState
*/
module.exports = {
"Offline": 0,
"Online": 1,
"Busy": 2,
"Away": 3,
"Snooze": 4,
"LookingToTrade": 5,
"LookingToPlay": 6,
"Max": 7,
// Value-to-name mapping for convenience
"0": "Offline",
"1": "Online",
"2": "Busy",
"3": "Away",
"4": "Snooze",
"5": "LookingToTrade",
"6": "LookingToPlay",
"7": "Max",
};

View File

@ -0,0 +1,27 @@
/**
* @enum EPersonaStateFlag
*/
module.exports = {
"HasRichPresence": 1,
"InJoinableGame": 2,
"Golden": 4, // removed "no longer has any effect"
"OnlineUsingWeb": 256, // removed "renamed to ClientTypeWeb"
"ClientTypeWeb": 256,
"OnlineUsingMobile": 512, // removed "renamed to ClientTypeMobile"
"ClientTypeMobile": 512,
"OnlineUsingBigPicture": 1024, // removed "renamed to ClientTypeTenfoot"
"ClientTypeTenfoot": 1024,
"OnlineUsingVR": 2048, // removed "renamed to ClientTypeVR"
"ClientTypeVR": 2048,
"LaunchTypeGamepad": 4096,
// Value-to-name mapping for convenience
"1": "HasRichPresence",
"2": "InJoinableGame",
"4": "Golden",
"256": "ClientTypeWeb",
"512": "ClientTypeMobile",
"1024": "ClientTypeTenfoot",
"2048": "ClientTypeVR",
"4096": "LaunchTypeGamepad",
};

232
resources/EResult.js Normal file
View File

@ -0,0 +1,232 @@
/**
* @enum EResult
*/
module.exports = {
"Invalid": 0,
"OK": 1,
"Fail": 2,
"NoConnection": 3,
"InvalidPassword": 5,
"LoggedInElsewhere": 6,
"InvalidProtocolVer": 7,
"InvalidParam": 8,
"FileNotFound": 9,
"Busy": 10,
"InvalidState": 11,
"InvalidName": 12,
"InvalidEmail": 13,
"DuplicateName": 14,
"AccessDenied": 15,
"Timeout": 16,
"Banned": 17,
"AccountNotFound": 18,
"InvalidSteamID": 19,
"ServiceUnavailable": 20,
"NotLoggedOn": 21,
"Pending": 22,
"EncryptionFailure": 23,
"InsufficientPrivilege": 24,
"LimitExceeded": 25,
"Revoked": 26,
"Expired": 27,
"AlreadyRedeemed": 28,
"DuplicateRequest": 29,
"AlreadyOwned": 30,
"IPNotFound": 31,
"PersistFailed": 32,
"LockingFailed": 33,
"LogonSessionReplaced": 34,
"ConnectFailed": 35,
"HandshakeFailed": 36,
"IOFailure": 37,
"RemoteDisconnect": 38,
"ShoppingCartNotFound": 39,
"Blocked": 40,
"Ignored": 41,
"NoMatch": 42,
"AccountDisabled": 43,
"ServiceReadOnly": 44,
"AccountNotFeatured": 45,
"AdministratorOK": 46,
"ContentVersion": 47,
"TryAnotherCM": 48,
"PasswordRequiredToKickSession": 49,
"AlreadyLoggedInElsewhere": 50,
"Suspended": 51,
"Cancelled": 52,
"DataCorruption": 53,
"DiskFull": 54,
"RemoteCallFailed": 55,
"PasswordNotSet": 56, // removed "renamed to PasswordUnset"
"PasswordUnset": 56,
"ExternalAccountUnlinked": 57,
"PSNTicketInvalid": 58,
"ExternalAccountAlreadyLinked": 59,
"RemoteFileConflict": 60,
"IllegalPassword": 61,
"SameAsPreviousValue": 62,
"AccountLogonDenied": 63,
"CannotUseOldPassword": 64,
"InvalidLoginAuthCode": 65,
"AccountLogonDeniedNoMailSent": 66, // removed "renamed to AccountLogonDeniedNoMail"
"AccountLogonDeniedNoMail": 66,
"HardwareNotCapableOfIPT": 67,
"IPTInitError": 68,
"ParentalControlRestricted": 69,
"FacebookQueryError": 70,
"ExpiredLoginAuthCode": 71,
"IPLoginRestrictionFailed": 72,
"AccountLocked": 73, // removed "renamed to AccountLockedDown"
"AccountLockedDown": 73,
"AccountLogonDeniedVerifiedEmailRequired": 74,
"NoMatchingURL": 75,
"BadResponse": 76,
"RequirePasswordReEntry": 77,
"ValueOutOfRange": 78,
"UnexpectedError": 79,
"Disabled": 80,
"InvalidCEGSubmission": 81,
"RestrictedDevice": 82,
"RegionLocked": 83,
"RateLimitExceeded": 84,
"AccountLogonDeniedNeedTwoFactorCode": 85, // removed "renamed to AccountLoginDeniedNeedTwoFactor"
"AccountLoginDeniedNeedTwoFactor": 85,
"ItemOrEntryHasBeenDeleted": 86, // removed "renamed to ItemDeleted"
"ItemDeleted": 86,
"AccountLoginDeniedThrottle": 87,
"TwoFactorCodeMismatch": 88,
"TwoFactorActivationCodeMismatch": 89,
"AccountAssociatedToMultiplePlayers": 90, // removed "renamed to AccountAssociatedToMultiplePartners"
"AccountAssociatedToMultiplePartners": 90,
"NotModified": 91,
"NoMobileDeviceAvailable": 92, // removed "renamed to NoMobileDevice"
"NoMobileDevice": 92,
"TimeIsOutOfSync": 93, // removed "renamed to TimeNotSynced"
"TimeNotSynced": 93,
"SMSCodeFailed": 94,
"TooManyAccountsAccessThisResource": 95, // removed "renamed to AccountLimitExceeded"
"AccountLimitExceeded": 95,
"AccountActivityLimitExceeded": 96,
"PhoneActivityLimitExceeded": 97,
"RefundToWallet": 98,
"EmailSendFailure": 99,
"NotSettled": 100,
"NeedCaptcha": 101,
"GSLTDenied": 102,
"GSOwnerDenied": 103,
"InvalidItemType": 104,
"IPBanned": 105,
"GSLTExpired": 106,
"InsufficientFunds": 107,
"TooManyPending": 108,
// Value-to-name mapping for convenience
"0": "Invalid",
"1": "OK",
"2": "Fail",
"3": "NoConnection",
"5": "InvalidPassword",
"6": "LoggedInElsewhere",
"7": "InvalidProtocolVer",
"8": "InvalidParam",
"9": "FileNotFound",
"10": "Busy",
"11": "InvalidState",
"12": "InvalidName",
"13": "InvalidEmail",
"14": "DuplicateName",
"15": "AccessDenied",
"16": "Timeout",
"17": "Banned",
"18": "AccountNotFound",
"19": "InvalidSteamID",
"20": "ServiceUnavailable",
"21": "NotLoggedOn",
"22": "Pending",
"23": "EncryptionFailure",
"24": "InsufficientPrivilege",
"25": "LimitExceeded",
"26": "Revoked",
"27": "Expired",
"28": "AlreadyRedeemed",
"29": "DuplicateRequest",
"30": "AlreadyOwned",
"31": "IPNotFound",
"32": "PersistFailed",
"33": "LockingFailed",
"34": "LogonSessionReplaced",
"35": "ConnectFailed",
"36": "HandshakeFailed",
"37": "IOFailure",
"38": "RemoteDisconnect",
"39": "ShoppingCartNotFound",
"40": "Blocked",
"41": "Ignored",
"42": "NoMatch",
"43": "AccountDisabled",
"44": "ServiceReadOnly",
"45": "AccountNotFeatured",
"46": "AdministratorOK",
"47": "ContentVersion",
"48": "TryAnotherCM",
"49": "PasswordRequiredToKickSession",
"50": "AlreadyLoggedInElsewhere",
"51": "Suspended",
"52": "Cancelled",
"53": "DataCorruption",
"54": "DiskFull",
"55": "RemoteCallFailed",
"56": "PasswordUnset",
"57": "ExternalAccountUnlinked",
"58": "PSNTicketInvalid",
"59": "ExternalAccountAlreadyLinked",
"60": "RemoteFileConflict",
"61": "IllegalPassword",
"62": "SameAsPreviousValue",
"63": "AccountLogonDenied",
"64": "CannotUseOldPassword",
"65": "InvalidLoginAuthCode",
"66": "AccountLogonDeniedNoMail",
"67": "HardwareNotCapableOfIPT",
"68": "IPTInitError",
"69": "ParentalControlRestricted",
"70": "FacebookQueryError",
"71": "ExpiredLoginAuthCode",
"72": "IPLoginRestrictionFailed",
"73": "AccountLockedDown",
"74": "AccountLogonDeniedVerifiedEmailRequired",
"75": "NoMatchingURL",
"76": "BadResponse",
"77": "RequirePasswordReEntry",
"78": "ValueOutOfRange",
"79": "UnexpectedError",
"80": "Disabled",
"81": "InvalidCEGSubmission",
"82": "RestrictedDevice",
"83": "RegionLocked",
"84": "RateLimitExceeded",
"85": "AccountLoginDeniedNeedTwoFactor",
"86": "ItemDeleted",
"87": "AccountLoginDeniedThrottle",
"88": "TwoFactorCodeMismatch",
"89": "TwoFactorActivationCodeMismatch",
"90": "AccountAssociatedToMultiplePartners",
"91": "NotModified",
"92": "NoMobileDevice",
"93": "TimeNotSynced",
"94": "SMSCodeFailed",
"95": "AccountLimitExceeded",
"96": "AccountActivityLimitExceeded",
"97": "PhoneActivityLimitExceeded",
"98": "RefundToWallet",
"99": "EmailSendFailure",
"100": "NotSettled",
"101": "NeedCaptcha",
"102": "GSLTDenied",
"103": "GSOwnerDenied",
"104": "InvalidItemType",
"105": "IPBanned",
"106": "GSLTExpired",
"107": "InsufficientFunds",
"108": "TooManyPending",
};