steamcommunity 3.46.1 → 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.
Files changed (51) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +22 -22
  3. package/classes/CConfirmation.js +37 -37
  4. package/classes/CEconItem.js +120 -120
  5. package/classes/CMarketItem.js +189 -189
  6. package/classes/CMarketSearchResult.js +89 -89
  7. package/classes/CSteamGroup.js +155 -155
  8. package/classes/CSteamUser.js +225 -217
  9. package/components/chat.js +283 -283
  10. package/components/confirmations.js +428 -428
  11. package/components/groups.js +798 -732
  12. package/components/help.js +64 -64
  13. package/components/helpers.js +128 -108
  14. package/components/http.js +150 -150
  15. package/components/inventoryhistory.js +173 -173
  16. package/components/login.js +110 -0
  17. package/components/market.js +387 -387
  18. package/components/profile.js +475 -475
  19. package/components/twofactor.js +152 -152
  20. package/components/users.js +831 -767
  21. package/components/webapi.js +118 -118
  22. package/examples/README.md +35 -35
  23. package/examples/accept_all_confirmations.js +173 -173
  24. package/examples/disable_twofactor.js +135 -135
  25. package/examples/edit-group-announcement.js +118 -118
  26. package/examples/enable_twofactor.js +182 -182
  27. package/index.js +13 -151
  28. package/package.json +11 -6
  29. package/resources/EChatState.js +14 -14
  30. package/resources/EConfirmationType.js +12 -12
  31. package/resources/EFriendRelationship.js +23 -23
  32. package/resources/EPersonaState.js +23 -23
  33. package/resources/EPersonaStateFlag.js +32 -32
  34. package/resources/EResult.js +254 -254
  35. package/.editorconfig +0 -13
  36. package/.github/FUNDING.yml +0 -2
  37. package/.idea/.name +0 -1
  38. package/.idea/codeStyleSettings.xml +0 -13
  39. package/.idea/codeStyles/Project.xml +0 -15
  40. package/.idea/codeStyles/codeStyleConfig.xml +0 -6
  41. package/.idea/copyright/profiles_settings.xml +0 -3
  42. package/.idea/encodings.xml +0 -6
  43. package/.idea/inspectionProfiles/Project_Default.xml +0 -11
  44. package/.idea/jsLibraryMappings.xml +0 -6
  45. package/.idea/misc.xml +0 -6
  46. package/.idea/modules.xml +0 -9
  47. package/.idea/node-steamcommunity.iml +0 -8
  48. package/.idea/steamcommunity.iml +0 -10
  49. package/.idea/vcs.xml +0 -7
  50. package/.idea/watcherTasks.xml +0 -4
  51. package/CONTRIBUTING.md +0 -36
