steamcommunity 3.47.1 → 3.48.0

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.
@@ -1,49 +1,152 @@
1
- var SteamCommunity = require('../index.js');
1
+ const SteamCommunity = require('../index.js');
2
2
 
3
3
  const Helpers = require('./helpers.js');
4
4
 
5
- SteamCommunity.prototype.getWebApiKey = function(domain, callback) {
6
- var self = this;
5
+ /**
6
+ * Retrieves your account's Steam Web API key, if you already have one. If you don't yet have one, this will fail.
7
+ * To create a Web API key, use `createWebApiKey()`.
8
+ *
9
+ * @param {null|function} unused - No longer used, kept for backward compatibility. You can omit this parameter and pass
10
+ * your callback directly as the first parameter if you want.
11
+ * @param {function} callback
12
+ */
13
+ SteamCommunity.prototype.getWebApiKey = function(unused, callback) {
14
+ if (typeof unused == 'function') {
15
+ callback = unused;
16
+ }
17
+
7
18
  this.httpRequest({
8
- "uri": "https://steamcommunity.com/dev/apikey?l=english",
9
- "followRedirect": false
10
- }, function(err, response, body) {
19
+ uri: 'https://steamcommunity.com/dev/apikey?l=english',
20
+ followRedirect: false
21
+ }, (err, response, body) => {
11
22
  if (err) {
12
23
  callback(err);
13
24
  return;
14
25
  }
15
26
 
16
- if(body.match(/<h2>Access Denied<\/h2>/)) {
17
- return callback(new Error("Access Denied"));
27
+ if (body.match(/You must have a validated email address to create a Steam Web API key./)) {
28
+ return callback(new Error('You must have a validated email address to create a Steam Web API key.'));
29
+ }
30
+
31
+ if (body.match(/Your account requires (<a [^>]+>)?Steam Guard Mobile Authenticator/)) {
32
+ return callback(new Error('Steam Guard Mobile Authenticator required to create a Steam Web API key'));
18
33
  }
19
34
 
20
- if(body.match(/You must have a validated email address to create a Steam Web API key./)) {
21
- return callback(new Error("You must have a validated email address to create a Steam Web API key."));
35
+ if (body.match(/<h2>Access Denied<\/h2>/)) {
36
+ return callback(new Error('Access Denied'));
22
37
  }
23
38
 
24
- var match = body.match(/<p>Key: ([0-9A-F]+)<\/p>/);
25
- if(match) {
39
+ let match = body.match(/<p>Key: ([0-9A-F]+)<\/p>/);
40
+ if (match) {
26
41
  // We already have an API key registered
27
42
  callback(null, match[1]);
28
43
  } else {
29
- // We need to register a new API key
30
- self.httpRequestPost('https://steamcommunity.com/dev/registerkey?l=english', {
31
- "form": {
32
- "domain": domain,
33
- "agreeToTerms": "agreed",
34
- "sessionid": self.getSessionID(),
35
- "Submit": "Register"
44
+ callback(new Error('No API key created for this account'));
45
+ }
46
+ }, "steamcommunity");
47
+ };
48
+
49
+ /**
50
+ * @typedef CreateApiKeyOptions
51
+ * @property {string} domain - The domain to associate with your API key
52
+ * @property {string} [requestID] - If finalizing an existing create request, include the request ID
53
+ * @property {string|Buffer} [identitySecret] - If you pass your identity_secret here, then steamcommunity will
54
+ * internally handle accepting any confirmations.
55
+ */
56
+
57
+ /**
58
+ * @typedef CreateApiKeyResponse
59
+ * @property {boolean} confirmationRequired
60
+ * @property {string} [apiKey] - If creating your API key succeeded, this is the new key
61
+ * @property {CreateApiKeyOptions} [finalizeOptions] - If confirmation is required to create a key, then accept the
62
+ * confirmation, then call createWebApiKey again and pass this whole object for the `options` parameter.
63
+ */
64
+
65
+ /**
66
+ * @callback createWebApiKeyCallback
67
+ * @param {Error|null} err
68
+ * @param {CreateApiKeyResponse} [result]
69
+ */
70
+
71
+ /**
72
+ * Starts the process to create a Steam Web API key. When the callback is fired, you will need to approve a mobile
73
+ * confirmation in your app or using getConfirmations().
74
+ *
75
+ * @param {CreateApiKeyOptions} options
76
+ * @param {createWebApiKeyCallback} callback
77
+ */
78
+ SteamCommunity.prototype.createWebApiKey = function(options, callback) {
79
+ if (!options.domain) {
80
+ callback(new Error('Passing a domain is required to register an API key'));
81
+ return;
82
+ }
83
+
84
+ this.httpRequestPost({
85
+ uri: 'https://steamcommunity.com/dev/requestkey',
86
+ form: {
87
+ domain: options.domain,
88
+ request_id: options.requestID || '0',
89
+ sessionid: this.getSessionID(),
90
+ agreeToTerms: 'true'
91
+ },
92
+ json: true
93
+ }, (err, res, body) => {
94
+ if (err) {
95
+ callback(err);
96
+ return;
97
+ }
98
+
99
+ // body.requires_confirmation is 1/0, but the Steam website doesn't check this value and instead only checks the
100
+ // value of `success`. So let's just do that.
101
+
102
+ // This is a mess. I'm glad we have promises and await now.
103
+
104
+ switch (body.success) {
105
+ case SteamCommunity.EResult.OK:
106
+ if (body.api_key) {
107
+ callback(null, {confirmationRequired: false, apiKey: body.api_key});
108
+ return;
36
109
  }
37
- }, function(err, response, body) {
38
- if (err) {
39
- callback(err);
110
+
111
+ // It's not been observed that we get result OK without api_key included, but the Steam website doesn't
112
+ // use this value so let's be safe just in case it disappears in the future.
113
+ this.getWebApiKey((err, key) => {
114
+ if (err) {
115
+ callback(err);
116
+ return;
117
+ }
118
+
119
+ callback(null, {confirmationRequired: false, apiKey: key});
120
+ });
121
+ return;
122
+
123
+ case SteamCommunity.EResult.Pending:
124
+ let finalizeOptions = {
125
+ domain: options.domain,
126
+ requestID: body.request_id || options.requestID
127
+ }
128
+
129
+ if (options.identitySecret) {
130
+ this.acceptConfirmationForObject(options.identitySecret, finalizeOptions.requestID, (err) => {
131
+ if (err) {
132
+ callback(err);
133
+ } else {
134
+ this.createWebApiKey(finalizeOptions, callback);
135
+ }
136
+ });
40
137
  return;
41
138
  }
42
139
 
43
- self.getWebApiKey(domain, callback);
44
- }, "steamcommunity");
140
+ callback(null, {
141
+ confirmationRequired: true,
142
+ finalizeOptions: finalizeOptions
143
+ });
144
+ return;
145
+
146
+ default:
147
+ callback(Helpers.eresultError(body.success));
45
148
  }
46
- }, "steamcommunity");
149
+ });
47
150
  };
48
151
 
49
152
  /**
@@ -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
+ }