From 96d87ee8e65a5caf393b4a55c6cfd64eae180158 Mon Sep 17 00:00:00 2001 From: Alexander Corn Date: Sun, 6 Aug 2017 19:08:45 -0400 Subject: [PATCH] Added methods to deal with Steam group join requests --- components/chat.js | 29 +---- components/groups.js | 108 ++++++++++++++- index.js | 8 +- resources/EChatState.js | 14 ++ resources/EConfirmationType.js | 12 ++ resources/EPersonaState.js | 23 ++++ resources/EPersonaStateFlag.js | 27 ++++ resources/EResult.js | 232 +++++++++++++++++++++++++++++++++ 8 files changed, 420 insertions(+), 33 deletions(-) create mode 100644 resources/EChatState.js create mode 100644 resources/EConfirmationType.js create mode 100644 resources/EPersonaState.js create mode 100644 resources/EPersonaStateFlag.js create mode 100644 resources/EResult.js diff --git a/components/chat.js b/components/chat.js index 252fa12..b3fe1ae 100644 --- a/components/chat.js +++ b/components/chat.js @@ -1,32 +1,9 @@ var SteamCommunity = require('../index.js'); var SteamID = require('steamid'); -SteamCommunity.ChatState = { - "Offline": 0, - "LoggingOn": 1, - "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.ChatState = require('../resources/EChatState.js'); +SteamCommunity.PersonaState = require('../resources/EPersonaState.js'); +SteamCommunity.PersonaStateFlag = require('../resources/EPersonaStateFlag.js'); SteamCommunity.prototype.chatLogon = function(interval, uiMode) { if(this.chatState == SteamCommunity.ChatState.LoggingOn || this.chatState == SteamCommunity.ChatState.LoggedOn) { diff --git a/components/groups.js b/components/groups.js index 64da890..2a545c2 100644 --- a/components/groups.js +++ b/components/groups.js @@ -2,6 +2,7 @@ var SteamCommunity = require('../index.js'); var SteamID = require('steamid'); var xml2js = require('xml2js'); var Cheerio = require('cheerio'); +var EResult = SteamCommunity.EResult; SteamCommunity.prototype.getGroupMembers = function(gid, callback, members, link, addresses, addressIdx) { members = members || []; @@ -152,7 +153,7 @@ SteamCommunity.prototype.getAllGroupAnnouncements = function(gid, time, callback return callback(null, announcements); }); }, "steamcommunity"); -} +}; SteamCommunity.prototype.postGroupAnnouncement = function(gid, headline, content, callback) { if(typeof gid === 'string') { @@ -517,3 +518,108 @@ SteamCommunity.prototype.getGroupHistory = function(gid, page, callback) { callback(null, output); }, "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"); +}; diff --git a/index.js b/index.js index 28e72a8..a802983 100644 --- a/index.js +++ b/index.js @@ -12,12 +12,8 @@ require('util').inherits(SteamCommunity, require('events').EventEmitter); module.exports = SteamCommunity; SteamCommunity.SteamID = SteamID; -SteamCommunity.ConfirmationType = { - // 1 is unknown, possibly "Invalid" - "Trade": 2, - "MarketListing": 3 - // 4 is opt-out or other like account confirmation? -}; +SteamCommunity.ConfirmationType = require('./resources/EConfirmationType.js'); +SteamCommunity.EResult = require('./resources/EResult.js'); function SteamCommunity(options) { options = options || {}; diff --git a/resources/EChatState.js b/resources/EChatState.js new file mode 100644 index 0000000..287ee8d --- /dev/null +++ b/resources/EChatState.js @@ -0,0 +1,14 @@ +/** + * @enum EChatState + */ +module.exports = { + "Offline": 0, + "LoggingOn": 1, + "LogOnFailed": 2, + "LoggedOn": 3, + + "0": "Offline", + "1": "LoggingOn", + "2": "LogOnFailed", + "3": "LoggedOn" +}; diff --git a/resources/EConfirmationType.js b/resources/EConfirmationType.js new file mode 100644 index 0000000..3c92474 --- /dev/null +++ b/resources/EConfirmationType.js @@ -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" +}; diff --git a/resources/EPersonaState.js b/resources/EPersonaState.js new file mode 100644 index 0000000..2bb811a --- /dev/null +++ b/resources/EPersonaState.js @@ -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", +}; diff --git a/resources/EPersonaStateFlag.js b/resources/EPersonaStateFlag.js new file mode 100644 index 0000000..dfb7b81 --- /dev/null +++ b/resources/EPersonaStateFlag.js @@ -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", +}; diff --git a/resources/EResult.js b/resources/EResult.js new file mode 100644 index 0000000..0ebdc2c --- /dev/null +++ b/resources/EResult.js @@ -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", +};