@@ -1,428 +1,428 @@
1
- var SteamCommunity = require('../index.js');
2
- var Cheerio = require('cheerio');
3
- var SteamTotp = require('steam-totp');
4
- var Async = require('async');
5
-
6
- var CConfirmation = require('../classes/CConfirmation.js');
7
- var EConfirmationType = require('../resources/EConfirmationType.js');
8
-
9
- /**
10
- * Get a list of your account's currently outstanding confirmations.
11
- * @param {int} time - The unix timestamp with which the following key was generated
12
- * @param {string} key - The confirmation key that was generated using the preceeding time and the tag 'conf' (this key can be reused)
13
- * @param {SteamCommunity~getConfirmations} callback - Called when the list of confirmations is received
14
- */
15
- SteamCommunity.prototype.getConfirmations = function(time, key, callback) {
16
- var self = this;
17
-
18
- // Ugly hack to maintain backward compatibility
19
- var tag = 'conf';
20
- if (typeof key == 'object') {
21
- tag = key.tag;
22
- key = key.key;
23
- }
24
-
25
- // The official Steam app uses the tag 'list', but 'conf' still works so let's use that for backward compatibility.
26
- request(this, 'getlist', key, time, tag, null, true, function(err, body) {
27
- if (err) {
28
- callback(err);
29
- return;
30
- }
31
-
32
- if (!body.success) {
33
- if (body.needauth) {
34
- var err = new Error('Not Logged In');
35
- self._notifySessionExpired(err);
36
- callback(err);
37
- return;
38
- }
39
-
40
- callback(new Error(body.message || body.detail || 'Failed to get confirmation list'));
41
- return;
42
- }
43
-
44
- var confs = (body.conf || []).map(conf => new CConfirmation(self, {
45
- id: conf.id,
46
- type: conf.type,
47
- creator: conf.creator_id,
48
- key: conf.nonce,
49
- title: `${conf.type_name || 'Confirm'} - ${conf.headline || ''}`,
50
- receiving: conf.type == EConfirmationType.Trade ? ((conf.summary || [])[1] || '') : '',
51
- sending: (conf.summary || [])[0] || '',
52
- time: (new Date(conf.creation_time * 1000)).toISOString(), // for backward compatibility
53
- timestamp: new Date(conf.creation_time * 1000),
54
- icon: conf.icon || ''
55
- }));
56
-
57
- callback(null, confs);
58
- });
59
- };
60
-
61
- /**
62
- * @callback SteamCommunity~getConfirmations
63
- * @param {Error|null} err - An Error object on failure, or null on success
64
- * @param {CConfirmation[]} confirmations - An array of CConfirmation objects
65
- */
66
-
67
- /**
68
- * Get the trade offer ID associated with a particular confirmation
69
- * @param {int} confID - The ID of the confirmation in question
70
- * @param {int} time - The unix timestamp with which the following key was generated
71
- * @param {string} key - The confirmation key that was generated using the preceeding time and the tag "detail" (this key can be reused)
72
- * @param {SteamCommunity~getConfirmationOfferID} callback
73
- */
74
- SteamCommunity.prototype.getConfirmationOfferID = function(confID, time, key, callback) {
75
- // The official Steam app uses the tag 'detail', but 'details' still works so let's use that for backward compatibility
76
- request(this, 'detailspage/' + confID, key, time, 'details', null, false, function(err, body) {
77
- if (err) {
78
- callback(err);
79
- return;
80
- }
81
-
82
- if (typeof body != 'string') {
83
- callback(new Error("Cannot load confirmation details"));
84
- return;
85
- }
86
-
87
- var $ = Cheerio.load(body);
88
- var offer = $('.tradeoffer');
89
- if(offer.length < 1) {
90
- callback(null, null);
91
- return;
92
- }
93
-
94
- callback(null, offer.attr('id').split('_')[1]);
95
- });
96
- };
97
-
98
- /**
99
- * @callback SteamCommunity~getConfirmationOfferID
100
- * @param {Error|null} err - An Error object on failure, or null on success
101
- * @param {string} offerID - The trade offer ID associated with the specified confirmation, or null if not for an offer
102
- */
103
-
104
- /**
105
- * Confirm or cancel a given confirmation.
106
- * @param {int|int[]} confID - The ID of the confirmation in question, or an array of confirmation IDs
107
- * @param {string|string[]} confKey - The confirmation key associated with the confirmation in question (or an array of them) (not a TOTP key, the `key` property of CConfirmation)
108
- * @param {int} time - The unix timestamp with which the following key was generated
109
- * @param {string} key - The confirmation key that was generated using the preceding time and the tag "allow" (if accepting) or "cancel" (if not accepting)
110
- * @param {boolean} accept - true if you want to accept the confirmation, false if you want to cancel it
111
- * @param {SteamCommunity~genericErrorCallback} callback - Called when the request is complete
112
- */
113
- SteamCommunity.prototype.respondToConfirmation = function(confID, confKey, time, key, accept, callback) {
114
- // Ugly hack to maintain backward compatibility
115
- var tag = accept ? 'allow' : 'cancel';
116
- if (typeof key == 'object') {
117
- tag = key.tag;
118
- key = key.key;
119
- }
120
-
121
- // The official app uses tags reject/accept, but cancel/allow still works so use these for backward compatibility
122
- request(this, (confID instanceof Array) ? 'multiajaxop' : 'ajaxop', key, time, tag, {
123
- op: accept ? 'allow' : 'cancel',
124
- cid: confID,
125
- ck: confKey
126
- }, true, function(err, body) {
127
- if (!callback) {
128
- return;
129
- }
130
-
131
- if (err) {
132
- callback(err);
133
- return;
134
- }
135
-
136
- if (body.success) {
137
- callback(null);
138
- return;
139
- }
140
-
141
- if (body.message) {
142
- callback(new Error(body.message));
143
- return;
144
- }
145
-
146
- callback(new Error('Could not act on confirmation'));
147
- });
148
- };
149
-
150
- /**
151
- * Accept a confirmation for a given object (trade offer or market listing) automatically.
152
- * @param {string} identitySecret
153
- * @param {number|string} objectID
154
- * @param {SteamCommunity~genericErrorCallback} callback
155
- */
156
- SteamCommunity.prototype.acceptConfirmationForObject = function(identitySecret, objectID, callback) {
157
- var self = this;
158
- this._usedConfTimes = this._usedConfTimes || [];
159
-
160
- if (typeof this._timeOffset !== 'undefined') {
161
- // time offset is already known and saved
162
- doConfirmation();
163
- } else {
164
- SteamTotp.getTimeOffset(function(err, offset) {
165
- if (err) {
166
- callback(err);
167
- return;
168
- }
169
-
170
- self._timeOffset = offset;
171
- doConfirmation();
172
-
173
- setTimeout(function() {
174
- // Delete the saved time offset after 12 hours because why not
175
- delete self._timeOffset;
176
- }, 1000 * 60 * 60 * 12).unref();
177
- });
178
- }
179
-
180
- function doConfirmation() {
181
- var offset = self._timeOffset;
182
- var time = SteamTotp.time(offset);
183
- var confKey = SteamTotp.getConfirmationKey(identitySecret, time, 'list');
184
- self.getConfirmations(time, {tag: 'list', key: confKey}, function(err, confs) {
185
- if (err) {
186
- callback(err);
187
- return;
188
- }
189
-
190
- var conf = confs.filter(function(conf) { return conf.creator == objectID; });
191
- if (conf.length == 0) {
192
- callback(new Error('Could not find confirmation for object ' + objectID));
193
- return;
194
- }
195
-
196
- conf = conf[0];
197
-
198
- // make sure we don't reuse the same time
199
- var localOffset = 0;
200
- do {
201
- time = SteamTotp.time(offset) + localOffset++;
202
- } while (self._usedConfTimes.indexOf(time) != -1);
203
-
204
- self._usedConfTimes.push(time);
205
- if (self._usedConfTimes.length > 60) {
206
- self._usedConfTimes.splice(0, self._usedConfTimes.length - 60); // we don't need to save more than 60 entries
207
- }
208
-
209
- confKey = SteamTotp.getConfirmationKey(identitySecret, time, 'accept');
210
- conf.respond(time, {tag: 'accept', key: confKey}, true, callback);
211
- });
212
- }
213
- };
214
-
215
- /**
216
- * Send a single request to Steam to accept all outstanding confirmations (after loading the list). If one fails, the
217
- * entire request will fail and there will be no way to know which failed without loading the list again.
218
- * @param {number} time
219
- * @param {string} confKey
220
- * @param {string} allowKey
221
- * @param {function} callback
222
- */
223
- SteamCommunity.prototype.acceptAllConfirmations = function(time, confKey, allowKey, callback) {
224
- var self = this;
225
-
226
- this.getConfirmations(time, confKey, function(err, confs) {
227
- if (err) {
228
- callback(err);
229
- return;
230
- }
231
-
232
- if (confs.length == 0) {
233
- callback(null, []);
234
- return;
235
- }
236
-
237
- self.respondToConfirmation(confs.map(function(conf) { return conf.id; }), confs.map(function(conf) { return conf.key; }), time, allowKey, true, function(err) {
238
- if (err) {
239
- callback(err);
240
- return;
241
- }
242
-
243
- callback(err, confs);
244
- });
245
- });
246
- };
247
-
248
- function request(community, url, key, time, tag, params, json, callback) {
249
- if (!community.steamID) {
250
- throw new Error('Must be logged in before trying to do anything with confirmations');
251
- }
252
-
253
- params = params || {};
254
- params.p = SteamTotp.getDeviceID(community.steamID);
255
- params.a = community.steamID.getSteamID64();
256
- params.k = key;
257
- params.t = time;
258
- params.m = 'react';
259
- params.tag = tag;
260
-
261
- var req = {
262
- method: url == 'multiajaxop' ? 'POST' : 'GET',
263
- uri: 'https://steamcommunity.com/mobileconf/' + url,
264
- json: !!json
265
- };
266
-
267
- if (req.method == 'GET') {
268
- req.qs = params;
269
- } else {
270
- req.form = params;
271
- }
272
-
273
- community.httpRequest(req, function(err, response, body) {
274
- if (err) {
275
- callback(err);
276
- return;
277
- }
278
-
279
- callback(null, body);
280
- }, 'steamcommunity');
281
- }
282
-
283
- // Confirmation checker
284
-
285
- /**
286
- * Start automatically polling our confirmations for new ones. The `confKeyNeeded` event will be emitted when we need a confirmation key, or `newConfirmation` when we get a new confirmation
287
- * @param {int} pollInterval - The interval, in milliseconds, at which we will poll for confirmations. This should probably be at least 10,000 to avoid rate-limits.
288
- * @param {Buffer|string|null} [identitySecret=null] - Your identity_secret. If passed, all confirmations will be automatically accepted and nothing will be emitted.
289
- */
290
- SteamCommunity.prototype.startConfirmationChecker = function(pollInterval, identitySecret) {
291
- this._confirmationPollInterval = pollInterval;
292
- this._knownConfirmations = this._knownConfirmations || {};
293
- this._confirmationKeys = this._confirmationKeys || {};
294
- this._identitySecret = identitySecret;
295
-
296
- if(this._confirmationTimer) {
297
- clearTimeout(this._confirmationTimer);
298
- }
299
-
300
- setTimeout(this.checkConfirmations.bind(this), 500);
301
- };
302
-
303
- /**
304
- * Stop automatically polling our confirmations.
305
- */
306
- SteamCommunity.prototype.stopConfirmationChecker = function() {
307
- if(this._confirmationPollInterval) {
308
- delete this._confirmationPollInterval;
309
- }
310
-
311
- if(this._identitySecret) {
312
- delete this._identitySecret;
313
- }
314
-
315
- if(this._confirmationTimer) {
316
- clearTimeout(this._confirmationTimer);
317
- delete this._confirmationTimer;
318
- }
319
- };
320
-
321
- /**
322
- * Run the confirmation checker right now instead of waiting for the next poll.
323
- * Useful to call right after you send/accept an offer that needs confirmation.
324
- */
325
- SteamCommunity.prototype.checkConfirmations = function() {
326
- if(this._confirmationTimer) {
327
- clearTimeout(this._confirmationTimer);
328
- delete this._confirmationTimer;
329
- }
330
-
331
- var self = this;
332
- if(!this._confirmationQueue) {
333
- this._confirmationQueue = Async.queue(function(conf, callback) {
334
- // Worker to process new confirmations
335
- if(self._identitySecret) {
336
- // We should accept this
337
- self.emit('debug', "Accepting confirmation #" + conf.id);
338
- var time = Math.floor(Date.now() / 1000);
339
- conf.respond(time, SteamTotp.getConfirmationKey(self._identitySecret, time, "allow"), true, function(err) {
340
- // If there was an error and it wasn't actually accepted, we'll pick it up again
341
- if (!err) self.emit('confirmationAccepted', conf);
342
- delete self._knownConfirmations[conf.id];
343
- setTimeout(callback, 1000); // Call the callback in 1 second, to make sure the time changes
344
- });
345
- } else {
346
- self.emit('newConfirmation', conf);
347
- setTimeout(callback, 1000); // Call the callback in 1 second, to make sure the time changes
348
- }
349
- }, 1);
350
- }
351
-
352
- this.emit('debug', 'Checking confirmations');
353
-
354
- this._confirmationCheckerGetKey('conf', function(err, key) {
355
- if(err) {
356
- resetTimer();
357
- return;
358
- }
359
-
360
- self.getConfirmations(key.time, key.key, function(err, confirmations) {
361
- if(err) {
362
- self.emit('debug', "Can't check confirmations: " + err.message);
363
- resetTimer();
364
- return;
365
- }
366
-
367
- var known = self._knownConfirmations;
368
-
369
- var newOnes = confirmations.filter(function(conf) {
370
- return !known[conf.id];
371
- });
372
-
373
- if(newOnes.length < 1) {
374
- resetTimer();
375
- return; // No new ones
376
- }
377
-
378
- // We have new confirmations!
379
- newOnes.forEach(function(conf) {
380
- self._knownConfirmations[conf.id] = conf; // Add it to our list of known confirmations
381
- self._confirmationQueue.push(conf);
382
- });
383
-
384
- resetTimer();
385
- });
386
- });
387
-
388
- function resetTimer() {
389
- if(self._confirmationPollInterval) {
390
- self._confirmationTimer = setTimeout(self.checkConfirmations.bind(self), self._confirmationPollInterval);
391
- }
392
- }
393
- };
394
-
395
- SteamCommunity.prototype._confirmationCheckerGetKey = function(tag, callback) {
396
- if(this._identitySecret) {
397
- if(tag == 'details') {
398
- // We don't care about details
399
- callback(new Error("Disabled"));
400
- return;
401
- }
402
-
403
- var time = Math.floor(Date.now() / 1000);
404
- callback(null, {"time": time, "key": SteamTotp.getConfirmationKey(this._identitySecret, time, tag)});
405
- return;
406
- }
407
-
408
- var existing = this._confirmationKeys[tag];
409
- var reusable = ['conf', 'details'];
410
-
411
- // See if we already have a key that we can reuse.
412
- if(reusable.indexOf(tag) != -1 && existing && (Date.now() - (existing.time * 1000) < (1000 * 60 * 5))) {
413
- callback(null, existing);
414
- return;
415
- }
416
-
417
- // We need a fresh one
418
- var self = this;
419
- this.emit('confKeyNeeded', tag, function(err, time, key) {
420
- if(err) {
421
- callback(err);
422
- return;
423
- }
424
-
425
- self._confirmationKeys[tag] = {"time": time, "key": key};
426
- callback(null, {"time": time, "key": key});
427
- });
428
- };
1
+ var SteamCommunity = require('../index.js');
2
+ var Cheerio = require('cheerio');
3
+ var SteamTotp = require('steam-totp');
4
+ var Async = require('async');
5
+
6
+ var CConfirmation = require('../classes/CConfirmation.js');
7
+ var EConfirmationType = require('../resources/EConfirmationType.js');
8
+
9
+ /**
10
+ * Get a list of your account's currently outstanding confirmations.
11
+ * @param {int} time - The unix timestamp with which the following key was generated
12
+ * @param {string} key - The confirmation key that was generated using the preceeding time and the tag 'conf' (this key can be reused)
13
+ * @param {SteamCommunity~getConfirmations} callback - Called when the list of confirmations is received
14
+ */
15
+ SteamCommunity.prototype.getConfirmations = function(time, key, callback) {
16
+ var self = this;
17
+
18
+ // Ugly hack to maintain backward compatibility
19
+ var tag = 'conf';
20
+ if (typeof key == 'object') {
21
+ tag = key.tag;
22
+ key = key.key;
23
+ }
24
+
25
+ // The official Steam app uses the tag 'list', but 'conf' still works so let's use that for backward compatibility.
26
+ request(this, 'getlist', key, time, tag, null, true, function(err, body) {
27
+ if (err) {
28
+ callback(err);
29
+ return;
30
+ }
31
+
32
+ if (!body.success) {
33
+ if (body.needauth) {
34
+ var err = new Error('Not Logged In');
35
+ self._notifySessionExpired(err);
36
+ callback(err);
37
+ return;
38
+ }
39
+
40
+ callback(new Error(body.message || body.detail || 'Failed to get confirmation list'));
41
+ return;
42
+ }
43
+
44
+ var confs = (body.conf || []).map(conf => new CConfirmation(self, {
45
+ id: conf.id,
46
+ type: conf.type,
47
+ creator: conf.creator_id,
48
+ key: conf.nonce,
49
+ title: `${conf.type_name || 'Confirm'} - ${conf.headline || ''}`,
50
+ receiving: conf.type == EConfirmationType.Trade ? ((conf.summary || [])[1] || '') : '',
51
+ sending: (conf.summary || [])[0] || '',
52
+ time: (new Date(conf.creation_time * 1000)).toISOString(), // for backward compatibility
53
+ timestamp: new Date(conf.creation_time * 1000),
54
+ icon: conf.icon || ''
55
+ }));
56
+
57
+ callback(null, confs);
58
+ });
59
+ };
60
+
61
+ /**
62
+ * @callback SteamCommunity~getConfirmations
63
+ * @param {Error|null} err - An Error object on failure, or null on success
64
+ * @param {CConfirmation[]} confirmations - An array of CConfirmation objects
65
+ */
66
+
67
+ /**
68
+ * Get the trade offer ID associated with a particular confirmation
69
+ * @param {int} confID - The ID of the confirmation in question
70
+ * @param {int} time - The unix timestamp with which the following key was generated
71
+ * @param {string} key - The confirmation key that was generated using the preceeding time and the tag "detail" (this key can be reused)
72
+ * @param {SteamCommunity~getConfirmationOfferID} callback
73
+ */
74
+ SteamCommunity.prototype.getConfirmationOfferID = function(confID, time, key, callback) {
75
+ // The official Steam app uses the tag 'detail', but 'details' still works so let's use that for backward compatibility
76
+ request(this, 'detailspage/' + confID, key, time, 'details', null, false, function(err, body) {
77
+ if (err) {
78
+ callback(err);
79
+ return;
80
+ }
81
+
82
+ if (typeof body != 'string') {
83
+ callback(new Error("Cannot load confirmation details"));
84
+ return;
85
+ }
86
+
87
+ var $ = Cheerio.load(body);
88
+ var offer = $('.tradeoffer');
89
+ if(offer.length < 1) {
90
+ callback(null, null);
91
+ return;
92
+ }
93
+
94
+ callback(null, offer.attr('id').split('_')[1]);
95
+ });
96
+ };
97
+
98
+ /**
99
+ * @callback SteamCommunity~getConfirmationOfferID
100
+ * @param {Error|null} err - An Error object on failure, or null on success
101
+ * @param {string} offerID - The trade offer ID associated with the specified confirmation, or null if not for an offer
102
+ */
103
+
104
+ /**
105
+ * Confirm or cancel a given confirmation.
106
+ * @param {int|int[]} confID - The ID of the confirmation in question, or an array of confirmation IDs
107
+ * @param {string|string[]} confKey - The confirmation key associated with the confirmation in question (or an array of them) (not a TOTP key, the `key` property of CConfirmation)
108
+ * @param {int} time - The unix timestamp with which the following key was generated
109
+ * @param {string} key - The confirmation key that was generated using the preceding time and the tag "allow" (if accepting) or "cancel" (if not accepting)
110
+ * @param {boolean} accept - true if you want to accept the confirmation, false if you want to cancel it
111
+ * @param {SteamCommunity~genericErrorCallback} callback - Called when the request is complete
112
+ */
113
+ SteamCommunity.prototype.respondToConfirmation = function(confID, confKey, time, key, accept, callback) {
114
+ // Ugly hack to maintain backward compatibility
115
+ var tag = accept ? 'allow' : 'cancel';
116
+ if (typeof key == 'object') {
117
+ tag = key.tag;
118
+ key = key.key;
119
+ }
120
+
121
+ // The official app uses tags reject/accept, but cancel/allow still works so use these for backward compatibility
122
+ request(this, (confID instanceof Array) ? 'multiajaxop' : 'ajaxop', key, time, tag, {
123
+ op: accept ? 'allow' : 'cancel',
124
+ cid: confID,
125
+ ck: confKey
126
+ }, true, function(err, body) {
127
+ if (!callback) {
128
+ return;
129
+ }
130
+
131
+ if (err) {
132
+ callback(err);
133
+ return;
134
+ }
135
+
136
+ if (body.success) {
137
+ callback(null);
138
+ return;
139
+ }
140
+
141
+ if (body.message) {
142
+ callback(new Error(body.message));
143
+ return;
144
+ }
145
+
146
+ callback(new Error('Could not act on confirmation'));
147
+ });
148
+ };
149
+
150
+ /**
151
+ * Accept a confirmation for a given object (trade offer or market listing) automatically.
152
+ * @param {string} identitySecret
153
+ * @param {number|string} objectID
154
+ * @param {SteamCommunity~genericErrorCallback} callback
155
+ */
156
+ SteamCommunity.prototype.acceptConfirmationForObject = function(identitySecret, objectID, callback) {
157
+ var self = this;
158
+ this._usedConfTimes = this._usedConfTimes || [];
159
+
160
+ if (typeof this._timeOffset !== 'undefined') {
161
+ // time offset is already known and saved
162
+ doConfirmation();
163
+ } else {
164
+ SteamTotp.getTimeOffset(function(err, offset) {
165
+ if (err) {
166
+ callback(err);
167
+ return;
168
+ }
169
+
170
+ self._timeOffset = offset;
171
+ doConfirmation();
172
+
173
+ setTimeout(function() {
174
+ // Delete the saved time offset after 12 hours because why not
175
+ delete self._timeOffset;
176
+ }, 1000 * 60 * 60 * 12).unref();
177
+ });
178
+ }
179
+
180
+ function doConfirmation() {
181
+ var offset = self._timeOffset;
182
+ var time = SteamTotp.time(offset);
183
+ var confKey = SteamTotp.getConfirmationKey(identitySecret, time, 'list');
184
+ self.getConfirmations(time, {tag: 'list', key: confKey}, function(err, confs) {
185
+ if (err) {
186
+ callback(err);
187
+ return;
188
+ }
189
+
190
+ var conf = confs.filter(function(conf) { return conf.creator == objectID; });
191
+ if (conf.length == 0) {
192
+ callback(new Error('Could not find confirmation for object ' + objectID));
193
+ return;
194
+ }
195
+
196
+ conf = conf[0];
197
+
198
+ // make sure we don't reuse the same time
199
+ var localOffset = 0;
200
+ do {
201
+ time = SteamTotp.time(offset) + localOffset++;
202
+ } while (self._usedConfTimes.indexOf(time) != -1);
203
+
204
+ self._usedConfTimes.push(time);
205
+ if (self._usedConfTimes.length > 60) {
206
+ self._usedConfTimes.splice(0, self._usedConfTimes.length - 60); // we don't need to save more than 60 entries
207
+ }
208
+
209
+ confKey = SteamTotp.getConfirmationKey(identitySecret, time, 'accept');
210
+ conf.respond(time, {tag: 'accept', key: confKey}, true, callback);
211
+ });
212
+ }
213
+ };
214
+
215
+ /**
216
+ * Send a single request to Steam to accept all outstanding confirmations (after loading the list). If one fails, the
217
+ * entire request will fail and there will be no way to know which failed without loading the list again.
218
+ * @param {number} time
219
+ * @param {string} confKey
220
+ * @param {string} allowKey
221
+ * @param {function} callback
222
+ */
223
+ SteamCommunity.prototype.acceptAllConfirmations = function(time, confKey, allowKey, callback) {
224
+ var self = this;
225
+
226
+ this.getConfirmations(time, confKey, function(err, confs) {
227
+ if (err) {
228
+ callback(err);
229
+ return;
230
+ }
231
+
232
+ if (confs.length == 0) {
233
+ callback(null, []);
234
+ return;
235
+ }
236
+
237
+ self.respondToConfirmation(confs.map(function(conf) { return conf.id; }), confs.map(function(conf) { return conf.key; }), time, allowKey, true, function(err) {
238
+ if (err) {
239
+ callback(err);
240
+ return;
241
+ }
242
+
243
+ callback(err, confs);
244
+ });
245
+ });
246
+ };
247
+
248
+ function request(community, url, key, time, tag, params, json, callback) {
249
+ if (!community.steamID) {
250
+ throw new Error('Must be logged in before trying to do anything with confirmations');
251
+ }
252
+
253
+ params = params || {};
254
+ params.p = SteamTotp.getDeviceID(community.steamID);
255
+ params.a = community.steamID.getSteamID64();
256
+ params.k = key;
257
+ params.t = time;
258
+ params.m = 'react';
259
+ params.tag = tag;
260
+
261
+ var req = {
262
+ method: url == 'multiajaxop' ? 'POST' : 'GET',
263
+ uri: 'https://steamcommunity.com/mobileconf/' + url,
264
+ json: !!json
265
+ };
266
+
267
+ if (req.method == 'GET') {
268
+ req.qs = params;
269
+ } else {
270
+ req.form = params;
271
+ }
272
+
273
+ community.httpRequest(req, function(err, response, body) {
274
+ if (err) {
275
+ callback(err);
276
+ return;
277
+ }
278
+
279
+ callback(null, body);
280
+ }, 'steamcommunity');
281
+ }
282
+
283
+ // Confirmation checker
284
+
285
+ /**
286
+ * Start automatically polling our confirmations for new ones. The `confKeyNeeded` event will be emitted when we need a confirmation key, or `newConfirmation` when we get a new confirmation
287
+ * @param {int} pollInterval - The interval, in milliseconds, at which we will poll for confirmations. This should probably be at least 10,000 to avoid rate-limits.
288
+ * @param {Buffer|string|null} [identitySecret=null] - Your identity_secret. If passed, all confirmations will be automatically accepted and nothing will be emitted.
289
+ */
290
+ SteamCommunity.prototype.startConfirmationChecker = function(pollInterval, identitySecret) {
291
+ this._confirmationPollInterval = pollInterval;
292
+ this._knownConfirmations = this._knownConfirmations || {};
293
+ this._confirmationKeys = this._confirmationKeys || {};
294
+ this._identitySecret = identitySecret;
295
+
296
+ if(this._confirmationTimer) {
297
+ clearTimeout(this._confirmationTimer);
298
+ }
299
+
300
+ setTimeout(this.checkConfirmations.bind(this), 500);
301
+ };
302
+
303
+ /**
304
+ * Stop automatically polling our confirmations.
305
+ */
306
+ SteamCommunity.prototype.stopConfirmationChecker = function() {
307
+ if(this._confirmationPollInterval) {
308
+ delete this._confirmationPollInterval;
309
+ }
310
+
311
+ if(this._identitySecret) {
312
+ delete this._identitySecret;
313
+ }
314
+
315
+ if(this._confirmationTimer) {
316
+ clearTimeout(this._confirmationTimer);
317
+ delete this._confirmationTimer;
318
+ }
319
+ };
320
+
321
+ /**
322
+ * Run the confirmation checker right now instead of waiting for the next poll.
323
+ * Useful to call right after you send/accept an offer that needs confirmation.
324
+ */
325
+ SteamCommunity.prototype.checkConfirmations = function() {
326
+ if(this._confirmationTimer) {
327
+ clearTimeout(this._confirmationTimer);
328
+ delete this._confirmationTimer;
329
+ }
330
+
331
+ var self = this;
332
+ if(!this._confirmationQueue) {
333
+ this._confirmationQueue = Async.queue(function(conf, callback) {
334
+ // Worker to process new confirmations
335
+ if(self._identitySecret) {
336
+ // We should accept this
337
+ self.emit('debug', "Accepting confirmation #" + conf.id);
338
+ var time = Math.floor(Date.now() / 1000);
339
+ conf.respond(time, SteamTotp.getConfirmationKey(self._identitySecret, time, "allow"), true, function(err) {
340
+ // If there was an error and it wasn't actually accepted, we'll pick it up again
341
+ if (!err) self.emit('confirmationAccepted', conf);
342
+ delete self._knownConfirmations[conf.id];
343
+ setTimeout(callback, 1000); // Call the callback in 1 second, to make sure the time changes
344
+ });
345
+ } else {
346
+ self.emit('newConfirmation', conf);
347
+ setTimeout(callback, 1000); // Call the callback in 1 second, to make sure the time changes
348
+ }
349
+ }, 1);
350
+ }
351
+
352
+ this.emit('debug', 'Checking confirmations');
353
+
354
+ this._confirmationCheckerGetKey('conf', function(err, key) {
355
+ if(err) {
356
+ resetTimer();
357
+ return;
358
+ }
359
+
360
+ self.getConfirmations(key.time, key.key, function(err, confirmations) {
361
+ if(err) {
362
+ self.emit('debug', "Can't check confirmations: " + err.message);
363
+ resetTimer();
364
+ return;
365
+ }
366
+
367
+ var known = self._knownConfirmations;
368
+
369
+ var newOnes = confirmations.filter(function(conf) {
370
+ return !known[conf.id];
371
+ });
372
+
373
+ if(newOnes.length < 1) {
374
+ resetTimer();
375
+ return; // No new ones
376
+ }
377
+
378
+ // We have new confirmations!
379
+ newOnes.forEach(function(conf) {
380
+ self._knownConfirmations[conf.id] = conf; // Add it to our list of known confirmations
381
+ self._confirmationQueue.push(conf);
382
+ });
383
+
384
+ resetTimer();
385
+ });
386
+ });
387
+
388
+ function resetTimer() {
389
+ if(self._confirmationPollInterval) {
390
+ self._confirmationTimer = setTimeout(self.checkConfirmations.bind(self), self._confirmationPollInterval);
391
+ }
392
+ }
393
+ };
394
+
395
+ SteamCommunity.prototype._confirmationCheckerGetKey = function(tag, callback) {
396
+ if(this._identitySecret) {
397
+ if(tag == 'details') {
398
+ // We don't care about details
399
+ callback(new Error("Disabled"));
400
+ return;
401
+ }
402
+
403
+ var time = Math.floor(Date.now() / 1000);
404
+ callback(null, {"time": time, "key": SteamTotp.getConfirmationKey(this._identitySecret, time, tag)});
405
+ return;
406
+ }
407
+
408
+ var existing = this._confirmationKeys[tag];
409
+ var reusable = ['conf', 'details'];
410
+
411
+ // See if we already have a key that we can reuse.
412
+ if(reusable.indexOf(tag) != -1 && existing && (Date.now() - (existing.time * 1000) < (1000 * 60 * 5))) {
413
+ callback(null, existing);
414
+ return;
415
+ }
416
+
417
+ // We need a fresh one
418
+ var self = this;
419
+ this.emit('confKeyNeeded', tag, function(err, time, key) {
420
+ if(err) {
421
+ callback(err);
422
+ return;
423
+ }
424
+
425
+ self._confirmationKeys[tag] = {"time": time, "key": key};
426
+ callback(null, {"time": time, "key": key});
427
+ });
428
+ };