steamcommunity 3.47.0 → 3.47.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +22 -22
- package/components/login.js +110 -0
- package/examples/README.md +35 -35
- package/examples/accept_all_confirmations.js +173 -173
- package/examples/disable_twofactor.js +135 -135
- package/examples/enable_twofactor.js +182 -182
- package/index.js +13 -151
- package/package.json +3 -5
- package/resources/ESharedFileType.js +13 -13
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
The MIT License (MIT)
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2015 Alex Corn
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
|
13
|
-
all copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
-
THE SOFTWARE.
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015 Alex Corn
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
# Steam Community for Node.js
|
|
2
|
-
[](https://npmjs.com/package/steamcommunity)
|
|
3
|
-
[](https://npmjs.com/package/steamcommunity)
|
|
4
|
-
[](https://github.com/DoctorMcKay/node-steamcommunity/blob/master/LICENSE)
|
|
5
|
-
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=N36YVAT42CZ4G&item_name=node%2dsteamcommunity¤cy_code=USD)
|
|
6
|
-
|
|
7
|
-
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.
|
|
8
|
-
|
|
9
|
-
It supports Steam Guard and CAPTCHAs.
|
|
10
|
-
|
|
11
|
-
**Have a question about the module or coding in general? *Do not create a GitHub issue.* GitHub issues are for feature
|
|
12
|
-
requests and bug reports. Instead, post in the [dedicated forum](https://dev.doctormckay.com/forum/8-node-steamcommunity/).
|
|
13
|
-
Such issues may be ignored!**
|
|
14
|
-
|
|
15
|
-
# Documentation
|
|
16
|
-
|
|
17
|
-
Documentation is available on the [GitHub wiki](https://github.com/DoctorMcKay/node-steamcommunity/wiki).
|
|
18
|
-
|
|
19
|
-
# Support
|
|
20
|
-
|
|
21
|
-
Report bugs on the [issue tracker](https://github.com/DoctorMcKay/node-steamcommunity/issues) or ask questions
|
|
22
|
-
on the [dedicated forum](https://dev.doctormckay.com/forum/8-node-steamcommunity/).
|
|
1
|
+
# Steam Community for Node.js
|
|
2
|
+
[](https://npmjs.com/package/steamcommunity)
|
|
3
|
+
[](https://npmjs.com/package/steamcommunity)
|
|
4
|
+
[](https://github.com/DoctorMcKay/node-steamcommunity/blob/master/LICENSE)
|
|
5
|
+
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=N36YVAT42CZ4G&item_name=node%2dsteamcommunity¤cy_code=USD)
|
|
6
|
+
|
|
7
|
+
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.
|
|
8
|
+
|
|
9
|
+
It supports Steam Guard and CAPTCHAs.
|
|
10
|
+
|
|
11
|
+
**Have a question about the module or coding in general? *Do not create a GitHub issue.* GitHub issues are for feature
|
|
12
|
+
requests and bug reports. Instead, post in the [dedicated forum](https://dev.doctormckay.com/forum/8-node-steamcommunity/).
|
|
13
|
+
Such issues may be ignored!**
|
|
14
|
+
|
|
15
|
+
# Documentation
|
|
16
|
+
|
|
17
|
+
Documentation is available on the [GitHub wiki](https://github.com/DoctorMcKay/node-steamcommunity/wiki).
|
|
18
|
+
|
|
19
|
+
# Support
|
|
20
|
+
|
|
21
|
+
Report bugs on the [issue tracker](https://github.com/DoctorMcKay/node-steamcommunity/issues) or ask questions
|
|
22
|
+
on the [dedicated forum](https://dev.doctormckay.com/forum/8-node-steamcommunity/).
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const {chrome} = require('@doctormckay/user-agents');
|
|
2
|
+
|
|
3
|
+
const SteamCommunity = require('../index.js');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef LogOnDetails
|
|
7
|
+
* @property {string} accountName
|
|
8
|
+
* @property {string} password
|
|
9
|
+
* @property {string} [steamguard]
|
|
10
|
+
* @property {string} [authCode]
|
|
11
|
+
* @property {string} [twoFactorCode]
|
|
12
|
+
* @property {boolean} disableMobile
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef LogOnResponse
|
|
17
|
+
* @property {string} sessionID
|
|
18
|
+
* @property {string[]} cookies
|
|
19
|
+
* @property {string} steamguard
|
|
20
|
+
* @property {string} [mobileAccessToken]
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
*
|
|
25
|
+
* @param {LogOnDetails} logOnDetails
|
|
26
|
+
* @returns {Promise<LogOnResponse>}
|
|
27
|
+
* @private
|
|
28
|
+
*/
|
|
29
|
+
SteamCommunity.prototype._modernLogin = function(logOnDetails) {
|
|
30
|
+
return new Promise(async (resolve, reject) => {
|
|
31
|
+
if (!isNodeVersionNewEnough()) {
|
|
32
|
+
return reject(new Error(`Node.js version is too old! Need >=12.22.0 or later, got ${process.versions.node}.`));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (this._options.request) {
|
|
36
|
+
return reject(new Error('SteamCommunity.login() is incompatible with node-steamcommunity v3\'s usage of \'request\'. If you need to specify a custom \'request\' instance (e.g. when using a proxy), use https://www.npmjs.com/package/steam-session directly to log onto Steam.'));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Import this here so we don't cause problems on old Node versions if this code path isn't taken.
|
|
40
|
+
const {LoginSession, EAuthTokenPlatformType, EAuthSessionGuardType} = require('steam-session');
|
|
41
|
+
|
|
42
|
+
let session = new LoginSession(
|
|
43
|
+
logOnDetails.disableMobile
|
|
44
|
+
? EAuthTokenPlatformType.WebBrowser
|
|
45
|
+
: EAuthTokenPlatformType.MobileApp,
|
|
46
|
+
{
|
|
47
|
+
localAddress: this._options.localAddress,
|
|
48
|
+
userAgent: this._options.userAgent || chrome()
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
session.on('authenticated', async () => {
|
|
53
|
+
try {
|
|
54
|
+
let webCookies = await session.getWebCookies();
|
|
55
|
+
let sessionIdCookie = webCookies.find(c => c.startsWith('sessionid='));
|
|
56
|
+
resolve({
|
|
57
|
+
sessionID: sessionIdCookie.split('=')[1],
|
|
58
|
+
cookies: webCookies,
|
|
59
|
+
steamguard: session.steamGuardMachineToken,
|
|
60
|
+
mobileAccessToken: logOnDetails.disableMobile ? null : session.accessToken
|
|
61
|
+
});
|
|
62
|
+
} catch (ex) {
|
|
63
|
+
reject(ex);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
session.on('error', (err) => {
|
|
68
|
+
reject(err);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
let startResult = await session.startWithCredentials({
|
|
73
|
+
accountName: logOnDetails.accountName,
|
|
74
|
+
password: logOnDetails.password,
|
|
75
|
+
steamGuardMachineToken: logOnDetails.steamguard,
|
|
76
|
+
steamGuardCode: logOnDetails.authCode || logOnDetails.twoFactorCode
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (startResult.actionRequired) {
|
|
80
|
+
// Cannot continue with login, need something from the user
|
|
81
|
+
session.cancelLoginAttempt();
|
|
82
|
+
|
|
83
|
+
let emailMfaAction = startResult.validActions.find(action => action.type == EAuthSessionGuardType.EmailCode);
|
|
84
|
+
if (emailMfaAction) {
|
|
85
|
+
let err = new Error('SteamGuard');
|
|
86
|
+
err.emaildomain = emailMfaAction.detail;
|
|
87
|
+
return reject(err);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return reject(new Error('SteamGuardMobile'));
|
|
91
|
+
}
|
|
92
|
+
} catch (ex) {
|
|
93
|
+
return reject(ex);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
function isNodeVersionNewEnough() {
|
|
99
|
+
let [major, minor] = process.versions.node.split('.');
|
|
100
|
+
|
|
101
|
+
if (major < 12) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (major == 12 && minor < 22) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return true;
|
|
110
|
+
}
|
package/examples/README.md
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
# node-steamcommunity examples
|
|
2
|
-
|
|
3
|
-
The files in this directory are example scripts that you can use as a getting-started point for using node-steamcommunity.
|
|
4
|
-
|
|
5
|
-
## Enable or Disable Two-Factor Authentication
|
|
6
|
-
|
|
7
|
-
If you need to enable or disable 2FA on your bot account, you can use enable_twofactor.js and disable_twofactor.js to do so.
|
|
8
|
-
The way that you're intended to use these scripts is by cloning the repository locally, and then running them directly
|
|
9
|
-
from this examples directory.
|
|
10
|
-
|
|
11
|
-
For example:
|
|
12
|
-
|
|
13
|
-
```shell
|
|
14
|
-
git clone https://github.com/DoctorMcKay/node-steamcommunity node-steamcommunity
|
|
15
|
-
cd node-steamcommunity
|
|
16
|
-
npm install
|
|
17
|
-
cd examples
|
|
18
|
-
node enable_twofactor.js
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Accept All Confirmations
|
|
22
|
-
|
|
23
|
-
If you need to accept trade or market confirmations on your bot account for which you have your identity secret, you can
|
|
24
|
-
use accept_all_confirmations.js to do so. The way that you're intended to use this script is by cloning the repository
|
|
25
|
-
locally, and then running it directly from this examples directory.
|
|
26
|
-
|
|
27
|
-
For example:
|
|
28
|
-
|
|
29
|
-
```shell
|
|
30
|
-
git clone https://github.com/DoctorMcKay/node-steamcommunity node-steamcommunity
|
|
31
|
-
cd node-steamcommunity
|
|
32
|
-
npm install
|
|
33
|
-
cd examples
|
|
34
|
-
node accept_all_confirmations.js
|
|
35
|
-
```
|
|
1
|
+
# node-steamcommunity examples
|
|
2
|
+
|
|
3
|
+
The files in this directory are example scripts that you can use as a getting-started point for using node-steamcommunity.
|
|
4
|
+
|
|
5
|
+
## Enable or Disable Two-Factor Authentication
|
|
6
|
+
|
|
7
|
+
If you need to enable or disable 2FA on your bot account, you can use enable_twofactor.js and disable_twofactor.js to do so.
|
|
8
|
+
The way that you're intended to use these scripts is by cloning the repository locally, and then running them directly
|
|
9
|
+
from this examples directory.
|
|
10
|
+
|
|
11
|
+
For example:
|
|
12
|
+
|
|
13
|
+
```shell
|
|
14
|
+
git clone https://github.com/DoctorMcKay/node-steamcommunity node-steamcommunity
|
|
15
|
+
cd node-steamcommunity
|
|
16
|
+
npm install
|
|
17
|
+
cd examples
|
|
18
|
+
node enable_twofactor.js
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Accept All Confirmations
|
|
22
|
+
|
|
23
|
+
If you need to accept trade or market confirmations on your bot account for which you have your identity secret, you can
|
|
24
|
+
use accept_all_confirmations.js to do so. The way that you're intended to use this script is by cloning the repository
|
|
25
|
+
locally, and then running it directly from this examples directory.
|
|
26
|
+
|
|
27
|
+
For example:
|
|
28
|
+
|
|
29
|
+
```shell
|
|
30
|
+
git clone https://github.com/DoctorMcKay/node-steamcommunity node-steamcommunity
|
|
31
|
+
cd node-steamcommunity
|
|
32
|
+
npm install
|
|
33
|
+
cd examples
|
|
34
|
+
node accept_all_confirmations.js
|
|
35
|
+
```
|
|
@@ -1,173 +1,173 @@
|
|
|
1
|
-
// If you aren't running this script inside of the repository, replace the following line with:
|
|
2
|
-
// const SteamCommunity = require('steamcommunity');
|
|
3
|
-
const SteamCommunity = require('../index.js');
|
|
4
|
-
const SteamSession = require('steam-session');
|
|
5
|
-
const SteamTotp = require('steam-totp');
|
|
6
|
-
const ReadLine = require('readline');
|
|
7
|
-
|
|
8
|
-
const EConfirmationType = SteamCommunity.ConfirmationType;
|
|
9
|
-
|
|
10
|
-
let g_AbortPromptFunc = null;
|
|
11
|
-
|
|
12
|
-
let community = new SteamCommunity();
|
|
13
|
-
|
|
14
|
-
main();
|
|
15
|
-
async function main() {
|
|
16
|
-
let accountName = await promptAsync('Username: ');
|
|
17
|
-
let password = await promptAsync('Password (hidden): ', true);
|
|
18
|
-
|
|
19
|
-
// Create a LoginSession for us to use to attempt to log into steam
|
|
20
|
-
let session = new SteamSession.LoginSession(SteamSession.EAuthTokenPlatformType.MobileApp);
|
|
21
|
-
|
|
22
|
-
// Go ahead and attach our event handlers before we do anything else.
|
|
23
|
-
session.on('authenticated', async () => {
|
|
24
|
-
abortPrompt();
|
|
25
|
-
|
|
26
|
-
let cookies = await session.getWebCookies();
|
|
27
|
-
community.setCookies(cookies);
|
|
28
|
-
|
|
29
|
-
doConfirmations();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
session.on('timeout', () => {
|
|
33
|
-
abortPrompt();
|
|
34
|
-
console.log('This login attempt has timed out.');
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
session.on('error', (err) => {
|
|
38
|
-
abortPrompt();
|
|
39
|
-
|
|
40
|
-
// This should ordinarily not happen. This only happens in case there's some kind of unexpected error while
|
|
41
|
-
// polling, e.g. the network connection goes down or Steam chokes on something.
|
|
42
|
-
|
|
43
|
-
console.log(`ERROR: This login attempt has failed! ${err.message}`);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// Start our login attempt
|
|
47
|
-
let startResult = await session.startWithCredentials({accountName, password});
|
|
48
|
-
if (startResult.actionRequired) {
|
|
49
|
-
// Some Steam Guard action is required. We only care about email and device codes; in theory an
|
|
50
|
-
// EmailConfirmation and/or DeviceConfirmation action could be possible, but we're just going to ignore those.
|
|
51
|
-
// If the user does receive a confirmation and accepts it, LoginSession will detect and handle that automatically.
|
|
52
|
-
// The only consequence of ignoring it here is that we don't print a message to the user indicating that they
|
|
53
|
-
// could accept an email or device confirmation.
|
|
54
|
-
|
|
55
|
-
let codeActionTypes = [SteamSession.EAuthSessionGuardType.EmailCode, SteamSession.EAuthSessionGuardType.DeviceCode];
|
|
56
|
-
let codeAction = startResult.validActions.find(action => codeActionTypes.includes(action.type));
|
|
57
|
-
if (codeAction) {
|
|
58
|
-
if (codeAction.type == SteamSession.EAuthSessionGuardType.EmailCode) {
|
|
59
|
-
// We wouldn't expect this to happen since mobile confirmations are only possible with 2FA enabled, but just in case...
|
|
60
|
-
console.log(`A code has been sent to your email address at ${codeAction.detail}.`);
|
|
61
|
-
} else {
|
|
62
|
-
console.log('You need to provide a Steam Guard Mobile Authenticator code.');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
let code = await promptAsync('Code or Shared Secret: ');
|
|
66
|
-
if (code) {
|
|
67
|
-
// The code might've been a shared secret
|
|
68
|
-
if (code.length > 10) {
|
|
69
|
-
code = SteamTotp.getAuthCode(code);
|
|
70
|
-
}
|
|
71
|
-
await session.submitSteamGuardCode(code);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// If we fall through here without submitting a Steam Guard code, that means one of two things:
|
|
75
|
-
// 1. The user pressed enter without providing a code, in which case the script will simply exit
|
|
76
|
-
// 2. The user approved a device/email confirmation, in which case 'authenticated' was emitted and the prompt was canceled
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function doConfirmations() {
|
|
82
|
-
let identitySecret = await promptAsync('Identity Secret: ');
|
|
83
|
-
|
|
84
|
-
let confs = await new Promise((resolve, reject) => {
|
|
85
|
-
let time = SteamTotp.time();
|
|
86
|
-
let key = SteamTotp.getConfirmationKey(identitySecret, time, 'conf');
|
|
87
|
-
community.getConfirmations(time, key, (err, confs) => {
|
|
88
|
-
if (err) {
|
|
89
|
-
return reject(err);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
resolve(confs);
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
console.log(`Found ${confs.length} outstanding confirmations.`);
|
|
97
|
-
|
|
98
|
-
// We need to track the previous timestamp we used, as we cannot reuse timestamps.
|
|
99
|
-
let previousTime = 0;
|
|
100
|
-
|
|
101
|
-
for (let i = 0; i < confs.length; i++) {
|
|
102
|
-
let conf = confs[i];
|
|
103
|
-
|
|
104
|
-
process.stdout.write(`Accepting confirmation for ${EConfirmationType[conf.type]} - ${conf.title}... `);
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
await new Promise((resolve, reject) => {
|
|
108
|
-
let time = SteamTotp.time();
|
|
109
|
-
if (time == previousTime) {
|
|
110
|
-
time++;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
previousTime = time;
|
|
114
|
-
let key = SteamTotp.getConfirmationKey(identitySecret, time, 'allow');
|
|
115
|
-
conf.respond(time, key, true, (err) => {
|
|
116
|
-
err ? reject(err) : resolve();
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
console.log('success');
|
|
121
|
-
} catch (ex) {
|
|
122
|
-
console.log(`error: ${ex.message}`);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// sleep 500ms so we don't run too far away from the current timestamp
|
|
126
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
console.log('Finished processing confirmations');
|
|
130
|
-
process.exit(0);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Nothing interesting below here, just code for prompting for input from the console.
|
|
134
|
-
|
|
135
|
-
function promptAsync(question, sensitiveInput = false) {
|
|
136
|
-
return new Promise((resolve) => {
|
|
137
|
-
let rl = ReadLine.createInterface({
|
|
138
|
-
input: process.stdin,
|
|
139
|
-
output: sensitiveInput ? null : process.stdout,
|
|
140
|
-
terminal: true
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
g_AbortPromptFunc = () => {
|
|
144
|
-
rl.close();
|
|
145
|
-
resolve('');
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
if (sensitiveInput) {
|
|
149
|
-
// We have to write the question manually if we didn't give readline an output stream
|
|
150
|
-
process.stdout.write(question);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
rl.question(question, (result) => {
|
|
154
|
-
if (sensitiveInput) {
|
|
155
|
-
// We have to manually print a newline
|
|
156
|
-
process.stdout.write('\n');
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
g_AbortPromptFunc = null;
|
|
160
|
-
rl.close();
|
|
161
|
-
resolve(result);
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function abortPrompt() {
|
|
167
|
-
if (!g_AbortPromptFunc) {
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
g_AbortPromptFunc();
|
|
172
|
-
process.stdout.write('\n');
|
|
173
|
-
}
|
|
1
|
+
// If you aren't running this script inside of the repository, replace the following line with:
|
|
2
|
+
// const SteamCommunity = require('steamcommunity');
|
|
3
|
+
const SteamCommunity = require('../index.js');
|
|
4
|
+
const SteamSession = require('steam-session');
|
|
5
|
+
const SteamTotp = require('steam-totp');
|
|
6
|
+
const ReadLine = require('readline');
|
|
7
|
+
|
|
8
|
+
const EConfirmationType = SteamCommunity.ConfirmationType;
|
|
9
|
+
|
|
10
|
+
let g_AbortPromptFunc = null;
|
|
11
|
+
|
|
12
|
+
let community = new SteamCommunity();
|
|
13
|
+
|
|
14
|
+
main();
|
|
15
|
+
async function main() {
|
|
16
|
+
let accountName = await promptAsync('Username: ');
|
|
17
|
+
let password = await promptAsync('Password (hidden): ', true);
|
|
18
|
+
|
|
19
|
+
// Create a LoginSession for us to use to attempt to log into steam
|
|
20
|
+
let session = new SteamSession.LoginSession(SteamSession.EAuthTokenPlatformType.MobileApp);
|
|
21
|
+
|
|
22
|
+
// Go ahead and attach our event handlers before we do anything else.
|
|
23
|
+
session.on('authenticated', async () => {
|
|
24
|
+
abortPrompt();
|
|
25
|
+
|
|
26
|
+
let cookies = await session.getWebCookies();
|
|
27
|
+
community.setCookies(cookies);
|
|
28
|
+
|
|
29
|
+
doConfirmations();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
session.on('timeout', () => {
|
|
33
|
+
abortPrompt();
|
|
34
|
+
console.log('This login attempt has timed out.');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
session.on('error', (err) => {
|
|
38
|
+
abortPrompt();
|
|
39
|
+
|
|
40
|
+
// This should ordinarily not happen. This only happens in case there's some kind of unexpected error while
|
|
41
|
+
// polling, e.g. the network connection goes down or Steam chokes on something.
|
|
42
|
+
|
|
43
|
+
console.log(`ERROR: This login attempt has failed! ${err.message}`);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Start our login attempt
|
|
47
|
+
let startResult = await session.startWithCredentials({accountName, password});
|
|
48
|
+
if (startResult.actionRequired) {
|
|
49
|
+
// Some Steam Guard action is required. We only care about email and device codes; in theory an
|
|
50
|
+
// EmailConfirmation and/or DeviceConfirmation action could be possible, but we're just going to ignore those.
|
|
51
|
+
// If the user does receive a confirmation and accepts it, LoginSession will detect and handle that automatically.
|
|
52
|
+
// The only consequence of ignoring it here is that we don't print a message to the user indicating that they
|
|
53
|
+
// could accept an email or device confirmation.
|
|
54
|
+
|
|
55
|
+
let codeActionTypes = [SteamSession.EAuthSessionGuardType.EmailCode, SteamSession.EAuthSessionGuardType.DeviceCode];
|
|
56
|
+
let codeAction = startResult.validActions.find(action => codeActionTypes.includes(action.type));
|
|
57
|
+
if (codeAction) {
|
|
58
|
+
if (codeAction.type == SteamSession.EAuthSessionGuardType.EmailCode) {
|
|
59
|
+
// We wouldn't expect this to happen since mobile confirmations are only possible with 2FA enabled, but just in case...
|
|
60
|
+
console.log(`A code has been sent to your email address at ${codeAction.detail}.`);
|
|
61
|
+
} else {
|
|
62
|
+
console.log('You need to provide a Steam Guard Mobile Authenticator code.');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let code = await promptAsync('Code or Shared Secret: ');
|
|
66
|
+
if (code) {
|
|
67
|
+
// The code might've been a shared secret
|
|
68
|
+
if (code.length > 10) {
|
|
69
|
+
code = SteamTotp.getAuthCode(code);
|
|
70
|
+
}
|
|
71
|
+
await session.submitSteamGuardCode(code);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// If we fall through here without submitting a Steam Guard code, that means one of two things:
|
|
75
|
+
// 1. The user pressed enter without providing a code, in which case the script will simply exit
|
|
76
|
+
// 2. The user approved a device/email confirmation, in which case 'authenticated' was emitted and the prompt was canceled
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function doConfirmations() {
|
|
82
|
+
let identitySecret = await promptAsync('Identity Secret: ');
|
|
83
|
+
|
|
84
|
+
let confs = await new Promise((resolve, reject) => {
|
|
85
|
+
let time = SteamTotp.time();
|
|
86
|
+
let key = SteamTotp.getConfirmationKey(identitySecret, time, 'conf');
|
|
87
|
+
community.getConfirmations(time, key, (err, confs) => {
|
|
88
|
+
if (err) {
|
|
89
|
+
return reject(err);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
resolve(confs);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
console.log(`Found ${confs.length} outstanding confirmations.`);
|
|
97
|
+
|
|
98
|
+
// We need to track the previous timestamp we used, as we cannot reuse timestamps.
|
|
99
|
+
let previousTime = 0;
|
|
100
|
+
|
|
101
|
+
for (let i = 0; i < confs.length; i++) {
|
|
102
|
+
let conf = confs[i];
|
|
103
|
+
|
|
104
|
+
process.stdout.write(`Accepting confirmation for ${EConfirmationType[conf.type]} - ${conf.title}... `);
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
await new Promise((resolve, reject) => {
|
|
108
|
+
let time = SteamTotp.time();
|
|
109
|
+
if (time == previousTime) {
|
|
110
|
+
time++;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
previousTime = time;
|
|
114
|
+
let key = SteamTotp.getConfirmationKey(identitySecret, time, 'allow');
|
|
115
|
+
conf.respond(time, key, true, (err) => {
|
|
116
|
+
err ? reject(err) : resolve();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
console.log('success');
|
|
121
|
+
} catch (ex) {
|
|
122
|
+
console.log(`error: ${ex.message}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// sleep 500ms so we don't run too far away from the current timestamp
|
|
126
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log('Finished processing confirmations');
|
|
130
|
+
process.exit(0);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Nothing interesting below here, just code for prompting for input from the console.
|
|
134
|
+
|
|
135
|
+
function promptAsync(question, sensitiveInput = false) {
|
|
136
|
+
return new Promise((resolve) => {
|
|
137
|
+
let rl = ReadLine.createInterface({
|
|
138
|
+
input: process.stdin,
|
|
139
|
+
output: sensitiveInput ? null : process.stdout,
|
|
140
|
+
terminal: true
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
g_AbortPromptFunc = () => {
|
|
144
|
+
rl.close();
|
|
145
|
+
resolve('');
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
if (sensitiveInput) {
|
|
149
|
+
// We have to write the question manually if we didn't give readline an output stream
|
|
150
|
+
process.stdout.write(question);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
rl.question(question, (result) => {
|
|
154
|
+
if (sensitiveInput) {
|
|
155
|
+
// We have to manually print a newline
|
|
156
|
+
process.stdout.write('\n');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
g_AbortPromptFunc = null;
|
|
160
|
+
rl.close();
|
|
161
|
+
resolve(result);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function abortPrompt() {
|
|
167
|
+
if (!g_AbortPromptFunc) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
g_AbortPromptFunc();
|
|
172
|
+
process.stdout.write('\n');
|
|
173
|
+
}
|