steamcommunity 3.48.3 → 3.48.4

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,831 +1,831 @@
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.followUser = function(userID, callback) {
300
- if(typeof userID === 'string') {
301
- userID = new SteamID(userID);
302
- }
303
-
304
- this.httpRequestPost({
305
- "uri": `https://steamcommunity.com/profiles/${userID.toString()}/followuser/`,
306
- "form": {
307
- "sessionid": this.getSessionID(),
308
- },
309
- "json": true
310
- }, function(err, response, body) {
311
- if (!callback) {
312
- return;
313
- }
314
-
315
- if (err) {
316
- callback(err);
317
- return;
318
- }
319
-
320
- if (body.success && body.success != SteamCommunity.EResult.OK) {
321
- callback(Helpers.eresultError(body.success));
322
- return;
323
- }
324
-
325
- callback(null);
326
- }, "steamcommunity");
327
- };
328
-
329
- SteamCommunity.prototype.unfollowUser = function(userID, callback) {
330
- if(typeof userID === 'string') {
331
- userID = new SteamID(userID);
332
- }
333
-
334
- this.httpRequestPost({
335
- "uri": `https://steamcommunity.com/profiles/${userID.toString()}/unfollowuser/`,
336
- "form": {
337
- "sessionid": this.getSessionID(),
338
- },
339
- "json": true
340
- }, function(err, response, body) {
341
- if (!callback) {
342
- return;
343
- }
344
-
345
- if (err) {
346
- callback(err);
347
- return;
348
- }
349
-
350
- if (body.success && body.success != SteamCommunity.EResult.OK) {
351
- callback(Helpers.eresultError(body.success));
352
- return;
353
- }
354
-
355
- callback(null);
356
- }, "steamcommunity");
357
- };
358
-
359
- SteamCommunity.prototype.getUserAliases = function(userID, callback) {
360
- if (typeof userID === 'string') {
361
- userID = new SteamID(userID);
362
- }
363
-
364
- this.httpRequestGet({
365
- "uri": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/ajaxaliases",
366
- "json": true
367
- }, function(err, response, body) {
368
- if (err) {
369
- callback(err);
370
- return;
371
- }
372
-
373
- if (typeof body !== 'object') {
374
- callback(new Error("Malformed response"));
375
- return;
376
- }
377
-
378
- callback(null, body.map(function(entry) {
379
- entry.timechanged = Helpers.decodeSteamTime(entry.timechanged);
380
- return entry;
381
- }));
382
- }, "steamcommunity");
383
- };
384
-
385
- /**
386
- * Get the background URL of user's profile.
387
- * @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
388
- * @param {function} callback
389
- */
390
- SteamCommunity.prototype.getUserProfileBackground = function(userID, callback) {
391
- if (typeof userID === 'string') {
392
- userID = new SteamID(userID);
393
- }
394
-
395
- this.httpRequest("https://steamcommunity.com/profiles/" + userID.getSteamID64(), (err, response, body) => {
396
- if (err) {
397
- callback(err);
398
- return;
399
- }
400
-
401
- var $ = Cheerio.load(body);
402
-
403
- var $privateProfileInfo = $('.profile_private_info');
404
- if ($privateProfileInfo.length > 0) {
405
- callback(new Error($privateProfileInfo.text().trim()));
406
- return;
407
- }
408
-
409
- if ($('body').hasClass('has_profile_background')) {
410
- var backgroundUrl = $('div.profile_background_image_content').css('background-image');
411
- var matcher = backgroundUrl.match(/\(([^)]+)\)/);
412
-
413
- if (matcher.length != 2 || !matcher[1].length) {
414
- callback(new Error("Malformed response"));
415
- } else {
416
- callback(null, matcher[1]);
417
- }
418
- } else {
419
- callback(null, null);
420
- }
421
- }, "steamcommunity");
422
- };
423
-
424
- SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) {
425
- if (typeof userID === 'string') {
426
- userID = new SteamID(userID);
427
- }
428
-
429
- if (typeof userID === 'function') {
430
- callback = userID;
431
- userID = this.steamID;
432
- }
433
-
434
- if (!userID) {
435
- callback(new Error("No SteamID specified and not logged in"));
436
- return;
437
- }
438
-
439
- var self = this;
440
- this.httpRequest("https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory/", function(err, response, body) {
441
- if (err) {
442
- callback(err);
443
- return;
444
- }
445
-
446
- var match = body.match(/var g_rgAppContextData = ([^\n]+);\r?\n/);
447
- if (!match) {
448
- callback(new Error('Malformed response'));
449
- return;
450
- }
451
-
452
- var data;
453
- try {
454
- data = JSON.parse(match[1]);
455
- } catch(e) {
456
- callback(new Error("Malformed response"));
457
- return;
458
- }
459
-
460
- if (Object.keys(data).length == 0) {
461
- if (body.match(/inventory is currently private\./)) {
462
- callback(new Error('Private inventory'));
463
- return;
464
- }
465
-
466
- if (body.match(/profile_private_info/)) {
467
- callback(new Error('Private profile'));
468
- return;
469
- }
470
-
471
- // If they truly have no items in their inventory, Steam will send g_rgAppContextData as [] instead of {}.
472
- data = {};
473
- }
474
-
475
- callback(null, data);
476
- }, "steamcommunity");
477
- };
478
-
479
- /**
480
- * Get the contents of a user's inventory context.
481
- * @deprecated Use getUserInventoryContents instead
482
- * @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
483
- * @param {int} appID - The Steam application ID of the game for which you want an inventory
484
- * @param {int} contextID - The ID of the "context" within the game you want to retrieve
485
- * @param {boolean} tradableOnly - true to get only tradable items and currencies
486
- * @param {function} callback
487
- */
488
- SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, tradableOnly, callback) {
489
- var self = this;
490
-
491
- if (typeof userID === 'string') {
492
- userID = new SteamID(userID);
493
- }
494
-
495
- var endpoint = "/profiles/" + userID.getSteamID64();
496
- get([], []);
497
-
498
- function get(inventory, currency, start) {
499
- self.httpRequest({
500
- "uri": "https://steamcommunity.com" + endpoint + "/inventory/json/" + appID + "/" + contextID,
501
- "headers": {
502
- "Referer": "https://steamcommunity.com" + endpoint + "/inventory"
503
- },
504
- "qs": {
505
- "start": start,
506
- "trading": tradableOnly ? 1 : undefined
507
- },
508
- "json": true
509
- }, function(err, response, body) {
510
- if (err) {
511
- callback(err);
512
- return;
513
- }
514
-
515
- if (!body || !body.success || !body.rgInventory || !body.rgDescriptions || !body.rgCurrency) {
516
- if (body) {
517
- callback(new Error(body.Error || "Malformed response"));
518
- } else {
519
- callback(new Error("Malformed response"));
520
- }
521
-
522
- return;
523
- }
524
-
525
- var i;
526
- for (i in body.rgInventory) {
527
- if (!body.rgInventory.hasOwnProperty(i)) {
528
- continue;
529
- }
530
-
531
- inventory.push(new CEconItem(body.rgInventory[i], body.rgDescriptions, contextID));
532
- }
533
-
534
- for (i in body.rgCurrency) {
535
- if (!body.rgCurrency.hasOwnProperty(i)) {
536
- continue;
537
- }
538
-
539
- currency.push(new CEconItem(body.rgCurrency[i], body.rgDescriptions, contextID));
540
- }
541
-
542
- if (body.more) {
543
- var match = response.request.uri.href.match(/\/(profiles|id)\/([^\/]+)\//);
544
- if(match) {
545
- endpoint = "/" + match[1] + "/" + match[2];
546
- }
547
-
548
- get(inventory, currency, body.more_start);
549
- } else {
550
- callback(null, inventory, currency);
551
- }
552
- }, "steamcommunity");
553
- }
554
- };
555
-
556
- /**
557
- * Get the contents of a user's inventory context.
558
- * @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
559
- * @param {int} appID - The Steam application ID of the game for which you want an inventory
560
- * @param {int} contextID - The ID of the "context" within the game you want to retrieve
561
- * @param {boolean} tradableOnly - true to get only tradable items and currencies
562
- * @param {string} [language] - The language of item descriptions to return. Omit for default (which may either be English or your account's chosen language)
563
- * @param {function} callback
564
- */
565
- SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, contextID, tradableOnly, language, callback) {
566
- if (typeof language === 'function') {
567
- callback = language;
568
- language = "english";
569
- }
570
-
571
- if (!userID) {
572
- callback(new Error("The user's SteamID is invalid or missing."));
573
- return;
574
- }
575
-
576
- var self = this;
577
-
578
- if (typeof userID === 'string') {
579
- userID = new SteamID(userID);
580
- }
581
-
582
- var pos = 1;
583
- get([], []);
584
-
585
- function get(inventory, currency, start) {
586
- self.httpRequest({
587
- "uri": "https://steamcommunity.com/inventory/" + userID.getSteamID64() + "/" + appID + "/" + contextID,
588
- "headers": {
589
- "Referer": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory"
590
- },
591
- "qs": {
592
- "l": language, // Default language
593
- "count": 5000, // Max items per 'page'
594
- "start_assetid": start
595
- },
596
- "json": true
597
- }, function(err, response, body) {
598
- if (err) {
599
- if (err.message == "HTTP error 403" && body === null) {
600
- // 403 with a body of "null" means the inventory/profile is private.
601
- if (self.steamID && userID.getSteamID64() == self.steamID.getSteamID64()) {
602
- // We can never get private profile error for our own inventory!
603
- self._notifySessionExpired(err);
604
- }
605
-
606
- callback(new Error("This profile is private."));
607
- return;
608
- }
609
-
610
- if (err.message == "HTTP error 500" && body && body.error) {
611
- err = new Error(body.error);
612
-
613
- var match = body.error.match(/^(.+) \((\d+)\)$/);
614
- if (match) {
615
- err.message = match[1];
616
- err.eresult = match[2];
617
- callback(err);
618
- return;
619
- }
620
- }
621
-
622
- callback(err);
623
- return;
624
- }
625
-
626
- if (body && body.success && body.total_inventory_count === 0) {
627
- // Empty inventory
628
- callback(null, [], [], 0);
629
- return;
630
- }
631
-
632
- if (!body || !body.success || !body.assets || !body.descriptions) {
633
- if (body) {
634
- // Dunno if the error/Error property even exists on this new endpoint
635
- callback(new Error(body.error || body.Error || "Malformed response"));
636
- } else {
637
- callback(new Error("Malformed response"));
638
- }
639
-
640
- return;
641
- }
642
-
643
- for (var i = 0; i < body.assets.length; i++) {
644
- var description = getDescription(body.descriptions, body.assets[i].classid, body.assets[i].instanceid);
645
-
646
- if (!tradableOnly || (description && description.tradable)) {
647
- body.assets[i].pos = pos++;
648
- (body.assets[i].currencyid ? currency : inventory).push(new CEconItem(body.assets[i], description, contextID));
649
- }
650
- }
651
-
652
- if (body.more_items) {
653
- get(inventory, currency, body.last_assetid);
654
- } else {
655
- callback(null, inventory, currency, body.total_inventory_count);
656
- }
657
- }, "steamcommunity");
658
- }
659
-
660
- // A bit of optimization; objects are hash tables so it's more efficient to look up by key than to iterate an array
661
- var quickDescriptionLookup = {};
662
-
663
- function getDescription(descriptions, classID, instanceID) {
664
- var key = classID + '_' + (instanceID || '0'); // instanceID can be undefined, in which case it's 0.
665
-
666
- if (quickDescriptionLookup[key]) {
667
- return quickDescriptionLookup[key];
668
- }
669
-
670
- for (var i = 0; i < descriptions.length; i++) {
671
- quickDescriptionLookup[descriptions[i].classid + '_' + (descriptions[i].instanceid || '0')] = descriptions[i];
672
- }
673
-
674
- return quickDescriptionLookup[key];
675
- }
676
- };
677
-
678
- /**
679
- * Upload an image to Steam and send it to another user over Steam chat.
680
- * @param {SteamID|string} userID - Either a SteamID object or a string that can parse into one
681
- * @param {Buffer} imageContentsBuffer - The image contents, as a Buffer
682
- * @param {{spoiler?: boolean}} [options]
683
- * @param {function} callback
684
- */
685
- SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer, options, callback) {
686
- if (typeof options == 'function') {
687
- callback = options;
688
- options = {};
689
- }
690
-
691
- options = options || {};
692
-
693
- if (!userID) {
694
- callback(new Error('The user\'s SteamID is invalid or missing'));
695
- return;
696
- }
697
-
698
- if (typeof userID == 'string') {
699
- userID = new SteamID(userID);
700
- }
701
-
702
- if (!Buffer.isBuffer(imageContentsBuffer)) {
703
- callback(new Error('The image contents must be a Buffer containing an image'));
704
- return;
705
- }
706
-
707
- var imageDetails = null;
708
- try {
709
- imageDetails = imageSize(imageContentsBuffer);
710
- } catch (ex) {
711
- callback(ex);
712
- return;
713
- }
714
-
715
- var imageHash = Crypto.createHash('sha1');
716
- imageHash.update(imageContentsBuffer);
717
- imageHash = imageHash.digest('hex');
718
-
719
- var filename = Date.now() + '_image.' + imageDetails.type;
720
-
721
- this.httpRequestPost({
722
- uri: 'https://steamcommunity.com/chat/beginfileupload/?l=english',
723
- headers: {
724
- referer: 'https://steamcommunity.com/chat/'
725
- },
726
- formData: { // it's multipart
727
- sessionid: this.getSessionID(),
728
- l: 'english',
729
- file_size: imageContentsBuffer.length,
730
- file_name: filename,
731
- file_sha: imageHash,
732
- file_image_width: imageDetails.width,
733
- file_image_height: imageDetails.height,
734
- file_type: 'image/' + (imageDetails.type == 'jpg' ? 'jpeg' : imageDetails.type)
735
- },
736
- json: true
737
- }, (err, res, body) => {
738
- if (err) {
739
- if (body && body.success) {
740
- var err2 = Helpers.eresultError(body.success);
741
- if (body.message) {
742
- err2.message = body.message;
743
- }
744
- callback(err2);
745
- } else {
746
- callback(err);
747
- }
748
- return;
749
- }
750
-
751
- if (body.success != 1) {
752
- callback(Helpers.eresultError(body.success));
753
- return;
754
- }
755
-
756
- var hmac = body.hmac;
757
- var timestamp = body.timestamp;
758
- var startResult = body.result;
759
-
760
- if (!startResult || !startResult.ugcid || !startResult.url_host || !startResult.request_headers) {
761
- callback(new Error('Malformed response'));
762
- return;
763
- }
764
-
765
- // Okay, now we need to PUT the file to the provided URL
766
- var uploadUrl = (startResult.use_https ? 'https' : 'http') + '://' + startResult.url_host + startResult.url_path;
767
- var headers = {};
768
- startResult.request_headers.forEach((header) => {
769
- headers[header.name.toLowerCase()] = header.value;
770
- });
771
-
772
- this.httpRequest({
773
- uri: uploadUrl,
774
- method: 'PUT',
775
- headers,
776
- body: imageContentsBuffer
777
- }, (err, res, body) => {
778
- if (err) {
779
- callback(err);
780
- return;
781
- }
782
-
783
- // Now we need to commit the upload
784
- this.httpRequestPost({
785
- uri: 'https://steamcommunity.com/chat/commitfileupload/',
786
- headers: {
787
- referer: 'https://steamcommunity.com/chat/'
788
- },
789
- formData: { // it's multipart again
790
- sessionid: this.getSessionID(),
791
- l: 'english',
792
- file_name: filename,
793
- file_sha: imageHash,
794
- success: '1',
795
- ugcid: startResult.ugcid,
796
- file_type: 'image/' + (imageDetails.type == 'jpg' ? 'jpeg' : imageDetails.type),
797
- file_image_width: imageDetails.width,
798
- file_image_height: imageDetails.height,
799
- timestamp,
800
- hmac,
801
- friend_steamid: userID.getSteamID64(),
802
- spoiler: options.spoiler ? '1' : '0'
803
- },
804
- json: true
805
- }, (err, res, body) => {
806
- if (err) {
807
- callback(err);
808
- return;
809
- }
810
-
811
- if (body.success != 1) {
812
- callback(Helpers.eresultError(body.success));
813
- return;
814
- }
815
-
816
- if (body.result.success != 1) {
817
- // lol valve
818
- callback(Helpers.eresultError(body.result.success));
819
- return;
820
- }
821
-
822
- if (!body.result.details || !body.result.details.url) {
823
- callback(new Error('Malformed response'));
824
- return;
825
- }
826
-
827
- callback(null, body.result.details.url);
828
- }, 'steamcommunity');
829
- }, 'steamcommunity');
830
- }, 'steamcommunity');
831
- };
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.followUser = function(userID, callback) {
300
+ if(typeof userID === 'string') {
301
+ userID = new SteamID(userID);
302
+ }
303
+
304
+ this.httpRequestPost({
305
+ "uri": `https://steamcommunity.com/profiles/${userID.toString()}/followuser/`,
306
+ "form": {
307
+ "sessionid": this.getSessionID(),
308
+ },
309
+ "json": true
310
+ }, function(err, response, body) {
311
+ if (!callback) {
312
+ return;
313
+ }
314
+
315
+ if (err) {
316
+ callback(err);
317
+ return;
318
+ }
319
+
320
+ if (body.success && body.success != SteamCommunity.EResult.OK) {
321
+ callback(Helpers.eresultError(body.success));
322
+ return;
323
+ }
324
+
325
+ callback(null);
326
+ }, "steamcommunity");
327
+ };
328
+
329
+ SteamCommunity.prototype.unfollowUser = function(userID, callback) {
330
+ if(typeof userID === 'string') {
331
+ userID = new SteamID(userID);
332
+ }
333
+
334
+ this.httpRequestPost({
335
+ "uri": `https://steamcommunity.com/profiles/${userID.toString()}/unfollowuser/`,
336
+ "form": {
337
+ "sessionid": this.getSessionID(),
338
+ },
339
+ "json": true
340
+ }, function(err, response, body) {
341
+ if (!callback) {
342
+ return;
343
+ }
344
+
345
+ if (err) {
346
+ callback(err);
347
+ return;
348
+ }
349
+
350
+ if (body.success && body.success != SteamCommunity.EResult.OK) {
351
+ callback(Helpers.eresultError(body.success));
352
+ return;
353
+ }
354
+
355
+ callback(null);
356
+ }, "steamcommunity");
357
+ };
358
+
359
+ SteamCommunity.prototype.getUserAliases = function(userID, callback) {
360
+ if (typeof userID === 'string') {
361
+ userID = new SteamID(userID);
362
+ }
363
+
364
+ this.httpRequestGet({
365
+ "uri": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/ajaxaliases",
366
+ "json": true
367
+ }, function(err, response, body) {
368
+ if (err) {
369
+ callback(err);
370
+ return;
371
+ }
372
+
373
+ if (typeof body !== 'object') {
374
+ callback(new Error("Malformed response"));
375
+ return;
376
+ }
377
+
378
+ callback(null, body.map(function(entry) {
379
+ entry.timechanged = Helpers.decodeSteamTime(entry.timechanged);
380
+ return entry;
381
+ }));
382
+ }, "steamcommunity");
383
+ };
384
+
385
+ /**
386
+ * Get the background URL of user's profile.
387
+ * @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
388
+ * @param {function} callback
389
+ */
390
+ SteamCommunity.prototype.getUserProfileBackground = function(userID, callback) {
391
+ if (typeof userID === 'string') {
392
+ userID = new SteamID(userID);
393
+ }
394
+
395
+ this.httpRequest("https://steamcommunity.com/profiles/" + userID.getSteamID64(), (err, response, body) => {
396
+ if (err) {
397
+ callback(err);
398
+ return;
399
+ }
400
+
401
+ var $ = Cheerio.load(body);
402
+
403
+ var $privateProfileInfo = $('.profile_private_info');
404
+ if ($privateProfileInfo.length > 0) {
405
+ callback(new Error($privateProfileInfo.text().trim()));
406
+ return;
407
+ }
408
+
409
+ if ($('body').hasClass('has_profile_background')) {
410
+ var backgroundUrl = $('div.profile_background_image_content').css('background-image');
411
+ var matcher = backgroundUrl.match(/\(([^)]+)\)/);
412
+
413
+ if (matcher.length != 2 || !matcher[1].length) {
414
+ callback(new Error("Malformed response"));
415
+ } else {
416
+ callback(null, matcher[1]);
417
+ }
418
+ } else {
419
+ callback(null, null);
420
+ }
421
+ }, "steamcommunity");
422
+ };
423
+
424
+ SteamCommunity.prototype.getUserInventoryContexts = function(userID, callback) {
425
+ if (typeof userID === 'string') {
426
+ userID = new SteamID(userID);
427
+ }
428
+
429
+ if (typeof userID === 'function') {
430
+ callback = userID;
431
+ userID = this.steamID;
432
+ }
433
+
434
+ if (!userID) {
435
+ callback(new Error("No SteamID specified and not logged in"));
436
+ return;
437
+ }
438
+
439
+ var self = this;
440
+ this.httpRequest("https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory/", function(err, response, body) {
441
+ if (err) {
442
+ callback(err);
443
+ return;
444
+ }
445
+
446
+ var match = body.match(/var g_rgAppContextData = ([^\n]+);\r?\n/);
447
+ if (!match) {
448
+ callback(new Error('Malformed response'));
449
+ return;
450
+ }
451
+
452
+ var data;
453
+ try {
454
+ data = JSON.parse(match[1]);
455
+ } catch(e) {
456
+ callback(new Error("Malformed response"));
457
+ return;
458
+ }
459
+
460
+ if (Object.keys(data).length == 0) {
461
+ if (body.match(/inventory is currently private\./)) {
462
+ callback(new Error('Private inventory'));
463
+ return;
464
+ }
465
+
466
+ if (body.match(/profile_private_info/)) {
467
+ callback(new Error('Private profile'));
468
+ return;
469
+ }
470
+
471
+ // If they truly have no items in their inventory, Steam will send g_rgAppContextData as [] instead of {}.
472
+ data = {};
473
+ }
474
+
475
+ callback(null, data);
476
+ }, "steamcommunity");
477
+ };
478
+
479
+ /**
480
+ * Get the contents of a user's inventory context.
481
+ * @deprecated Use getUserInventoryContents instead
482
+ * @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
483
+ * @param {int} appID - The Steam application ID of the game for which you want an inventory
484
+ * @param {int} contextID - The ID of the "context" within the game you want to retrieve
485
+ * @param {boolean} tradableOnly - true to get only tradable items and currencies
486
+ * @param {function} callback
487
+ */
488
+ SteamCommunity.prototype.getUserInventory = function(userID, appID, contextID, tradableOnly, callback) {
489
+ var self = this;
490
+
491
+ if (typeof userID === 'string') {
492
+ userID = new SteamID(userID);
493
+ }
494
+
495
+ var endpoint = "/profiles/" + userID.getSteamID64();
496
+ get([], []);
497
+
498
+ function get(inventory, currency, start) {
499
+ self.httpRequest({
500
+ "uri": "https://steamcommunity.com" + endpoint + "/inventory/json/" + appID + "/" + contextID,
501
+ "headers": {
502
+ "Referer": "https://steamcommunity.com" + endpoint + "/inventory"
503
+ },
504
+ "qs": {
505
+ "start": start,
506
+ "trading": tradableOnly ? 1 : undefined
507
+ },
508
+ "json": true
509
+ }, function(err, response, body) {
510
+ if (err) {
511
+ callback(err);
512
+ return;
513
+ }
514
+
515
+ if (!body || !body.success || !body.rgInventory || !body.rgDescriptions || !body.rgCurrency) {
516
+ if (body) {
517
+ callback(new Error(body.Error || "Malformed response"));
518
+ } else {
519
+ callback(new Error("Malformed response"));
520
+ }
521
+
522
+ return;
523
+ }
524
+
525
+ var i;
526
+ for (i in body.rgInventory) {
527
+ if (!body.rgInventory.hasOwnProperty(i)) {
528
+ continue;
529
+ }
530
+
531
+ inventory.push(new CEconItem(body.rgInventory[i], body.rgDescriptions, contextID));
532
+ }
533
+
534
+ for (i in body.rgCurrency) {
535
+ if (!body.rgCurrency.hasOwnProperty(i)) {
536
+ continue;
537
+ }
538
+
539
+ currency.push(new CEconItem(body.rgCurrency[i], body.rgDescriptions, contextID));
540
+ }
541
+
542
+ if (body.more) {
543
+ var match = response.request.uri.href.match(/\/(profiles|id)\/([^\/]+)\//);
544
+ if(match) {
545
+ endpoint = "/" + match[1] + "/" + match[2];
546
+ }
547
+
548
+ get(inventory, currency, body.more_start);
549
+ } else {
550
+ callback(null, inventory, currency);
551
+ }
552
+ }, "steamcommunity");
553
+ }
554
+ };
555
+
556
+ /**
557
+ * Get the contents of a user's inventory context.
558
+ * @param {SteamID|string} userID - The user's SteamID as a SteamID object or a string which can parse into one
559
+ * @param {int} appID - The Steam application ID of the game for which you want an inventory
560
+ * @param {int} contextID - The ID of the "context" within the game you want to retrieve
561
+ * @param {boolean} tradableOnly - true to get only tradable items and currencies
562
+ * @param {string} [language] - The language of item descriptions to return. Omit for default (which may either be English or your account's chosen language)
563
+ * @param {function} callback
564
+ */
565
+ SteamCommunity.prototype.getUserInventoryContents = function(userID, appID, contextID, tradableOnly, language, callback) {
566
+ if (typeof language === 'function') {
567
+ callback = language;
568
+ language = "english";
569
+ }
570
+
571
+ if (!userID) {
572
+ callback(new Error("The user's SteamID is invalid or missing."));
573
+ return;
574
+ }
575
+
576
+ var self = this;
577
+
578
+ if (typeof userID === 'string') {
579
+ userID = new SteamID(userID);
580
+ }
581
+
582
+ var pos = 1;
583
+ get([], []);
584
+
585
+ function get(inventory, currency, start) {
586
+ self.httpRequest({
587
+ "uri": "https://steamcommunity.com/inventory/" + userID.getSteamID64() + "/" + appID + "/" + contextID,
588
+ "headers": {
589
+ "Referer": "https://steamcommunity.com/profiles/" + userID.getSteamID64() + "/inventory"
590
+ },
591
+ "qs": {
592
+ "l": language, // Default language
593
+ "count": 5000, // Max items per 'page'
594
+ "start_assetid": start
595
+ },
596
+ "json": true
597
+ }, function(err, response, body) {
598
+ if (err) {
599
+ if (err.message == "HTTP error 403" && body === null) {
600
+ // 403 with a body of "null" means the inventory/profile is private.
601
+ if (self.steamID && userID.getSteamID64() == self.steamID.getSteamID64()) {
602
+ // We can never get private profile error for our own inventory!
603
+ self._notifySessionExpired(err);
604
+ }
605
+
606
+ callback(new Error("This profile is private."));
607
+ return;
608
+ }
609
+
610
+ if (err.message == "HTTP error 500" && body && body.error) {
611
+ err = new Error(body.error);
612
+
613
+ var match = body.error.match(/^(.+) \((\d+)\)$/);
614
+ if (match) {
615
+ err.message = match[1];
616
+ err.eresult = match[2];
617
+ callback(err);
618
+ return;
619
+ }
620
+ }
621
+
622
+ callback(err);
623
+ return;
624
+ }
625
+
626
+ if (body && body.success && body.total_inventory_count === 0) {
627
+ // Empty inventory
628
+ callback(null, [], [], 0);
629
+ return;
630
+ }
631
+
632
+ if (!body || !body.success || !body.assets || !body.descriptions) {
633
+ if (body) {
634
+ // Dunno if the error/Error property even exists on this new endpoint
635
+ callback(new Error(body.error || body.Error || "Malformed response"));
636
+ } else {
637
+ callback(new Error("Malformed response"));
638
+ }
639
+
640
+ return;
641
+ }
642
+
643
+ for (var i = 0; i < body.assets.length; i++) {
644
+ var description = getDescription(body.descriptions, body.assets[i].classid, body.assets[i].instanceid);
645
+
646
+ if (!tradableOnly || (description && description.tradable)) {
647
+ body.assets[i].pos = pos++;
648
+ (body.assets[i].currencyid ? currency : inventory).push(new CEconItem(body.assets[i], description, contextID));
649
+ }
650
+ }
651
+
652
+ if (body.more_items) {
653
+ get(inventory, currency, body.last_assetid);
654
+ } else {
655
+ callback(null, inventory, currency, body.total_inventory_count);
656
+ }
657
+ }, "steamcommunity");
658
+ }
659
+
660
+ // A bit of optimization; objects are hash tables so it's more efficient to look up by key than to iterate an array
661
+ var quickDescriptionLookup = {};
662
+
663
+ function getDescription(descriptions, classID, instanceID) {
664
+ var key = classID + '_' + (instanceID || '0'); // instanceID can be undefined, in which case it's 0.
665
+
666
+ if (quickDescriptionLookup[key]) {
667
+ return quickDescriptionLookup[key];
668
+ }
669
+
670
+ for (var i = 0; i < descriptions.length; i++) {
671
+ quickDescriptionLookup[descriptions[i].classid + '_' + (descriptions[i].instanceid || '0')] = descriptions[i];
672
+ }
673
+
674
+ return quickDescriptionLookup[key];
675
+ }
676
+ };
677
+
678
+ /**
679
+ * Upload an image to Steam and send it to another user over Steam chat.
680
+ * @param {SteamID|string} userID - Either a SteamID object or a string that can parse into one
681
+ * @param {Buffer} imageContentsBuffer - The image contents, as a Buffer
682
+ * @param {{spoiler?: boolean}} [options]
683
+ * @param {function} callback
684
+ */
685
+ SteamCommunity.prototype.sendImageToUser = function(userID, imageContentsBuffer, options, callback) {
686
+ if (typeof options == 'function') {
687
+ callback = options;
688
+ options = {};
689
+ }
690
+
691
+ options = options || {};
692
+
693
+ if (!userID) {
694
+ callback(new Error('The user\'s SteamID is invalid or missing'));
695
+ return;
696
+ }
697
+
698
+ if (typeof userID == 'string') {
699
+ userID = new SteamID(userID);
700
+ }
701
+
702
+ if (!Buffer.isBuffer(imageContentsBuffer)) {
703
+ callback(new Error('The image contents must be a Buffer containing an image'));
704
+ return;
705
+ }
706
+
707
+ var imageDetails = null;
708
+ try {
709
+ imageDetails = imageSize(imageContentsBuffer);
710
+ } catch (ex) {
711
+ callback(ex);
712
+ return;
713
+ }
714
+
715
+ var imageHash = Crypto.createHash('sha1');
716
+ imageHash.update(imageContentsBuffer);
717
+ imageHash = imageHash.digest('hex');
718
+
719
+ var filename = Date.now() + '_image.' + imageDetails.type;
720
+
721
+ this.httpRequestPost({
722
+ uri: 'https://steamcommunity.com/chat/beginfileupload/?l=english',
723
+ headers: {
724
+ referer: 'https://steamcommunity.com/chat/'
725
+ },
726
+ formData: { // it's multipart
727
+ sessionid: this.getSessionID(),
728
+ l: 'english',
729
+ file_size: imageContentsBuffer.length,
730
+ file_name: filename,
731
+ file_sha: imageHash,
732
+ file_image_width: imageDetails.width,
733
+ file_image_height: imageDetails.height,
734
+ file_type: 'image/' + (imageDetails.type == 'jpg' ? 'jpeg' : imageDetails.type)
735
+ },
736
+ json: true
737
+ }, (err, res, body) => {
738
+ if (err) {
739
+ if (body && body.success) {
740
+ var err2 = Helpers.eresultError(body.success);
741
+ if (body.message) {
742
+ err2.message = body.message;
743
+ }
744
+ callback(err2);
745
+ } else {
746
+ callback(err);
747
+ }
748
+ return;
749
+ }
750
+
751
+ if (body.success != 1) {
752
+ callback(Helpers.eresultError(body.success));
753
+ return;
754
+ }
755
+
756
+ var hmac = body.hmac;
757
+ var timestamp = body.timestamp;
758
+ var startResult = body.result;
759
+
760
+ if (!startResult || !startResult.ugcid || !startResult.url_host || !startResult.request_headers) {
761
+ callback(new Error('Malformed response'));
762
+ return;
763
+ }
764
+
765
+ // Okay, now we need to PUT the file to the provided URL
766
+ var uploadUrl = (startResult.use_https ? 'https' : 'http') + '://' + startResult.url_host + startResult.url_path;
767
+ var headers = {};
768
+ startResult.request_headers.forEach((header) => {
769
+ headers[header.name.toLowerCase()] = header.value;
770
+ });
771
+
772
+ this.httpRequest({
773
+ uri: uploadUrl,
774
+ method: 'PUT',
775
+ headers,
776
+ body: imageContentsBuffer
777
+ }, (err, res, body) => {
778
+ if (err) {
779
+ callback(err);
780
+ return;
781
+ }
782
+
783
+ // Now we need to commit the upload
784
+ this.httpRequestPost({
785
+ uri: 'https://steamcommunity.com/chat/commitfileupload/',
786
+ headers: {
787
+ referer: 'https://steamcommunity.com/chat/'
788
+ },
789
+ formData: { // it's multipart again
790
+ sessionid: this.getSessionID(),
791
+ l: 'english',
792
+ file_name: filename,
793
+ file_sha: imageHash,
794
+ success: '1',
795
+ ugcid: startResult.ugcid,
796
+ file_type: 'image/' + (imageDetails.type == 'jpg' ? 'jpeg' : imageDetails.type),
797
+ file_image_width: imageDetails.width,
798
+ file_image_height: imageDetails.height,
799
+ timestamp,
800
+ hmac,
801
+ friend_steamid: userID.getSteamID64(),
802
+ spoiler: options.spoiler ? '1' : '0'
803
+ },
804
+ json: true
805
+ }, (err, res, body) => {
806
+ if (err) {
807
+ callback(err);
808
+ return;
809
+ }
810
+
811
+ if (body.success != 1) {
812
+ callback(Helpers.eresultError(body.success));
813
+ return;
814
+ }
815
+
816
+ if (body.result.success != 1) {
817
+ // lol valve
818
+ callback(Helpers.eresultError(body.result.success));
819
+ return;
820
+ }
821
+
822
+ if (!body.result.details || !body.result.details.url) {
823
+ callback(new Error('Malformed response'));
824
+ return;
825
+ }
826
+
827
+ callback(null, body.result.details.url);
828
+ }, 'steamcommunity');
829
+ }, 'steamcommunity');
830
+ }, 'steamcommunity');
831
+ };