steamcommunity 3.43.1 → 3.44.2

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,767 +1,767 @@
1
- const Cheerio = require('cheerio');
2
- const Crypto = require('crypto');
3
- const imageSize = require('image-size');
4
- const SteamID = require('steamid');
5
-
6
- const SteamCommunity = require('../index.js');
7
-
8
- const CEconItem = require('../classes/CEconItem.js');
9
- const Helpers = require('./helpers.js');
10
-
11
- SteamCommunity.prototype.addFriend = function(userID, callback) {
12
- if(typeof userID === 'string') {
13
- userID = new SteamID(userID);
14
- }
15
-
16
- var self = this;
17
- this.httpRequestPost({
18
- "uri": "https://steamcommunity.com/actions/AddFriendAjax",
19
- "form": {
20
- "accept_invite": 0,
21
- "sessionID": this.getSessionID(),
22
- "steamid": userID.toString()
23
- },
24
- "json": true
25
- }, function(err, response, body) {
26
- if(!callback) {
27
- return;
28
- }
29
-
30
- if (err) {
31
- callback(err);
32
- return;
33
- }
34
-
35
- if(body.success) {
36
- callback(null);
37
- } else {
38
- callback(new Error("Unknown error"));
39
- }
40
- }, "steamcommunity");
41
- };
42
-
43
- SteamCommunity.prototype.acceptFriendRequest = function(userID, callback) {
44
- if(typeof userID === 'string') {
45
- userID = new SteamID(userID);
46
- }
47
-
48
- var self = this;
49
- this.httpRequestPost({
50
- "uri": "https://steamcommunity.com/actions/AddFriendAjax",
51
- "form": {
52
- "accept_invite": 1,
53
- "sessionID": this.getSessionID(),
54
- "steamid": userID.toString()
55
- }
56
- }, function(err, response, body) {
57
- if(!callback) {
58
- return;
59
- }
60
-
61
- callback(err || null);
62
- }, "steamcommunity");
63
- };
64
-
65
- SteamCommunity.prototype.removeFriend = function(userID, callback) {
66
- if(typeof userID === 'string') {
67
- userID = new SteamID(userID);
68
- }
69
-
70
- var self = this;
71
- this.httpRequestPost({
72
- "uri": "https://steamcommunity.com/actions/RemoveFriendAjax",
73
- "form": {
74
- "sessionID": this.getSessionID(),
75
- "steamid": userID.toString()
76
- }
77
- }, function(err, response, body) {
78
- if(!callback) {
79
- return;
80
- }
81
-
82
- callback(err || null);
83
- }, "steamcommunity");
84
- };
85
-
86
- SteamCommunity.prototype.blockCommunication = function(userID, callback) {
87
- if(typeof userID === 'string') {
88
- userID = new SteamID(userID);
89
- }
90
-
91
- var self = this;
92
- this.httpRequestPost({
93
- "uri": "https://steamcommunity.com/actions/BlockUserAjax",
94
- "form": {
95
- "sessionID": this.getSessionID(),
96
- "steamid": userID.toString()
97
- }
98
- }, function(err, response, body) {
99
- if(!callback) {
100
- return;
101
- }
102
-
103
- callback(err || null);
104
- }, "steamcommunity");
105
- };
106
-
107
- SteamCommunity.prototype.unblockCommunication = function(userID, callback) {
108
- if(typeof userID === 'string') {
109
- userID = new SteamID(userID);
110
- }
111
-
112
- var form = {"action": "unignore"};
113
- form['friends[' + userID.toString() + ']'] = 1;
114
-
115
- this._myProfile('friends/blocked/', form, function(err, response, body) {
116
- if(!callback) {
117
- return;
118
- }
119
-
120
- if(err || response.statusCode >= 400) {
121
- callback(err || new Error("HTTP error " + response.statusCode));
122
- return;
123
- }
124
-
125
- callback(null);
126
- });
127
- };
128
-
129
- SteamCommunity.prototype.postUserComment = function(userID, message, callback) {
130
- if(typeof userID === 'string') {
131
- userID = new SteamID(userID);
132
- }
133
-
134
- var self = this;
135
- this.httpRequestPost({
136
- "uri": "https://steamcommunity.com/comment/Profile/post/" + userID.toString() + "/-1",
137
- "form": {
138
- "comment": message,
139
- "count": 1,
140
- "sessionid": this.getSessionID()
141
- },
142
- "json": true
143
- }, function(err, response, body) {
144
- if(!callback) {
145
- return;
146
- }
147
-
148
- if (err) {
149
- callback(err);
150
- return;
151
- }
152
-
153
- if(body.success) {
154
- const $ = Cheerio.load(body.comments_html);
155
- const commentID = $('.commentthread_comment').attr('id').split('_')[1];
156
-
157
- callback(null, commentID);
158
- } else if(body.error) {
159
- callback(new Error(body.error));
160
- } else {
161
- callback(new Error("Unknown error"));
162
- }
163
- }, "steamcommunity");
164
- };
165
-
166
- SteamCommunity.prototype.deleteUserComment = function(userID, commentID, callback) {
167
- if(typeof userID === 'string') {
168
- userID = new SteamID(userID);
169
- }
170
-
171
- var self = this;
172
- this.httpRequestPost({
173
- "uri": "https://steamcommunity.com/comment/Profile/delete/" + userID.toString() + "/-1",
174
- "form": {
175
- "gidcomment": commentID,
176
- "start": 0,
177
- "count": 1,
178
- "sessionid": this.getSessionID(),
179
- "feature2": -1
180
- },
181
- "json": true
182
- }, function(err, response, body) {
183
- if(!callback) {
184
- return;
185
- }
186
-
187
- if (err) {
188
- callback(err);
189
- return;
190
- }
191
-
192
- if(body.success && !body.comments_html.includes(commentID)) {
193
- callback(null);
194
- } else if(body.error) {
195
- callback(new Error(body.error));
196
- } else if(body.comments_html.includes(commentID)) {
197
- callback(new Error("Failed to delete comment"));
198
- } else {
199
- callback(new Error("Unknown error"));
200
- }
201
- }, "steamcommunity");
202
- };
203
-
204
- SteamCommunity.prototype.getUserComments = function(userID, options, callback) {
205
- if(typeof userID === 'string') {
206
- userID = new SteamID(userID);
207
- }
208
-
209
- if (typeof options === 'function') {
210
- callback = options;
211
- options = {};
212
- }
213
-
214
- var form = Object.assign({
215
- "start": 0,
216
- "count": 0,
217
- "feature2": -1,
218
- "sessionid": this.getSessionID()
219
- }, options);
220
-
221
- this.httpRequestPost({
222
- "uri": "https://steamcommunity.com/comment/Profile/render/" + userID.toString() + "/-1",
223
- "form": form,
224
- "json": true
225
- }, function(err, response, body) {
226
- if(!callback) {
227
- return;
228
- }
229
-
230
- if (err) {
231
- callback(err);
232
- return;
233
- }
234
-
235
- if(body.success) {
236
- const $ = Cheerio.load(body.comments_html);
237
- const comments = $(".commentthread_comment.responsive_body_text[id]").map((i, elem) => {
238
- var $elem = $(elem),
239
- $commentContent = $elem.find(".commentthread_comment_text");
240
- return {
241
- id: $elem.attr("id").split("_")[1],
242
- author: {
243
- steamID: new SteamID("[U:1:" + $elem.find("[data-miniprofile]").data("miniprofile") + "]"),
244
- name: $elem.find("bdi").text(),
245
- avatar: $elem.find(".playerAvatar img[src]").attr("src"),
246
- state: $elem.find(".playerAvatar").attr("class").split(" ").pop()
247
- },
248
- date: new Date($elem.find(".commentthread_comment_timestamp").data("timestamp") * 1000),
249
- text: $commentContent.text().trim(),
250
- html: $commentContent.html().trim()
251
- }
252
- }).get();
253
-
254
- callback(null, comments, body.total_count);
255
- } else if(body.error) {
256
- callback(new Error(body.error));
257
- } else {
258
- callback(new Error("Unknown error"));
259
- }
260
- }, "steamcommunity");
261
- };
262
-
263
- SteamCommunity.prototype.inviteUserToGroup = function(userID, groupID, callback) {
264
- if(typeof userID === 'string') {
265
- userID = new SteamID(userID);
266
- }
267
-
268
- var self = this;
269
- this.httpRequestPost({
270
- "uri": "https://steamcommunity.com/actions/GroupInvite",
271
- "form": {
272
- "group": groupID.toString(),
273
- "invitee": userID.toString(),
274
- "json": 1,
275
- "sessionID": this.getSessionID(),
276
- "type": "groupInvite"
277
- },
278
- "json": true
279
- }, function(err, response, body) {
280
- if(!callback) {
281
- return;
282
- }
283
-
284
- if (err) {
285
- callback(err);
286
- return;
287
- }
288
-
289
- if(body.results == 'OK') {
290
- callback(null);
291
- } else if(body.results) {
292
- callback(new Error(body.results));
293
- } else {
294
- callback(new Error("Unknown error"));
295
- }
296
- }, "steamcommunity");
297
- };
298
-
299
- SteamCommunity.prototype.getUserAliases = function(userID, callback) {
300
- if (typeof userID === 'string') {
301
- userID = new SteamID(userID);
302
- }
303
-
304
- this.httpRequestGet({
305
- "uri": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/ajaxaliases",
306
- "json": true
307
- }, function(err, response, body) {
308
- if (err) {
309
- callback(err);
310
- return;
311
- }
312
-
313
- if (typeof body !== 'object') {
314
- callback(new Error("Malformed response"));
315
- return;
316
- }
317
-
318
- callback(null, body.map(function(entry) {
319
- entry.timechanged = Helpers.decodeSteamTime(entry.timechanged);
320
- return entry;
321
- }));
322
- }, "steamcommunity");
323
- };
324
-
325
- /**
326
- * Get the background URL of user's profile.
327
- * @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
328
- * @param {function} callback
329
- */
330
- SteamCommunity.prototype.getUserProfileBackground = function(userID, callback) {
331
- if (typeof userID === 'string') {
332
- userID = new SteamID(userID);
333
- }
334
-
335
- this.httpRequest("https://steamcommunity.com/profiles/" + userID.getSteamID64(), (err, response, body) => {
336
- if (err) {
337
- callback(err);
338
- return;
339
- }
340
-
341
- var $ = Cheerio.load(body);
342
-
343
- var $privateProfileInfo = $('.profile_private_info');
344
- if ($privateProfileInfo.length > 0) {
345
- callback(new Error($privateProfileInfo.text().trim()));
346
- return;
347
- }
348
-
349
- if ($('body').hasClass('has_profile_background')) {
350
- var backgroundUrl = $('div.profile_background_image_content').css('background-image');
351
- var matcher = backgroundUrl.match(/\(([^)]+)\)/);
352
-
353
- if (matcher.length != 2 || !matcher[1].length) {
354
- callback(new Error("Malformed response"));
355
- } else {
356
- callback(null, matcher[1]);
357
- }
358
- } else {
359
- callback(null, null);
360
- }
361
- }, "steamcommunity");
362
- };
363
-
364
- SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) {
365
- if (typeof userID === 'string') {
366
- userID = new SteamID(userID);
367
- }
368
-
369
- if (typeof userID === 'function') {
370
- callback = userID;
371
- userID = this.steamID;
372
- }
373
-
374
- if (!userID) {
375
- callback(new Error("No SteamID specified and not logged in"));
376
- return;
377
- }
378
-
379
- var self = this;
380
- this.httpRequest("https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory/", function(err, response, body) {
381
- if (err) {
382
- callback(err);
383
- return;
384
- }
385
-
386
- var match = body.match(/var g_rgAppContextData = ([^\n]+);\r?\n/);
387
- if (!match) {
388
- var errorMessage = "Malformed response";
389
-
390
- if(body.match(/0 items in their inventory\./)){
391
- callback(null, {});
392
- return;
393
- }else if(body.match(/inventory is currently private\./)){
394
- errorMessage = "Private inventory";
395
- }else if(body.match(/profile\_private\_info/)){
396
- errorMessage = "Private profile";
397
- }
398
-
399
- callback(new Error(errorMessage));
400
- return;
401
- }
402
-
403
- var data;
404
- try {
405
- data = JSON.parse(match[1]);
406
- } catch(e) {
407
- callback(new Error("Malformed response"));
408
- return;
409
- }
410
-
411
- callback(null, data);
412
- }, "steamcommunity");
413
- };
414
-
415
- /**
416
- * Get the contents of a user's inventory context.
417
- * @deprecated Use getUserInventoryContents instead
418
- * @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
419
- * @param {int} appID - The Steam application ID of the game for which you want an inventory
420
- * @param {int} contextID - The ID of the "context" within the game you want to retrieve
421
- * @param {boolean} tradableOnly - true to get only tradable items and currencies
422
- * @param {function} callback
423
- */
424
- SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, tradableOnly, callback) {
425
- var self = this;
426
-
427
- if (typeof userID === 'string') {
428
- userID = new SteamID(userID);
429
- }
430
-
431
- var endpoint = "/profiles/" + userID.getSteamID64();
432
- get([], []);
433
-
434
- function get(inventory, currency, start) {
435
- self.httpRequest({
436
- "uri": "https://steamcommunity.com" + endpoint + "/inventory/json/" + appID + "/" + contextID,
437
- "headers": {
438
- "Referer": "https://steamcommunity.com" + endpoint + "/inventory"
439
- },
440
- "qs": {
441
- "start": start,
442
- "trading": tradableOnly ? 1 : undefined
443
- },
444
- "json": true
445
- }, function(err, response, body) {
446
- if (err) {
447
- callback(err);
448
- return;
449
- }
450
-
451
- if (!body || !body.success || !body.rgInventory || !body.rgDescriptions || !body.rgCurrency) {
452
- if (body) {
453
- callback(new Error(body.Error || "Malformed response"));
454
- } else {
455
- callback(new Error("Malformed response"));
456
- }
457
-
458
- return;
459
- }
460
-
461
- var i;
462
- for (i in body.rgInventory) {
463
- if (!body.rgInventory.hasOwnProperty(i)) {
464
- continue;
465
- }
466
-
467
- inventory.push(new CEconItem(body.rgInventory[i], body.rgDescriptions, contextID));
468
- }
469
-
470
- for (i in body.rgCurrency) {
471
- if (!body.rgCurrency.hasOwnProperty(i)) {
472
- continue;
473
- }
474
-
475
- currency.push(new CEconItem(body.rgInventory[i], body.rgDescriptions, contextID));
476
- }
477
-
478
- if (body.more) {
479
- var match = response.request.uri.href.match(/\/(profiles|id)\/([^\/]+)\//);
480
- if(match) {
481
- endpoint = "/" + match[1] + "/" + match[2];
482
- }
483
-
484
- get(inventory, currency, body.more_start);
485
- } else {
486
- callback(null, inventory, currency);
487
- }
488
- }, "steamcommunity");
489
- }
490
- };
491
-
492
- /**
493
- * Get the contents of a user's inventory context.
494
- * @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
495
- * @param {int} appID - The Steam application ID of the game for which you want an inventory
496
- * @param {int} contextID - The ID of the "context" within the game you want to retrieve
497
- * @param {boolean} tradableOnly - true to get only tradable items and currencies
498
- * @param {string} [language] - The language of item descriptions to return. Omit for default (which may either be English or your account's chosen language)
499
- * @param {function} callback
500
- */
501
- SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, contextID, tradableOnly, language, callback) {
502
- if (typeof language === 'function') {
503
- callback = language;
504
- language = "english";
505
- }
506
-
507
- if (!userID) {
508
- callback(new Error("The user's SteamID is invalid or missing."));
509
- return;
510
- }
511
-
512
- var self = this;
513
-
514
- if (typeof userID === 'string') {
515
- userID = new SteamID(userID);
516
- }
517
-
518
- var pos = 1;
519
- get([], []);
520
-
521
- function get(inventory, currency, start) {
522
- self.httpRequest({
523
- "uri": "https://steamcommunity.com/inventory/" + userID.getSteamID64() + "/" + appID + "/" + contextID,
524
- "headers": {
525
- "Referer": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory"
526
- },
527
- "qs": {
528
- "l": language, // Default language
529
- "count": 5000, // Max items per 'page'
530
- "start_assetid": start
531
- },
532
- "json": true
533
- }, function(err, response, body) {
534
- if (err) {
535
- if (err.message == "HTTP error 403" && body === null) {
536
- // 403 with a body of "null" means the inventory/profile is private.
537
- if (self.steamID && userID.getSteamID64() == self.steamID.getSteamID64()) {
538
- // We can never get private profile error for our own inventory!
539
- self._notifySessionExpired(err);
540
- }
541
-
542
- callback(new Error("This profile is private."));
543
- return;
544
- }
545
-
546
- if (err.message == "HTTP error 500" && body && body.error) {
547
- err = new Error(body.error);
548
-
549
- var match = body.error.match(/^(.+) \((\d+)\)$/);
550
- if (match) {
551
- err.message = match[1];
552
- err.eresult = match[2];
553
- callback(err);
554
- return;
555
- }
556
- }
557
-
558
- callback(err);
559
- return;
560
- }
561
-
562
- if (body && body.success && body.total_inventory_count === 0) {
563
- // Empty inventory
564
- callback(null, [], [], 0);
565
- return;
566
- }
567
-
568
- if (!body || !body.success || !body.assets || !body.descriptions) {
569
- if (body) {
570
- // Dunno if the error/Error property even exists on this new endpoint
571
- callback(new Error(body.error || body.Error || "Malformed response"));
572
- } else {
573
- callback(new Error("Malformed response"));
574
- }
575
-
576
- return;
577
- }
578
-
579
- for (var i = 0; i < body.assets.length; i++) {
580
- var description = getDescription(body.descriptions, body.assets[i].classid, body.assets[i].instanceid);
581
-
582
- if (!tradableOnly || (description && description.tradable)) {
583
- body.assets[i].pos = pos++;
584
- (body.assets[i].currencyid ? currency : inventory).push(new CEconItem(body.assets[i], description, contextID));
585
- }
586
- }
587
-
588
- if (body.more_items) {
589
- get(inventory, currency, body.last_assetid);
590
- } else {
591
- callback(null, inventory, currency, body.total_inventory_count);
592
- }
593
- }, "steamcommunity");
594
- }
595
-
596
- // A bit of optimization; objects are hash tables so it's more efficient to look up by key than to iterate an array
597
- var quickDescriptionLookup = {};
598
-
599
- function getDescription(descriptions, classID, instanceID) {
600
- var key = classID + '_' + (instanceID || '0'); // instanceID can be undefined, in which case it's 0.
601
-
602
- if (quickDescriptionLookup[key]) {
603
- return quickDescriptionLookup[key];
604
- }
605
-
606
- for (var i = 0; i < descriptions.length; i++) {
607
- quickDescriptionLookup[descriptions[i].classid + '_' + (descriptions[i].instanceid || '0')] = descriptions[i];
608
- }
609
-
610
- return quickDescriptionLookup[key];
611
- }
612
- };
613
-
614
- /**
615
- * Upload an image to Steam and send it to another user over Steam chat.
616
- * @param {SteamID|string} userID - Either a SteamID object or a string that can parse into one
617
- * @param {Buffer} imageContentsBuffer - The image contents, as a Buffer
618
- * @param {{spoiler?: boolean}} [options]
619
- * @param {function} callback
620
- */
621
- SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer, options, callback) {
622
- if (typeof options == 'function') {
623
- callback = options;
624
- options = {};
625
- }
626
-
627
- options = options || {};
628
-
629
- if (!userID) {
630
- callback(new Error('The user\'s SteamID is invalid or missing'));
631
- return;
632
- }
633
-
634
- if (typeof userID == 'string') {
635
- userID = new SteamID(userID);
636
- }
637
-
638
- if (!Buffer.isBuffer(imageContentsBuffer)) {
639
- callback(new Error('The image contents must be a Buffer containing an image'));
640
- return;
641
- }
642
-
643
- var imageDetails = null;
644
- try {
645
- imageDetails = imageSize(imageContentsBuffer);
646
- } catch (ex) {
647
- callback(ex);
648
- return;
649
- }
650
-
651
- var imageHash = Crypto.createHash('sha1');
652
- imageHash.update(imageContentsBuffer);
653
- imageHash = imageHash.digest('hex');
654
-
655
- var filename = Date.now() + '_image.' + imageDetails.type;
656
-
657
- this.httpRequestPost({
658
- uri: 'https://steamcommunity.com/chat/beginfileupload/?l=english',
659
- headers: {
660
- referer: 'https://steamcommunity.com/chat/'
661
- },
662
- formData: { // it's multipart
663
- sessionid: this.getSessionID(),
664
- l: 'english',
665
- file_size: imageContentsBuffer.length,
666
- file_name: filename,
667
- file_sha: imageHash,
668
- file_image_width: imageDetails.width,
669
- file_image_height: imageDetails.height,
670
- file_type: 'image/' + (imageDetails.type == 'jpg' ? 'jpeg' : imageDetails.type)
671
- },
672
- json: true
673
- }, (err, res, body) => {
674
- if (err) {
675
- if (body && body.success) {
676
- var err2 = Helpers.eresultError(body.success);
677
- if (body.message) {
678
- err2.message = body.message;
679
- }
680
- callback(err2);
681
- } else {
682
- callback(err);
683
- }
684
- return;
685
- }
686
-
687
- if (body.success != 1) {
688
- callback(Helpers.eresultError(body.success));
689
- return;
690
- }
691
-
692
- var hmac = body.hmac;
693
- var timestamp = body.timestamp;
694
- var startResult = body.result;
695
-
696
- if (!startResult || !startResult.ugcid || !startResult.url_host || !startResult.request_headers) {
697
- callback(new Error('Malformed response'));
698
- return;
699
- }
700
-
701
- // Okay, now we need to PUT the file to the provided URL
702
- var uploadUrl = (startResult.use_https ? 'https' : 'http') + '://' + startResult.url_host + startResult.url_path;
703
- var headers = {};
704
- startResult.request_headers.forEach((header) => {
705
- headers[header.name.toLowerCase()] = header.value;
706
- });
707
-
708
- this.httpRequest({
709
- uri: uploadUrl,
710
- method: 'PUT',
711
- headers,
712
- body: imageContentsBuffer
713
- }, (err, res, body) => {
714
- if (err) {
715
- callback(err);
716
- return;
717
- }
718
-
719
- // Now we need to commit the upload
720
- this.httpRequestPost({
721
- uri: 'https://steamcommunity.com/chat/commitfileupload/',
722
- headers: {
723
- referer: 'https://steamcommunity.com/chat/'
724
- },
725
- formData: { // it's multipart again
726
- sessionid: this.getSessionID(),
727
- l: 'english',
728
- file_name: filename,
729
- file_sha: imageHash,
730
- success: '1',
731
- ugcid: startResult.ugcid,
732
- file_type: 'image/' + (imageDetails.type == 'jpg' ? 'jpeg' : imageDetails.type),
733
- file_image_width: imageDetails.width,
734
- file_image_height: imageDetails.height,
735
- timestamp,
736
- hmac,
737
- friend_steamid: userID.getSteamID64(),
738
- spoiler: options.spoiler ? '1' : '0'
739
- },
740
- json: true
741
- }, (err, res, body) => {
742
- if (err) {
743
- callback(err);
744
- return;
745
- }
746
-
747
- if (body.success != 1) {
748
- callback(Helpers.eresultError(body.success));
749
- return;
750
- }
751
-
752
- if (body.result.success != 1) {
753
- // lol valve
754
- callback(Helpers.eresultError(body.result.success));
755
- return;
756
- }
757
-
758
- if (!body.result.details || !body.result.details.url) {
759
- callback(new Error('Malformed response'));
760
- return;
761
- }
762
-
763
- callback(null, body.result.details.url);
764
- }, 'steamcommunity');
765
- }, 'steamcommunity');
766
- }, 'steamcommunity');
767
- };
1
+ const Cheerio = require('cheerio');
2
+ const Crypto = require('crypto');
3
+ const imageSize = require('image-size');
4
+ const SteamID = require('steamid');
5
+
6
+ const SteamCommunity = require('../index.js');
7
+
8
+ const CEconItem = require('../classes/CEconItem.js');
9
+ const Helpers = require('./helpers.js');
10
+
11
+ SteamCommunity.prototype.addFriend = function(userID, callback) {
12
+ if(typeof userID === 'string') {
13
+ userID = new SteamID(userID);
14
+ }
15
+
16
+ var self = this;
17
+ this.httpRequestPost({
18
+ "uri": "https://steamcommunity.com/actions/AddFriendAjax",
19
+ "form": {
20
+ "accept_invite": 0,
21
+ "sessionID": this.getSessionID(),
22
+ "steamid": userID.toString()
23
+ },
24
+ "json": true
25
+ }, function(err, response, body) {
26
+ if(!callback) {
27
+ return;
28
+ }
29
+
30
+ if (err) {
31
+ callback(err);
32
+ return;
33
+ }
34
+
35
+ if(body.success) {
36
+ callback(null);
37
+ } else {
38
+ callback(new Error("Unknown error"));
39
+ }
40
+ }, "steamcommunity");
41
+ };
42
+
43
+ SteamCommunity.prototype.acceptFriendRequest = function(userID, callback) {
44
+ if(typeof userID === 'string') {
45
+ userID = new SteamID(userID);
46
+ }
47
+
48
+ var self = this;
49
+ this.httpRequestPost({
50
+ "uri": "https://steamcommunity.com/actions/AddFriendAjax",
51
+ "form": {
52
+ "accept_invite": 1,
53
+ "sessionID": this.getSessionID(),
54
+ "steamid": userID.toString()
55
+ }
56
+ }, function(err, response, body) {
57
+ if(!callback) {
58
+ return;
59
+ }
60
+
61
+ callback(err || null);
62
+ }, "steamcommunity");
63
+ };
64
+
65
+ SteamCommunity.prototype.removeFriend = function(userID, callback) {
66
+ if(typeof userID === 'string') {
67
+ userID = new SteamID(userID);
68
+ }
69
+
70
+ var self = this;
71
+ this.httpRequestPost({
72
+ "uri": "https://steamcommunity.com/actions/RemoveFriendAjax",
73
+ "form": {
74
+ "sessionID": this.getSessionID(),
75
+ "steamid": userID.toString()
76
+ }
77
+ }, function(err, response, body) {
78
+ if(!callback) {
79
+ return;
80
+ }
81
+
82
+ callback(err || null);
83
+ }, "steamcommunity");
84
+ };
85
+
86
+ SteamCommunity.prototype.blockCommunication = function(userID, callback) {
87
+ if(typeof userID === 'string') {
88
+ userID = new SteamID(userID);
89
+ }
90
+
91
+ var self = this;
92
+ this.httpRequestPost({
93
+ "uri": "https://steamcommunity.com/actions/BlockUserAjax",
94
+ "form": {
95
+ "sessionID": this.getSessionID(),
96
+ "steamid": userID.toString()
97
+ }
98
+ }, function(err, response, body) {
99
+ if(!callback) {
100
+ return;
101
+ }
102
+
103
+ callback(err || null);
104
+ }, "steamcommunity");
105
+ };
106
+
107
+ SteamCommunity.prototype.unblockCommunication = function(userID, callback) {
108
+ if(typeof userID === 'string') {
109
+ userID = new SteamID(userID);
110
+ }
111
+
112
+ var form = {"action": "unignore"};
113
+ form['friends[' + userID.toString() + ']'] = 1;
114
+
115
+ this._myProfile('friends/blocked/', form, function(err, response, body) {
116
+ if(!callback) {
117
+ return;
118
+ }
119
+
120
+ if(err || response.statusCode >= 400) {
121
+ callback(err || new Error("HTTP error " + response.statusCode));
122
+ return;
123
+ }
124
+
125
+ callback(null);
126
+ });
127
+ };
128
+
129
+ SteamCommunity.prototype.postUserComment = function(userID, message, callback) {
130
+ if(typeof userID === 'string') {
131
+ userID = new SteamID(userID);
132
+ }
133
+
134
+ var self = this;
135
+ this.httpRequestPost({
136
+ "uri": "https://steamcommunity.com/comment/Profile/post/" + userID.toString() + "/-1",
137
+ "form": {
138
+ "comment": message,
139
+ "count": 1,
140
+ "sessionid": this.getSessionID()
141
+ },
142
+ "json": true
143
+ }, function(err, response, body) {
144
+ if(!callback) {
145
+ return;
146
+ }
147
+
148
+ if (err) {
149
+ callback(err);
150
+ return;
151
+ }
152
+
153
+ if(body.success) {
154
+ const $ = Cheerio.load(body.comments_html);
155
+ const commentID = $('.commentthread_comment').attr('id').split('_')[1];
156
+
157
+ callback(null, commentID);
158
+ } else if(body.error) {
159
+ callback(new Error(body.error));
160
+ } else {
161
+ callback(new Error("Unknown error"));
162
+ }
163
+ }, "steamcommunity");
164
+ };
165
+
166
+ SteamCommunity.prototype.deleteUserComment = function(userID, commentID, callback) {
167
+ if(typeof userID === 'string') {
168
+ userID = new SteamID(userID);
169
+ }
170
+
171
+ var self = this;
172
+ this.httpRequestPost({
173
+ "uri": "https://steamcommunity.com/comment/Profile/delete/" + userID.toString() + "/-1",
174
+ "form": {
175
+ "gidcomment": commentID,
176
+ "start": 0,
177
+ "count": 1,
178
+ "sessionid": this.getSessionID(),
179
+ "feature2": -1
180
+ },
181
+ "json": true
182
+ }, function(err, response, body) {
183
+ if(!callback) {
184
+ return;
185
+ }
186
+
187
+ if (err) {
188
+ callback(err);
189
+ return;
190
+ }
191
+
192
+ if(body.success && !body.comments_html.includes(commentID)) {
193
+ callback(null);
194
+ } else if(body.error) {
195
+ callback(new Error(body.error));
196
+ } else if(body.comments_html.includes(commentID)) {
197
+ callback(new Error("Failed to delete comment"));
198
+ } else {
199
+ callback(new Error("Unknown error"));
200
+ }
201
+ }, "steamcommunity");
202
+ };
203
+
204
+ SteamCommunity.prototype.getUserComments = function(userID, options, callback) {
205
+ if(typeof userID === 'string') {
206
+ userID = new SteamID(userID);
207
+ }
208
+
209
+ if (typeof options === 'function') {
210
+ callback = options;
211
+ options = {};
212
+ }
213
+
214
+ var form = Object.assign({
215
+ "start": 0,
216
+ "count": 0,
217
+ "feature2": -1,
218
+ "sessionid": this.getSessionID()
219
+ }, options);
220
+
221
+ this.httpRequestPost({
222
+ "uri": "https://steamcommunity.com/comment/Profile/render/" + userID.toString() + "/-1",
223
+ "form": form,
224
+ "json": true
225
+ }, function(err, response, body) {
226
+ if(!callback) {
227
+ return;
228
+ }
229
+
230
+ if (err) {
231
+ callback(err);
232
+ return;
233
+ }
234
+
235
+ if(body.success) {
236
+ const $ = Cheerio.load(body.comments_html);
237
+ const comments = $(".commentthread_comment.responsive_body_text[id]").map((i, elem) => {
238
+ var $elem = $(elem),
239
+ $commentContent = $elem.find(".commentthread_comment_text");
240
+ return {
241
+ id: $elem.attr("id").split("_")[1],
242
+ author: {
243
+ steamID: new SteamID("[U:1:" + $elem.find("[data-miniprofile]").data("miniprofile") + "]"),
244
+ name: $elem.find("bdi").text(),
245
+ avatar: $elem.find(".playerAvatar img[src]").attr("src"),
246
+ state: $elem.find(".playerAvatar").attr("class").split(" ").pop()
247
+ },
248
+ date: new Date($elem.find(".commentthread_comment_timestamp").data("timestamp") * 1000),
249
+ text: $commentContent.text().trim(),
250
+ html: $commentContent.html().trim()
251
+ }
252
+ }).get();
253
+
254
+ callback(null, comments, body.total_count);
255
+ } else if(body.error) {
256
+ callback(new Error(body.error));
257
+ } else {
258
+ callback(new Error("Unknown error"));
259
+ }
260
+ }, "steamcommunity");
261
+ };
262
+
263
+ SteamCommunity.prototype.inviteUserToGroup = function(userID, groupID, callback) {
264
+ if(typeof userID === 'string') {
265
+ userID = new SteamID(userID);
266
+ }
267
+
268
+ var self = this;
269
+ this.httpRequestPost({
270
+ "uri": "https://steamcommunity.com/actions/GroupInvite",
271
+ "form": {
272
+ "group": groupID.toString(),
273
+ "invitee": userID.toString(),
274
+ "json": 1,
275
+ "sessionID": this.getSessionID(),
276
+ "type": "groupInvite"
277
+ },
278
+ "json": true
279
+ }, function(err, response, body) {
280
+ if(!callback) {
281
+ return;
282
+ }
283
+
284
+ if (err) {
285
+ callback(err);
286
+ return;
287
+ }
288
+
289
+ if(body.results == 'OK') {
290
+ callback(null);
291
+ } else if(body.results) {
292
+ callback(new Error(body.results));
293
+ } else {
294
+ callback(new Error("Unknown error"));
295
+ }
296
+ }, "steamcommunity");
297
+ };
298
+
299
+ SteamCommunity.prototype.getUserAliases = function(userID, callback) {
300
+ if (typeof userID === 'string') {
301
+ userID = new SteamID(userID);
302
+ }
303
+
304
+ this.httpRequestGet({
305
+ "uri": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/ajaxaliases",
306
+ "json": true
307
+ }, function(err, response, body) {
308
+ if (err) {
309
+ callback(err);
310
+ return;
311
+ }
312
+
313
+ if (typeof body !== 'object') {
314
+ callback(new Error("Malformed response"));
315
+ return;
316
+ }
317
+
318
+ callback(null, body.map(function(entry) {
319
+ entry.timechanged = Helpers.decodeSteamTime(entry.timechanged);
320
+ return entry;
321
+ }));
322
+ }, "steamcommunity");
323
+ };
324
+
325
+ /**
326
+ * Get the background URL of user's profile.
327
+ * @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
328
+ * @param {function} callback
329
+ */
330
+ SteamCommunity.prototype.getUserProfileBackground = function(userID, callback) {
331
+ if (typeof userID === 'string') {
332
+ userID = new SteamID(userID);
333
+ }
334
+
335
+ this.httpRequest("https://steamcommunity.com/profiles/" + userID.getSteamID64(), (err, response, body) => {
336
+ if (err) {
337
+ callback(err);
338
+ return;
339
+ }
340
+
341
+ var $ = Cheerio.load(body);
342
+
343
+ var $privateProfileInfo = $('.profile_private_info');
344
+ if ($privateProfileInfo.length > 0) {
345
+ callback(new Error($privateProfileInfo.text().trim()));
346
+ return;
347
+ }
348
+
349
+ if ($('body').hasClass('has_profile_background')) {
350
+ var backgroundUrl = $('div.profile_background_image_content').css('background-image');
351
+ var matcher = backgroundUrl.match(/\(([^)]+)\)/);
352
+
353
+ if (matcher.length != 2 || !matcher[1].length) {
354
+ callback(new Error("Malformed response"));
355
+ } else {
356
+ callback(null, matcher[1]);
357
+ }
358
+ } else {
359
+ callback(null, null);
360
+ }
361
+ }, "steamcommunity");
362
+ };
363
+
364
+ SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) {
365
+ if (typeof userID === 'string') {
366
+ userID = new SteamID(userID);
367
+ }
368
+
369
+ if (typeof userID === 'function') {
370
+ callback = userID;
371
+ userID = this.steamID;
372
+ }
373
+
374
+ if (!userID) {
375
+ callback(new Error("No SteamID specified and not logged in"));
376
+ return;
377
+ }
378
+
379
+ var self = this;
380
+ this.httpRequest("https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory/", function(err, response, body) {
381
+ if (err) {
382
+ callback(err);
383
+ return;
384
+ }
385
+
386
+ var match = body.match(/var g_rgAppContextData = ([^\n]+);\r?\n/);
387
+ if (!match) {
388
+ var errorMessage = "Malformed response";
389
+
390
+ if(body.match(/0 items in their inventory\./)){
391
+ callback(null, {});
392
+ return;
393
+ }else if(body.match(/inventory is currently private\./)){
394
+ errorMessage = "Private inventory";
395
+ }else if(body.match(/profile\_private\_info/)){
396
+ errorMessage = "Private profile";
397
+ }
398
+
399
+ callback(new Error(errorMessage));
400
+ return;
401
+ }
402
+
403
+ var data;
404
+ try {
405
+ data = JSON.parse(match[1]);
406
+ } catch(e) {
407
+ callback(new Error("Malformed response"));
408
+ return;
409
+ }
410
+
411
+ callback(null, data);
412
+ }, "steamcommunity");
413
+ };
414
+
415
+ /**
416
+ * Get the contents of a user's inventory context.
417
+ * @deprecated Use getUserInventoryContents instead
418
+ * @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
419
+ * @param {int} appID - The Steam application ID of the game for which you want an inventory
420
+ * @param {int} contextID - The ID of the "context" within the game you want to retrieve
421
+ * @param {boolean} tradableOnly - true to get only tradable items and currencies
422
+ * @param {function} callback
423
+ */
424
+ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, tradableOnly, callback) {
425
+ var self = this;
426
+
427
+ if (typeof userID === 'string') {
428
+ userID = new SteamID(userID);
429
+ }
430
+
431
+ var endpoint = "/profiles/" + userID.getSteamID64();
432
+ get([], []);
433
+
434
+ function get(inventory, currency, start) {
435
+ self.httpRequest({
436
+ "uri": "https://steamcommunity.com" + endpoint + "/inventory/json/" + appID + "/" + contextID,
437
+ "headers": {
438
+ "Referer": "https://steamcommunity.com" + endpoint + "/inventory"
439
+ },
440
+ "qs": {
441
+ "start": start,
442
+ "trading": tradableOnly ? 1 : undefined
443
+ },
444
+ "json": true
445
+ }, function(err, response, body) {
446
+ if (err) {
447
+ callback(err);
448
+ return;
449
+ }
450
+
451
+ if (!body || !body.success || !body.rgInventory || !body.rgDescriptions || !body.rgCurrency) {
452
+ if (body) {
453
+ callback(new Error(body.Error || "Malformed response"));
454
+ } else {
455
+ callback(new Error("Malformed response"));
456
+ }
457
+
458
+ return;
459
+ }
460
+
461
+ var i;
462
+ for (i in body.rgInventory) {
463
+ if (!body.rgInventory.hasOwnProperty(i)) {
464
+ continue;
465
+ }
466
+
467
+ inventory.push(new CEconItem(body.rgInventory[i], body.rgDescriptions, contextID));
468
+ }
469
+
470
+ for (i in body.rgCurrency) {
471
+ if (!body.rgCurrency.hasOwnProperty(i)) {
472
+ continue;
473
+ }
474
+
475
+ currency.push(new CEconItem(body.rgInventory[i], body.rgDescriptions, contextID));
476
+ }
477
+
478
+ if (body.more) {
479
+ var match = response.request.uri.href.match(/\/(profiles|id)\/([^\/]+)\//);
480
+ if(match) {
481
+ endpoint = "/" + match[1] + "/" + match[2];
482
+ }
483
+
484
+ get(inventory, currency, body.more_start);
485
+ } else {
486
+ callback(null, inventory, currency);
487
+ }
488
+ }, "steamcommunity");
489
+ }
490
+ };
491
+
492
+ /**
493
+ * Get the contents of a user's inventory context.
494
+ * @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
495
+ * @param {int} appID - The Steam application ID of the game for which you want an inventory
496
+ * @param {int} contextID - The ID of the "context" within the game you want to retrieve
497
+ * @param {boolean} tradableOnly - true to get only tradable items and currencies
498
+ * @param {string} [language] - The language of item descriptions to return. Omit for default (which may either be English or your account's chosen language)
499
+ * @param {function} callback
500
+ */
501
+ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, contextID, tradableOnly, language, callback) {
502
+ if (typeof language === 'function') {
503
+ callback = language;
504
+ language = "english";
505
+ }
506
+
507
+ if (!userID) {
508
+ callback(new Error("The user's SteamID is invalid or missing."));
509
+ return;
510
+ }
511
+
512
+ var self = this;
513
+
514
+ if (typeof userID === 'string') {
515
+ userID = new SteamID(userID);
516
+ }
517
+
518
+ var pos = 1;
519
+ get([], []);
520
+
521
+ function get(inventory, currency, start) {
522
+ self.httpRequest({
523
+ "uri": "https://steamcommunity.com/inventory/" + userID.getSteamID64() + "/" + appID + "/" + contextID,
524
+ "headers": {
525
+ "Referer": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory"
526
+ },
527
+ "qs": {
528
+ "l": language, // Default language
529
+ "count": 5000, // Max items per 'page'
530
+ "start_assetid": start
531
+ },
532
+ "json": true
533
+ }, function(err, response, body) {
534
+ if (err) {
535
+ if (err.message == "HTTP error 403" && body === null) {
536
+ // 403 with a body of "null" means the inventory/profile is private.
537
+ if (self.steamID && userID.getSteamID64() == self.steamID.getSteamID64()) {
538
+ // We can never get private profile error for our own inventory!
539
+ self._notifySessionExpired(err);
540
+ }
541
+
542
+ callback(new Error("This profile is private."));
543
+ return;
544
+ }
545
+
546
+ if (err.message == "HTTP error 500" && body && body.error) {
547
+ err = new Error(body.error);
548
+
549
+ var match = body.error.match(/^(.+) \((\d+)\)$/);
550
+ if (match) {
551
+ err.message = match[1];
552
+ err.eresult = match[2];
553
+ callback(err);
554
+ return;
555
+ }
556
+ }
557
+
558
+ callback(err);
559
+ return;
560
+ }
561
+
562
+ if (body && body.success && body.total_inventory_count === 0) {
563
+ // Empty inventory
564
+ callback(null, [], [], 0);
565
+ return;
566
+ }
567
+
568
+ if (!body || !body.success || !body.assets || !body.descriptions) {
569
+ if (body) {
570
+ // Dunno if the error/Error property even exists on this new endpoint
571
+ callback(new Error(body.error || body.Error || "Malformed response"));
572
+ } else {
573
+ callback(new Error("Malformed response"));
574
+ }
575
+
576
+ return;
577
+ }
578
+
579
+ for (var i = 0; i < body.assets.length; i++) {
580
+ var description = getDescription(body.descriptions, body.assets[i].classid, body.assets[i].instanceid);
581
+
582
+ if (!tradableOnly || (description && description.tradable)) {
583
+ body.assets[i].pos = pos++;
584
+ (body.assets[i].currencyid ? currency : inventory).push(new CEconItem(body.assets[i], description, contextID));
585
+ }
586
+ }
587
+
588
+ if (body.more_items) {
589
+ get(inventory, currency, body.last_assetid);
590
+ } else {
591
+ callback(null, inventory, currency, body.total_inventory_count);
592
+ }
593
+ }, "steamcommunity");
594
+ }
595
+
596
+ // A bit of optimization; objects are hash tables so it's more efficient to look up by key than to iterate an array
597
+ var quickDescriptionLookup = {};
598
+
599
+ function getDescription(descriptions, classID, instanceID) {
600
+ var key = classID + '_' + (instanceID || '0'); // instanceID can be undefined, in which case it's 0.
601
+
602
+ if (quickDescriptionLookup[key]) {
603
+ return quickDescriptionLookup[key];
604
+ }
605
+
606
+ for (var i = 0; i < descriptions.length; i++) {
607
+ quickDescriptionLookup[descriptions[i].classid + '_' + (descriptions[i].instanceid || '0')] = descriptions[i];
608
+ }
609
+
610
+ return quickDescriptionLookup[key];
611
+ }
612
+ };
613
+
614
+ /**
615
+ * Upload an image to Steam and send it to another user over Steam chat.
616
+ * @param {SteamID|string} userID - Either a SteamID object or a string that can parse into one
617
+ * @param {Buffer} imageContentsBuffer - The image contents, as a Buffer
618
+ * @param {{spoiler?: boolean}} [options]
619
+ * @param {function} callback
620
+ */
621
+ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer, options, callback) {
622
+ if (typeof options == 'function') {
623
+ callback = options;
624
+ options = {};
625
+ }
626
+
627
+ options = options || {};
628
+
629
+ if (!userID) {
630
+ callback(new Error('The user\'s SteamID is invalid or missing'));
631
+ return;
632
+ }
633
+
634
+ if (typeof userID == 'string') {
635
+ userID = new SteamID(userID);
636
+ }
637
+
638
+ if (!Buffer.isBuffer(imageContentsBuffer)) {
639
+ callback(new Error('The image contents must be a Buffer containing an image'));
640
+ return;
641
+ }
642
+
643
+ var imageDetails = null;
644
+ try {
645
+ imageDetails = imageSize(imageContentsBuffer);
646
+ } catch (ex) {
647
+ callback(ex);
648
+ return;
649
+ }
650
+
651
+ var imageHash = Crypto.createHash('sha1');
652
+ imageHash.update(imageContentsBuffer);
653
+ imageHash = imageHash.digest('hex');
654
+
655
+ var filename = Date.now() + '_image.' + imageDetails.type;
656
+
657
+ this.httpRequestPost({
658
+ uri: 'https://steamcommunity.com/chat/beginfileupload/?l=english',
659
+ headers: {
660
+ referer: 'https://steamcommunity.com/chat/'
661
+ },
662
+ formData: { // it's multipart
663
+ sessionid: this.getSessionID(),
664
+ l: 'english',
665
+ file_size: imageContentsBuffer.length,
666
+ file_name: filename,
667
+ file_sha: imageHash,
668
+ file_image_width: imageDetails.width,
669
+ file_image_height: imageDetails.height,
670
+ file_type: 'image/' + (imageDetails.type == 'jpg' ? 'jpeg' : imageDetails.type)
671
+ },
672
+ json: true
673
+ }, (err, res, body) => {
674
+ if (err) {
675
+ if (body && body.success) {
676
+ var err2 = Helpers.eresultError(body.success);
677
+ if (body.message) {
678
+ err2.message = body.message;
679
+ }
680
+ callback(err2);
681
+ } else {
682
+ callback(err);
683
+ }
684
+ return;
685
+ }
686
+
687
+ if (body.success != 1) {
688
+ callback(Helpers.eresultError(body.success));
689
+ return;
690
+ }
691
+
692
+ var hmac = body.hmac;
693
+ var timestamp = body.timestamp;
694
+ var startResult = body.result;
695
+
696
+ if (!startResult || !startResult.ugcid || !startResult.url_host || !startResult.request_headers) {
697
+ callback(new Error('Malformed response'));
698
+ return;
699
+ }
700
+
701
+ // Okay, now we need to PUT the file to the provided URL
702
+ var uploadUrl = (startResult.use_https ? 'https' : 'http') + '://' + startResult.url_host + startResult.url_path;
703
+ var headers = {};
704
+ startResult.request_headers.forEach((header) => {
705
+ headers[header.name.toLowerCase()] = header.value;
706
+ });
707
+
708
+ this.httpRequest({
709
+ uri: uploadUrl,
710
+ method: 'PUT',
711
+ headers,
712
+ body: imageContentsBuffer
713
+ }, (err, res, body) => {
714
+ if (err) {
715
+ callback(err);
716
+ return;
717
+ }
718
+
719
+ // Now we need to commit the upload
720
+ this.httpRequestPost({
721
+ uri: 'https://steamcommunity.com/chat/commitfileupload/',
722
+ headers: {
723
+ referer: 'https://steamcommunity.com/chat/'
724
+ },
725
+ formData: { // it's multipart again
726
+ sessionid: this.getSessionID(),
727
+ l: 'english',
728
+ file_name: filename,
729
+ file_sha: imageHash,
730
+ success: '1',
731
+ ugcid: startResult.ugcid,
732
+ file_type: 'image/' + (imageDetails.type == 'jpg' ? 'jpeg' : imageDetails.type),
733
+ file_image_width: imageDetails.width,
734
+ file_image_height: imageDetails.height,
735
+ timestamp,
736
+ hmac,
737
+ friend_steamid: userID.getSteamID64(),
738
+ spoiler: options.spoiler ? '1' : '0'
739
+ },
740
+ json: true
741
+ }, (err, res, body) => {
742
+ if (err) {
743
+ callback(err);
744
+ return;
745
+ }
746
+
747
+ if (body.success != 1) {
748
+ callback(Helpers.eresultError(body.success));
749
+ return;
750
+ }
751
+
752
+ if (body.result.success != 1) {
753
+ // lol valve
754
+ callback(Helpers.eresultError(body.result.success));
755
+ return;
756
+ }
757
+
758
+ if (!body.result.details || !body.result.details.url) {
759
+ callback(new Error('Malformed response'));
760
+ return;
761
+ }
762
+
763
+ callback(null, body.result.details.url);
764
+ }, 'steamcommunity');
765
+ }, 'steamcommunity');
766
+ }, 'steamcommunity');
767
+ };