steamcommunity 3.47.1 → 3.48.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,221 +1,221 @@
1
- const Cheerio = require('cheerio');
2
- const SteamID = require('steamid');
3
-
4
- const SteamCommunity = require('../index.js');
5
- const Helpers = require('../components/helpers.js');
6
-
7
- const ESharedFileType = require('../resources/ESharedFileType.js');
8
-
9
-
10
- /**
11
- * Scrape a sharedfile's DOM to get all available information
12
- * @param {string} sharedFileId - ID of the sharedfile
13
- * @param {function} callback - First argument is null/Error, second is object containing all available information
14
- */
15
- SteamCommunity.prototype.getSteamSharedFile = function(sharedFileId, callback) {
16
- // Construct object holding all the data we can scrape
17
- let sharedfile = {
18
- id: sharedFileId,
19
- type: null,
20
- appID: null,
21
- owner: null,
22
- fileSize: null,
23
- postDate: null,
24
- resolution: null,
25
- uniqueVisitorsCount: null,
26
- favoritesCount: null,
27
- upvoteCount: null,
28
- guideNumRatings: null,
29
- isUpvoted: null,
30
- isDownvoted: null
31
- };
32
-
33
- // Get DOM of sharedfile
34
- this.httpRequestGet(`https://steamcommunity.com/sharedfiles/filedetails/?id=${sharedFileId}`, (err, res, body) => {
35
- try {
36
-
37
- /* --------------------- Preprocess output --------------------- */
38
-
39
- // Load output into cheerio to make parsing easier
40
- let $ = Cheerio.load(body);
41
-
42
- // Dynamically map detailsStatsContainerLeft to detailsStatsContainerRight in an object to make readout easier. It holds size, post date and resolution.
43
- let detailsStatsObj = {};
44
- let detailsLeft = $(".detailsStatsContainerLeft").children();
45
- let detailsRight = $(".detailsStatsContainerRight").children();
46
-
47
- Object.keys(detailsLeft).forEach((e) => { // Dynamically get all details. Don't hardcore so that this also works for guides.
48
- if (isNaN(e)) {
49
- return; // Ignore invalid entries
50
- }
51
-
52
- detailsStatsObj[detailsLeft[e].children[0].data.trim()] = detailsRight[e].children[0].data;
53
- });
54
-
55
- // Dynamically map stats_table descriptions to values. This holds Unique Visitors and Current Favorites
56
- let statsTableObj = {};
57
- let statsTable = $(".stats_table").children();
58
-
59
- Object.keys(statsTable).forEach((e) => {
60
- if (isNaN(e)) {
61
- return; // Ignore invalid entries
62
- }
63
-
64
- // Value description is at index 3, value data at index 1
65
- statsTableObj[statsTable[e].children[3].children[0].data] = statsTable[e].children[1].children[0].data.replace(/,/g, ""); // Remove commas from 1k+ values
66
- });
67
-
68
-
69
- /* --------------------- Find and map values --------------------- */
70
-
71
- // Find appID in share button onclick event
72
- sharedfile.appID = Number($("#ShareItemBtn").attr()["onclick"].replace(`ShowSharePublishedFilePopup( '${sharedFileId}', '`, "").replace("' );", ""));
73
-
74
-
75
- // Find fileSize if not guide
76
- sharedfile.fileSize = detailsStatsObj["File Size"] || null; // TODO: Convert to bytes? It seems like to always be MB but no guarantee
77
-
78
-
79
- // Find postDate and convert to timestamp
80
- let posted = detailsStatsObj["Posted"].trim();
81
-
82
- sharedfile.postDate = Helpers.decodeSteamTime(posted);
83
-
84
-
85
- // Find resolution if artwork or screenshot
86
- sharedfile.resolution = detailsStatsObj["Size"] || null;
87
-
88
-
89
- // Find uniqueVisitorsCount. We can't use ' || null' here as Number("0") casts to false
90
- if (statsTableObj["Unique Visitors"]) {
91
- sharedfile.uniqueVisitorsCount = Number(statsTableObj["Unique Visitors"]);
92
- }
93
-
94
-
95
- // Find favoritesCount. We can't use ' || null' here as Number("0") casts to false
96
- if (statsTableObj["Current Favorites"]) {
97
- sharedfile.favoritesCount = Number(statsTableObj["Current Favorites"]);
98
- }
99
-
100
-
101
- // Find upvoteCount. We can't use ' || null' here as Number("0") casts to false
102
- let upvoteCount = $("#VotesUpCountContainer > #VotesUpCount").text();
103
-
104
- if (upvoteCount) {
105
- sharedfile.upvoteCount = Number(upvoteCount);
106
- }
107
-
108
-
109
- // Find numRatings if this is a guide as they use a different voting system
110
- let numRatings = $(".ratingSection > .numRatings").text().replace(" ratings", "");
111
-
112
- sharedfile.guideNumRatings = Number(numRatings) || null; // Set to null if not a guide or if the guide does not have enough ratings to show a value
113
-
114
-
115
- // Determine if this account has already voted on this sharedfile
116
- sharedfile.isUpvoted = String($(".workshopItemControlCtn > #VoteUpBtn")[0].attribs["class"]).includes("toggled"); // Check if upvote btn class contains "toggled"
117
- sharedfile.isDownvoted = String($(".workshopItemControlCtn > #VoteDownBtn")[0].attribs["class"]).includes("toggled"); // Check if downvote btn class contains "toggled"
118
-
119
-
120
- // Determine type by looking at the second breadcrumb. Find the first separator as it has a unique name and go to the next element which holds our value of interest
121
- let breadcrumb = $(".breadcrumbs > .breadcrumb_separator").next().get(0).children[0].data || "";
122
-
123
- if (breadcrumb.includes("Screenshot")) {
124
- sharedfile.type = ESharedFileType.Screenshot;
125
- }
126
-
127
- if (breadcrumb.includes("Artwork")) {
128
- sharedfile.type = ESharedFileType.Artwork;
129
- }
130
-
131
- if (breadcrumb.includes("Guide")) {
132
- sharedfile.type = ESharedFileType.Guide;
133
- }
134
-
135
-
136
- // Find owner profile link, convert to steamID64 using SteamIdResolver lib and create a SteamID object
137
- let ownerHref = $(".friendBlockLinkOverlay").attr()["href"];
138
-
139
- Helpers.resolveVanityURL(ownerHref, (err, data) => { // This request takes <1 sec
140
- if (err) {
141
- callback(err);
142
- return;
143
- }
144
-
145
- sharedfile.owner = new SteamID(data.steamID);
146
-
147
- // Make callback when ID was resolved as otherwise owner will always be null
148
- callback(null, new CSteamSharedFile(this, sharedfile));
149
- });
150
-
151
- } catch (err) {
152
- callback(err, null);
153
- }
154
- }, "steamcommunity");
155
- };
156
-
157
- /**
158
- * Constructor - Creates a new SharedFile object
159
- * @class
160
- * @param {SteamCommunity} community
161
- * @param {{ id: string, type: ESharedFileType, appID: number, owner: SteamID|null, fileSize: string|null, postDate: number, resolution: string|null, uniqueVisitorsCount: number, favoritesCount: number, upvoteCount: number|null, guideNumRatings: Number|null, isUpvoted: boolean, isDownvoted: boolean }} data
162
- */
163
- function CSteamSharedFile(community, data) {
164
- /**
165
- * @type {SteamCommunity}
166
- */
167
- this._community = community;
168
-
169
- // Clone all the data we received
170
- Object.assign(this, data);
171
- }
172
-
173
- /**
174
- * Deletes a comment from this sharedfile's comment section
175
- * @param {String} cid - ID of the comment to delete
176
- * @param {function} callback - Takes only an Error object/null as the first argument
177
- */
178
- CSteamSharedFile.prototype.deleteComment = function(cid, callback) {
179
- this._community.deleteSharedFileComment(this.owner, this.id, cid, callback);
180
- };
181
-
182
- /**
183
- * Favorites this sharedfile
184
- * @param {function} callback - Takes only an Error object/null as the first argument
185
- */
186
- CSteamSharedFile.prototype.favorite = function(callback) {
187
- this._community.favoriteSharedFile(this.id, this.appID, callback);
188
- };
189
-
190
- /**
191
- * Posts a comment to this sharedfile
192
- * @param {String} message - Content of the comment to post
193
- * @param {function} callback - Takes only an Error object/null as the first argument
194
- */
195
- CSteamSharedFile.prototype.comment = function(message, callback) {
196
- this._community.postSharedFileComment(this.owner, this.id, message, callback);
197
- };
198
-
199
- /**
200
- * Subscribes to this sharedfile's comment section. Note: Checkbox on webpage does not update
201
- * @param {function} callback - Takes only an Error object/null as the first argument
202
- */
203
- CSteamSharedFile.prototype.subscribe = function(callback) {
204
- this._community.subscribeSharedFileComments(this.owner, this.id, callback);
205
- };
206
-
207
- /**
208
- * Unfavorites this sharedfile
209
- * @param {function} callback - Takes only an Error object/null as the first argument
210
- */
211
- CSteamSharedFile.prototype.unfavorite = function(callback) {
212
- this._community.unfavoriteSharedFile(this.id, this.appID, callback);
213
- };
214
-
215
- /**
216
- * Unsubscribes from this sharedfile's comment section. Note: Checkbox on webpage does not update
217
- * @param {function} callback - Takes only an Error object/null as the first argument
218
- */
219
- CSteamSharedFile.prototype.unsubscribe = function(callback) {
220
- this._community.unsubscribeSharedFileComments(this.owner, this.id, callback);
221
- };
1
+ const Cheerio = require('cheerio');
2
+ const SteamID = require('steamid');
3
+
4
+ const SteamCommunity = require('../index.js');
5
+ const Helpers = require('../components/helpers.js');
6
+
7
+ const ESharedFileType = require('../resources/ESharedFileType.js');
8
+
9
+
10
+ /**
11
+ * Scrape a sharedfile's DOM to get all available information
12
+ * @param {string} sharedFileId - ID of the sharedfile
13
+ * @param {function} callback - First argument is null/Error, second is object containing all available information
14
+ */
15
+ SteamCommunity.prototype.getSteamSharedFile = function(sharedFileId, callback) {
16
+ // Construct object holding all the data we can scrape
17
+ let sharedfile = {
18
+ id: sharedFileId,
19
+ type: null,
20
+ appID: null,
21
+ owner: null,
22
+ fileSize: null,
23
+ postDate: null,
24
+ resolution: null,
25
+ uniqueVisitorsCount: null,
26
+ favoritesCount: null,
27
+ upvoteCount: null,
28
+ guideNumRatings: null,
29
+ isUpvoted: null,
30
+ isDownvoted: null
31
+ };
32
+
33
+ // Get DOM of sharedfile
34
+ this.httpRequestGet(`https://steamcommunity.com/sharedfiles/filedetails/?id=${sharedFileId}`, (err, res, body) => {
35
+ try {
36
+
37
+ /* --------------------- Preprocess output --------------------- */
38
+
39
+ // Load output into cheerio to make parsing easier
40
+ let $ = Cheerio.load(body);
41
+
42
+ // Dynamically map detailsStatsContainerLeft to detailsStatsContainerRight in an object to make readout easier. It holds size, post date and resolution.
43
+ let detailsStatsObj = {};
44
+ let detailsLeft = $(".detailsStatsContainerLeft").children();
45
+ let detailsRight = $(".detailsStatsContainerRight").children();
46
+
47
+ Object.keys(detailsLeft).forEach((e) => { // Dynamically get all details. Don't hardcore so that this also works for guides.
48
+ if (isNaN(e)) {
49
+ return; // Ignore invalid entries
50
+ }
51
+
52
+ detailsStatsObj[detailsLeft[e].children[0].data.trim()] = detailsRight[e].children[0].data;
53
+ });
54
+
55
+ // Dynamically map stats_table descriptions to values. This holds Unique Visitors and Current Favorites
56
+ let statsTableObj = {};
57
+ let statsTable = $(".stats_table").children();
58
+
59
+ Object.keys(statsTable).forEach((e) => {
60
+ if (isNaN(e)) {
61
+ return; // Ignore invalid entries
62
+ }
63
+
64
+ // Value description is at index 3, value data at index 1
65
+ statsTableObj[statsTable[e].children[3].children[0].data] = statsTable[e].children[1].children[0].data.replace(/,/g, ""); // Remove commas from 1k+ values
66
+ });
67
+
68
+
69
+ /* --------------------- Find and map values --------------------- */
70
+
71
+ // Find appID in share button onclick event
72
+ sharedfile.appID = Number($("#ShareItemBtn").attr()["onclick"].replace(`ShowSharePublishedFilePopup( '${sharedFileId}', '`, "").replace("' );", ""));
73
+
74
+
75
+ // Find fileSize if not guide
76
+ sharedfile.fileSize = detailsStatsObj["File Size"] || null; // TODO: Convert to bytes? It seems like to always be MB but no guarantee
77
+
78
+
79
+ // Find postDate and convert to timestamp
80
+ let posted = detailsStatsObj["Posted"].trim();
81
+
82
+ sharedfile.postDate = Helpers.decodeSteamTime(posted);
83
+
84
+
85
+ // Find resolution if artwork or screenshot
86
+ sharedfile.resolution = detailsStatsObj["Size"] || null;
87
+
88
+
89
+ // Find uniqueVisitorsCount. We can't use ' || null' here as Number("0") casts to false
90
+ if (statsTableObj["Unique Visitors"]) {
91
+ sharedfile.uniqueVisitorsCount = Number(statsTableObj["Unique Visitors"]);
92
+ }
93
+
94
+
95
+ // Find favoritesCount. We can't use ' || null' here as Number("0") casts to false
96
+ if (statsTableObj["Current Favorites"]) {
97
+ sharedfile.favoritesCount = Number(statsTableObj["Current Favorites"]);
98
+ }
99
+
100
+
101
+ // Find upvoteCount. We can't use ' || null' here as Number("0") casts to false
102
+ let upvoteCount = $("#VotesUpCountContainer > #VotesUpCount").text();
103
+
104
+ if (upvoteCount) {
105
+ sharedfile.upvoteCount = Number(upvoteCount);
106
+ }
107
+
108
+
109
+ // Find numRatings if this is a guide as they use a different voting system
110
+ let numRatings = $(".ratingSection > .numRatings").text().replace(" ratings", "");
111
+
112
+ sharedfile.guideNumRatings = Number(numRatings) || null; // Set to null if not a guide or if the guide does not have enough ratings to show a value
113
+
114
+
115
+ // Determine if this account has already voted on this sharedfile
116
+ sharedfile.isUpvoted = String($(".workshopItemControlCtn > #VoteUpBtn")[0].attribs["class"]).includes("toggled"); // Check if upvote btn class contains "toggled"
117
+ sharedfile.isDownvoted = String($(".workshopItemControlCtn > #VoteDownBtn")[0].attribs["class"]).includes("toggled"); // Check if downvote btn class contains "toggled"
118
+
119
+
120
+ // Determine type by looking at the second breadcrumb. Find the first separator as it has a unique name and go to the next element which holds our value of interest
121
+ let breadcrumb = $(".breadcrumbs > .breadcrumb_separator").next().get(0).children[0].data || "";
122
+
123
+ if (breadcrumb.includes("Screenshot")) {
124
+ sharedfile.type = ESharedFileType.Screenshot;
125
+ }
126
+
127
+ if (breadcrumb.includes("Artwork")) {
128
+ sharedfile.type = ESharedFileType.Artwork;
129
+ }
130
+
131
+ if (breadcrumb.includes("Guide")) {
132
+ sharedfile.type = ESharedFileType.Guide;
133
+ }
134
+
135
+
136
+ // Find owner profile link, convert to steamID64 using SteamIdResolver lib and create a SteamID object
137
+ let ownerHref = $(".friendBlockLinkOverlay").attr()["href"];
138
+
139
+ Helpers.resolveVanityURL(ownerHref, (err, data) => { // This request takes <1 sec
140
+ if (err) {
141
+ callback(err);
142
+ return;
143
+ }
144
+
145
+ sharedfile.owner = new SteamID(data.steamID);
146
+
147
+ // Make callback when ID was resolved as otherwise owner will always be null
148
+ callback(null, new CSteamSharedFile(this, sharedfile));
149
+ });
150
+
151
+ } catch (err) {
152
+ callback(err, null);
153
+ }
154
+ }, "steamcommunity");
155
+ };
156
+
157
+ /**
158
+ * Constructor - Creates a new SharedFile object
159
+ * @class
160
+ * @param {SteamCommunity} community
161
+ * @param {{ id: string, type: ESharedFileType, appID: number, owner: SteamID|null, fileSize: string|null, postDate: number, resolution: string|null, uniqueVisitorsCount: number, favoritesCount: number, upvoteCount: number|null, guideNumRatings: Number|null, isUpvoted: boolean, isDownvoted: boolean }} data
162
+ */
163
+ function CSteamSharedFile(community, data) {
164
+ /**
165
+ * @type {SteamCommunity}
166
+ */
167
+ this._community = community;
168
+
169
+ // Clone all the data we received
170
+ Object.assign(this, data);
171
+ }
172
+
173
+ /**
174
+ * Deletes a comment from this sharedfile's comment section
175
+ * @param {String} cid - ID of the comment to delete
176
+ * @param {function} callback - Takes only an Error object/null as the first argument
177
+ */
178
+ CSteamSharedFile.prototype.deleteComment = function(cid, callback) {
179
+ this._community.deleteSharedFileComment(this.owner, this.id, cid, callback);
180
+ };
181
+
182
+ /**
183
+ * Favorites this sharedfile
184
+ * @param {function} callback - Takes only an Error object/null as the first argument
185
+ */
186
+ CSteamSharedFile.prototype.favorite = function(callback) {
187
+ this._community.favoriteSharedFile(this.id, this.appID, callback);
188
+ };
189
+
190
+ /**
191
+ * Posts a comment to this sharedfile
192
+ * @param {String} message - Content of the comment to post
193
+ * @param {function} callback - Takes only an Error object/null as the first argument
194
+ */
195
+ CSteamSharedFile.prototype.comment = function(message, callback) {
196
+ this._community.postSharedFileComment(this.owner, this.id, message, callback);
197
+ };
198
+
199
+ /**
200
+ * Subscribes to this sharedfile's comment section. Note: Checkbox on webpage does not update
201
+ * @param {function} callback - Takes only an Error object/null as the first argument
202
+ */
203
+ CSteamSharedFile.prototype.subscribe = function(callback) {
204
+ this._community.subscribeSharedFileComments(this.owner, this.id, callback);
205
+ };
206
+
207
+ /**
208
+ * Unfavorites this sharedfile
209
+ * @param {function} callback - Takes only an Error object/null as the first argument
210
+ */
211
+ CSteamSharedFile.prototype.unfavorite = function(callback) {
212
+ this._community.unfavoriteSharedFile(this.id, this.appID, callback);
213
+ };
214
+
215
+ /**
216
+ * Unsubscribes from this sharedfile's comment section. Note: Checkbox on webpage does not update
217
+ * @param {function} callback - Takes only an Error object/null as the first argument
218
+ */
219
+ CSteamSharedFile.prototype.unsubscribe = function(callback) {
220
+ this._community.unsubscribeSharedFileComments(this.owner, this.id, callback);
221
+ };