steamutils 1.5.51 → 1.5.52
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.
- package/.idea/prettier.xml +0 -1
- package/SteamClient.js +3159 -3159
- package/_steamproto.js +56 -56
- package/const.js +582 -582
- package/full_steamproto.js +7 -2
- package/index.js +34 -40
- package/package.json +1 -1
- package/parse_html.js +189 -189
- package/remote.js +2503 -2503
- package/steamproto.js +57 -57
- package/utils.js +1300 -1300
- package/.idea/gbrowser_project.xml +0 -11
- package/.idea/git_toolbox_blame.xml +0 -6
- package/.idea/git_toolbox_prj.xml +0 -15
- package/.idea/inspectionProfiles/Project_Default.xml +0 -6
- package/.idea/jsLinters/eslint.xml +0 -6
- package/race.js +0 -2241
package/full_steamproto.js
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import path from "path";
|
2
2
|
import { fileURLToPath } from "url";
|
3
3
|
import Protobuf from "protobufjs";
|
4
|
+
import gpf from "google-proto-files";
|
4
5
|
|
5
6
|
const __filename = fileURLToPath(import.meta.url);
|
6
7
|
const __dirname = path.dirname(__filename);
|
@@ -11,10 +12,14 @@ export class SteamProto {
|
|
11
12
|
}
|
12
13
|
|
13
14
|
toProto() {
|
14
|
-
const
|
15
|
+
const localProto = path.join(`${__dirname}/protos/`, this._proto.filename);
|
16
|
+
|
17
|
+
const descriptorPath = gpf.getProtoPath("protobuf/descriptor.proto");
|
18
|
+
|
19
|
+
const root = new Protobuf.Root().loadSync([descriptorPath, localProto], {
|
15
20
|
keepCase: true,
|
16
21
|
});
|
17
|
-
return root
|
22
|
+
return root.lookupType(this._proto.name);
|
18
23
|
}
|
19
24
|
|
20
25
|
protoEncode(obj) {
|
package/index.js
CHANGED
@@ -7291,6 +7291,39 @@ export default class SteamUser {
|
|
7291
7291
|
};
|
7292
7292
|
}
|
7293
7293
|
|
7294
|
+
/**
|
7295
|
+
* @typedef {Object} FriendGameplayInfoEntry
|
7296
|
+
* @property {string} steamId - The SteamID64 of the friend.
|
7297
|
+
* @property {number} minutes_played - Number of minutes played recently or currently.
|
7298
|
+
* @property {number} minutes_played_forever - Total minutes this friend has played forever.
|
7299
|
+
*/
|
7300
|
+
|
7301
|
+
/**
|
7302
|
+
* @typedef {Object} YourInfo
|
7303
|
+
* @property {number} minutes_played - Number of minutes you have played recently or currently.
|
7304
|
+
* @property {number} minutes_played_forever - Total minutes you have played forever.
|
7305
|
+
* @property {boolean} in_wishlist - Whether the app is in your wishlist.
|
7306
|
+
* @property {boolean} owned - Whether you own the app.
|
7307
|
+
*/
|
7308
|
+
|
7309
|
+
/**
|
7310
|
+
* @typedef {Object} FriendsGameplayInfoResponse
|
7311
|
+
* @property {FriendGameplayInfoEntry[]} in_game - Friends currently playing the game.
|
7312
|
+
* @property {FriendGameplayInfoEntry[]} played_recently - Friends who played the game recently.
|
7313
|
+
* @property {FriendGameplayInfoEntry[]} played_ever - Friends who have ever played the game.
|
7314
|
+
* @property {FriendGameplayInfoEntry[]} owns - Friends who own the game.
|
7315
|
+
* @property {YourInfo} your_info - Information about your own playtime, wishlist and ownership for this app.
|
7316
|
+
*/
|
7317
|
+
|
7318
|
+
/**
|
7319
|
+
* Retrieves Steam friends' gameplay stats for a specific app.
|
7320
|
+
*
|
7321
|
+
* @param {string} accessToken - Steam user access token.
|
7322
|
+
* @param {number|string} appId - The App ID of the game to query.
|
7323
|
+
* @returns {Promise<FriendsGameplayInfoResponse|undefined>}
|
7324
|
+
* Resolves with gameplay info: lists of friends (by status) and your stats for the app,
|
7325
|
+
* or `undefined` if the result is empty or on unauthorized access.
|
7326
|
+
*/
|
7294
7327
|
async getFriendsGameplayInfo(accessToken, appId) {
|
7295
7328
|
if (!appId) {
|
7296
7329
|
return;
|
@@ -7311,7 +7344,7 @@ export default class SteamUser {
|
|
7311
7344
|
responseType: "arraybuffer",
|
7312
7345
|
});
|
7313
7346
|
if (result instanceof ResponseError) {
|
7314
|
-
return
|
7347
|
+
return;
|
7315
7348
|
}
|
7316
7349
|
|
7317
7350
|
if (!result || result.status === 401) {
|
@@ -7339,45 +7372,6 @@ export default class SteamUser {
|
|
7339
7372
|
owns: formatList(owns),
|
7340
7373
|
your_info: _.omit(your_info, "steamid"),
|
7341
7374
|
};
|
7342
|
-
|
7343
|
-
const example = [
|
7344
|
-
{
|
7345
|
-
public_data: {
|
7346
|
-
visibility_state: 3,
|
7347
|
-
privacy_state: 0,
|
7348
|
-
profile_state: 1,
|
7349
|
-
ban_expires_time: 0,
|
7350
|
-
account_flags: 0,
|
7351
|
-
persona_name: "LOL haha",
|
7352
|
-
profile_url: "",
|
7353
|
-
content_country_restricted: false,
|
7354
|
-
steamId: "76561199243542939",
|
7355
|
-
avatarHash: "0000000000000000000000000000000000000000",
|
7356
|
-
},
|
7357
|
-
private_data: {
|
7358
|
-
persona_state: 0,
|
7359
|
-
persona_state_flags: 0,
|
7360
|
-
time_created: 1644675779,
|
7361
|
-
game_id: 0,
|
7362
|
-
game_server_ip_address: 0,
|
7363
|
-
game_server_port: 0,
|
7364
|
-
game_extra_info: "",
|
7365
|
-
account_name: "",
|
7366
|
-
lobby_steam_id: 0,
|
7367
|
-
rich_presence_kv: "",
|
7368
|
-
watching_broadcast_accountid: 0,
|
7369
|
-
watching_broadcast_appid: 0,
|
7370
|
-
watching_broadcast_viewers: 0,
|
7371
|
-
watching_broadcast_title: "",
|
7372
|
-
last_logoff_time: 0,
|
7373
|
-
last_seen_online: 0,
|
7374
|
-
game_os_type: 0,
|
7375
|
-
game_device_type: 0,
|
7376
|
-
game_device_name: "",
|
7377
|
-
game_is_private: false,
|
7378
|
-
},
|
7379
|
-
},
|
7380
|
-
];
|
7381
7375
|
}
|
7382
7376
|
|
7383
7377
|
/**
|
package/package.json
CHANGED
package/parse_html.js
CHANGED
@@ -1,189 +1,189 @@
|
|
1
|
-
import { StringUtils } from "alpha-common-utils/index.js";
|
2
|
-
import { formatMarketHistoryDate } from "./utils.js";
|
3
|
-
import * as cheerio from "cheerio";
|
4
|
-
|
5
|
-
/**
|
6
|
-
* @typedef {Object} HoverItem
|
7
|
-
* @property {string|null} listingId The listing ID extracted from the selector, if available (as string).
|
8
|
-
* @property {number} appid The app (game) ID for this item.
|
9
|
-
* @property {number} contextid The context ID for this item.
|
10
|
-
* @property {number} assetid The unique asset ID.
|
11
|
-
* @property {string} unknown A string field captured from the arguments (usually "0").
|
12
|
-
* @property {string} [name_selector] The HTML selector for the item's name, if present.
|
13
|
-
* @property {string} [image_selector] The HTML selector for the item's image, if present.
|
14
|
-
* @property {string} [other_selector] The selector string if it does not match name/image pattern.
|
15
|
-
*/
|
16
|
-
|
17
|
-
/**
|
18
|
-
* Extracts item information from Steam inventory trade history or similar HTML fragments.
|
19
|
-
* Scans the given HTML string for calls to `CreateItemHoverFromContainer` and extracts
|
20
|
-
* key data for each referenced item. Returns each item's info in a structured array.
|
21
|
-
*
|
22
|
-
* @param {string} html - The HTML string to parse.
|
23
|
-
* @returns {HoverItem[]} Array of hover item objects.
|
24
|
-
*/
|
25
|
-
export function extractAssetItemsFromHovers(html) {
|
26
|
-
// Use your preferred HTML/space scrubber.
|
27
|
-
html = StringUtils.cleanSpace(html);
|
28
|
-
|
29
|
-
// RegExp for CreateItemHoverFromContainer arguments
|
30
|
-
const re = /CreateItemHoverFromContainer\s*\(\s*g_rgAssets\s*,\s*'([^']+)'\s*,\s*(\d+)\s*,\s*'(\d+)'\s*,\s*'(\d+)'\s*,\s*(\d+)\s*\)/g;
|
31
|
-
|
32
|
-
/** @type {Record<number, HoverItem>} */
|
33
|
-
const itemsMap = {};
|
34
|
-
let match;
|
35
|
-
|
36
|
-
while ((match = re.exec(html)) !== null) {
|
37
|
-
const [_, selector, appid, contextid, assetid, unknown] = match;
|
38
|
-
|
39
|
-
// Extract listingId as a string if present: "history_row_<listingId>_"
|
40
|
-
let listingId = null;
|
41
|
-
const lidMatch = selector.match(/history_row_(\d+)_/);
|
42
|
-
if (lidMatch) listingId = lidMatch[1]; // assign as string
|
43
|
-
|
44
|
-
const appidNum = Number(appid);
|
45
|
-
const contextidNum = Number(contextid);
|
46
|
-
const assetidNum = Number(assetid);
|
47
|
-
|
48
|
-
if (!itemsMap[assetidNum]) {
|
49
|
-
itemsMap[assetidNum] = {
|
50
|
-
listingId,
|
51
|
-
appid: appidNum,
|
52
|
-
contextid: contextidNum,
|
53
|
-
assetid: assetidNum,
|
54
|
-
unknown,
|
55
|
-
};
|
56
|
-
}
|
57
|
-
|
58
|
-
if (selector.endsWith("_name")) {
|
59
|
-
itemsMap[assetidNum].name_selector = selector;
|
60
|
-
} else if (selector.endsWith("_image")) {
|
61
|
-
itemsMap[assetidNum].image_selector = selector;
|
62
|
-
} else {
|
63
|
-
itemsMap[assetidNum].other_selector = selector;
|
64
|
-
}
|
65
|
-
}
|
66
|
-
return Object.values(itemsMap);
|
67
|
-
}
|
68
|
-
|
69
|
-
/**
|
70
|
-
* @typedef {Object} MarketHistoryListing
|
71
|
-
* @property {string} id - HTML row id attribute (e.g. "history_row_4316182845737233773_4316182845737233774").
|
72
|
-
* @property {string} listingId - Listing id (e.g. "4316182845737233773").
|
73
|
-
* @property {number} price - Price in integer form (e.g. 80270).
|
74
|
-
* @property {string} itemName - The market item's name (e.g. "UMP-45 | Mudder").
|
75
|
-
* @property {string} gameName - The game's name (e.g. "Counter-Strike 2").
|
76
|
-
* @property {string} listedOn - Listing date as string (e.g. "27 May 2024").
|
77
|
-
* @property {string} actedOn - Acted on date or empty string.
|
78
|
-
* @property {string} image - Full image URL for the item.
|
79
|
-
* @property {string} gainOrLoss - "+" for buys, "-" for sells.
|
80
|
-
* @property {string} status - Status text, usually empty string.
|
81
|
-
* @property {string} [assetId] - (optional) Associated asset id, if available.
|
82
|
-
*/
|
83
|
-
|
84
|
-
/**
|
85
|
-
* Parses Steam market history listing rows into structured data objects.
|
86
|
-
*
|
87
|
-
* @param {string} html - The raw HTML string to parse.
|
88
|
-
* @param {Object} [assetByListingId={}] - Optional mapping of listingId to assetId.
|
89
|
-
* @returns {MarketHistoryListing[]} Array of market listing objects.
|
90
|
-
*/
|
91
|
-
export function parseMarketHistoryListings(html, assetByListingId = {}) {
|
92
|
-
const $ = cheerio.load(html);
|
93
|
-
return $(".market_listing_row")
|
94
|
-
.toArray()
|
95
|
-
.map((el) => {
|
96
|
-
const $el = $(el);
|
97
|
-
const id = $el.attr("id");
|
98
|
-
const listingId = id?.match(/history_row_(.*?)_/)?.[1];
|
99
|
-
if (!listingId) return null;
|
100
|
-
const [gainOrLoss, image, priceText, itemName, gameName, listedOnText, actedOnText, status] = [$el.find(".market_listing_gainorloss").text().trim(), $el.find(".market_listing_item_img").attr("src"), $el.find(".market_table_value .market_listing_price").text(), $el.find(".market_listing_item_name").text().trim(), $el.find(".market_listing_game_name").text().trim(), $el.find(".market_listing_listed_date + .market_listing_listed_date").text(), $el.find(".market_listing_whoactedwith + .market_listing_listed_date").text(), $el.find(".market_listing_whoactedwith").text().trim()];
|
101
|
-
const price = parseInt(priceText.replace(/[₫().,]/g, "").trim(), 10) || "";
|
102
|
-
const listedOn = typeof formatMarketHistoryDate === "function" ? formatMarketHistoryDate(listedOnText.replace(/Listed:/, "").trim()) : listedOnText.replace(/Listed:/, "").trim();
|
103
|
-
const actedOn = typeof formatMarketHistoryDate === "function" ? formatMarketHistoryDate(actedOnText.replace(/Listed:/, "").trim()) : actedOnText.replace(/Listed:/, "").trim();
|
104
|
-
|
105
|
-
const result = {
|
106
|
-
id,
|
107
|
-
listingId,
|
108
|
-
price,
|
109
|
-
itemName,
|
110
|
-
gameName,
|
111
|
-
listedOn,
|
112
|
-
actedOn,
|
113
|
-
image,
|
114
|
-
gainOrLoss,
|
115
|
-
status,
|
116
|
-
};
|
117
|
-
if (assetByListingId?.[listingId]) result.assetId = assetByListingId[listingId];
|
118
|
-
return result;
|
119
|
-
})
|
120
|
-
.filter(Boolean);
|
121
|
-
}
|
122
|
-
|
123
|
-
/**
|
124
|
-
* Parses Steam Market HTML listings to an array of items.
|
125
|
-
*
|
126
|
-
* @param {string} html - HTML string containing listing rows.
|
127
|
-
* @returns {Array<{
|
128
|
-
* listingId: string,
|
129
|
-
* appId: number,
|
130
|
-
* contextId: number,
|
131
|
-
* itemId: number,
|
132
|
-
* imageUrl: string|null,
|
133
|
-
* buyerPaysPrice: string,
|
134
|
-
* receivePrice: string,
|
135
|
-
* itemName: string,
|
136
|
-
* gameName: string,
|
137
|
-
* listedDate: string
|
138
|
-
* }>}
|
139
|
-
*/
|
140
|
-
export function parseMarketListings(html) {
|
141
|
-
const $ = cheerio.load(html);
|
142
|
-
const results = [];
|
143
|
-
|
144
|
-
$(".market_listing_row").each((index, element) => {
|
145
|
-
const $row = $(element);
|
146
|
-
|
147
|
-
const cancelLink = $row.find(".market_listing_cancel_button > a").attr("href");
|
148
|
-
if (!cancelLink) return;
|
149
|
-
|
150
|
-
const paramsMatch = cancelLink.match(/\(([^)]*)\)/);
|
151
|
-
if (!paramsMatch) return;
|
152
|
-
|
153
|
-
let [, listingId, appId, contextId, itemId] = paramsMatch[1].split(",").map((param) => param.trim().replace(/^['"]|['"]$/g, ""));
|
154
|
-
|
155
|
-
appId = Number(appId);
|
156
|
-
contextId = Number(contextId);
|
157
|
-
itemId = Number(itemId);
|
158
|
-
|
159
|
-
const imageUrl = $row.find(`#mylisting_${listingId}_image`).attr("src") || null;
|
160
|
-
|
161
|
-
const buyerPaysElem = $row.find('.market_listing_price span[title="This is the price the buyer pays."]');
|
162
|
-
const buyerPaysPrice = StringUtils.cleanSpace(buyerPaysElem.text().replace(/[()]/g, ""));
|
163
|
-
|
164
|
-
const receiveElem = $row.find('.market_listing_price span[title="This is how much you will receive."]');
|
165
|
-
const receivePrice = StringUtils.cleanSpace(receiveElem.text().replace(/[()]/g, ""));
|
166
|
-
|
167
|
-
const itemName = $row.find(".market_listing_item_name_link").text() || $row.find(".market_listing_item_name").text();
|
168
|
-
|
169
|
-
const gameName = $row.find(".market_listing_game_name").text();
|
170
|
-
|
171
|
-
const listedDateElem = $row.find(".market_listing_game_name + .market_listing_listed_date_combined");
|
172
|
-
const listedDate = formatMarketHistoryDate(StringUtils.cleanSpace(listedDateElem.text()));
|
173
|
-
|
174
|
-
results.push({
|
175
|
-
listingId,
|
176
|
-
appId,
|
177
|
-
contextId,
|
178
|
-
itemId,
|
179
|
-
imageUrl,
|
180
|
-
buyerPaysPrice,
|
181
|
-
receivePrice,
|
182
|
-
itemName,
|
183
|
-
gameName,
|
184
|
-
listedDate,
|
185
|
-
});
|
186
|
-
});
|
187
|
-
|
188
|
-
return results;
|
189
|
-
}
|
1
|
+
import { StringUtils } from "alpha-common-utils/index.js";
|
2
|
+
import { formatMarketHistoryDate } from "./utils.js";
|
3
|
+
import * as cheerio from "cheerio";
|
4
|
+
|
5
|
+
/**
|
6
|
+
* @typedef {Object} HoverItem
|
7
|
+
* @property {string|null} listingId The listing ID extracted from the selector, if available (as string).
|
8
|
+
* @property {number} appid The app (game) ID for this item.
|
9
|
+
* @property {number} contextid The context ID for this item.
|
10
|
+
* @property {number} assetid The unique asset ID.
|
11
|
+
* @property {string} unknown A string field captured from the arguments (usually "0").
|
12
|
+
* @property {string} [name_selector] The HTML selector for the item's name, if present.
|
13
|
+
* @property {string} [image_selector] The HTML selector for the item's image, if present.
|
14
|
+
* @property {string} [other_selector] The selector string if it does not match name/image pattern.
|
15
|
+
*/
|
16
|
+
|
17
|
+
/**
|
18
|
+
* Extracts item information from Steam inventory trade history or similar HTML fragments.
|
19
|
+
* Scans the given HTML string for calls to `CreateItemHoverFromContainer` and extracts
|
20
|
+
* key data for each referenced item. Returns each item's info in a structured array.
|
21
|
+
*
|
22
|
+
* @param {string} html - The HTML string to parse.
|
23
|
+
* @returns {HoverItem[]} Array of hover item objects.
|
24
|
+
*/
|
25
|
+
export function extractAssetItemsFromHovers(html) {
|
26
|
+
// Use your preferred HTML/space scrubber.
|
27
|
+
html = StringUtils.cleanSpace(html);
|
28
|
+
|
29
|
+
// RegExp for CreateItemHoverFromContainer arguments
|
30
|
+
const re = /CreateItemHoverFromContainer\s*\(\s*g_rgAssets\s*,\s*'([^']+)'\s*,\s*(\d+)\s*,\s*'(\d+)'\s*,\s*'(\d+)'\s*,\s*(\d+)\s*\)/g;
|
31
|
+
|
32
|
+
/** @type {Record<number, HoverItem>} */
|
33
|
+
const itemsMap = {};
|
34
|
+
let match;
|
35
|
+
|
36
|
+
while ((match = re.exec(html)) !== null) {
|
37
|
+
const [_, selector, appid, contextid, assetid, unknown] = match;
|
38
|
+
|
39
|
+
// Extract listingId as a string if present: "history_row_<listingId>_"
|
40
|
+
let listingId = null;
|
41
|
+
const lidMatch = selector.match(/history_row_(\d+)_/);
|
42
|
+
if (lidMatch) listingId = lidMatch[1]; // assign as string
|
43
|
+
|
44
|
+
const appidNum = Number(appid);
|
45
|
+
const contextidNum = Number(contextid);
|
46
|
+
const assetidNum = Number(assetid);
|
47
|
+
|
48
|
+
if (!itemsMap[assetidNum]) {
|
49
|
+
itemsMap[assetidNum] = {
|
50
|
+
listingId,
|
51
|
+
appid: appidNum,
|
52
|
+
contextid: contextidNum,
|
53
|
+
assetid: assetidNum,
|
54
|
+
unknown,
|
55
|
+
};
|
56
|
+
}
|
57
|
+
|
58
|
+
if (selector.endsWith("_name")) {
|
59
|
+
itemsMap[assetidNum].name_selector = selector;
|
60
|
+
} else if (selector.endsWith("_image")) {
|
61
|
+
itemsMap[assetidNum].image_selector = selector;
|
62
|
+
} else {
|
63
|
+
itemsMap[assetidNum].other_selector = selector;
|
64
|
+
}
|
65
|
+
}
|
66
|
+
return Object.values(itemsMap);
|
67
|
+
}
|
68
|
+
|
69
|
+
/**
|
70
|
+
* @typedef {Object} MarketHistoryListing
|
71
|
+
* @property {string} id - HTML row id attribute (e.g. "history_row_4316182845737233773_4316182845737233774").
|
72
|
+
* @property {string} listingId - Listing id (e.g. "4316182845737233773").
|
73
|
+
* @property {number} price - Price in integer form (e.g. 80270).
|
74
|
+
* @property {string} itemName - The market item's name (e.g. "UMP-45 | Mudder").
|
75
|
+
* @property {string} gameName - The game's name (e.g. "Counter-Strike 2").
|
76
|
+
* @property {string} listedOn - Listing date as string (e.g. "27 May 2024").
|
77
|
+
* @property {string} actedOn - Acted on date or empty string.
|
78
|
+
* @property {string} image - Full image URL for the item.
|
79
|
+
* @property {string} gainOrLoss - "+" for buys, "-" for sells.
|
80
|
+
* @property {string} status - Status text, usually empty string.
|
81
|
+
* @property {string} [assetId] - (optional) Associated asset id, if available.
|
82
|
+
*/
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Parses Steam market history listing rows into structured data objects.
|
86
|
+
*
|
87
|
+
* @param {string} html - The raw HTML string to parse.
|
88
|
+
* @param {Object} [assetByListingId={}] - Optional mapping of listingId to assetId.
|
89
|
+
* @returns {MarketHistoryListing[]} Array of market listing objects.
|
90
|
+
*/
|
91
|
+
export function parseMarketHistoryListings(html, assetByListingId = {}) {
|
92
|
+
const $ = cheerio.load(html);
|
93
|
+
return $(".market_listing_row")
|
94
|
+
.toArray()
|
95
|
+
.map((el) => {
|
96
|
+
const $el = $(el);
|
97
|
+
const id = $el.attr("id");
|
98
|
+
const listingId = id?.match(/history_row_(.*?)_/)?.[1];
|
99
|
+
if (!listingId) return null;
|
100
|
+
const [gainOrLoss, image, priceText, itemName, gameName, listedOnText, actedOnText, status] = [$el.find(".market_listing_gainorloss").text().trim(), $el.find(".market_listing_item_img").attr("src"), $el.find(".market_table_value .market_listing_price").text(), $el.find(".market_listing_item_name").text().trim(), $el.find(".market_listing_game_name").text().trim(), $el.find(".market_listing_listed_date + .market_listing_listed_date").text(), $el.find(".market_listing_whoactedwith + .market_listing_listed_date").text(), $el.find(".market_listing_whoactedwith").text().trim()];
|
101
|
+
const price = parseInt(priceText.replace(/[₫().,]/g, "").trim(), 10) || "";
|
102
|
+
const listedOn = typeof formatMarketHistoryDate === "function" ? formatMarketHistoryDate(listedOnText.replace(/Listed:/, "").trim()) : listedOnText.replace(/Listed:/, "").trim();
|
103
|
+
const actedOn = typeof formatMarketHistoryDate === "function" ? formatMarketHistoryDate(actedOnText.replace(/Listed:/, "").trim()) : actedOnText.replace(/Listed:/, "").trim();
|
104
|
+
|
105
|
+
const result = {
|
106
|
+
id,
|
107
|
+
listingId,
|
108
|
+
price,
|
109
|
+
itemName,
|
110
|
+
gameName,
|
111
|
+
listedOn,
|
112
|
+
actedOn,
|
113
|
+
image,
|
114
|
+
gainOrLoss,
|
115
|
+
status,
|
116
|
+
};
|
117
|
+
if (assetByListingId?.[listingId]) result.assetId = assetByListingId[listingId];
|
118
|
+
return result;
|
119
|
+
})
|
120
|
+
.filter(Boolean);
|
121
|
+
}
|
122
|
+
|
123
|
+
/**
|
124
|
+
* Parses Steam Market HTML listings to an array of items.
|
125
|
+
*
|
126
|
+
* @param {string} html - HTML string containing listing rows.
|
127
|
+
* @returns {Array<{
|
128
|
+
* listingId: string,
|
129
|
+
* appId: number,
|
130
|
+
* contextId: number,
|
131
|
+
* itemId: number,
|
132
|
+
* imageUrl: string|null,
|
133
|
+
* buyerPaysPrice: string,
|
134
|
+
* receivePrice: string,
|
135
|
+
* itemName: string,
|
136
|
+
* gameName: string,
|
137
|
+
* listedDate: string
|
138
|
+
* }>}
|
139
|
+
*/
|
140
|
+
export function parseMarketListings(html) {
|
141
|
+
const $ = cheerio.load(html);
|
142
|
+
const results = [];
|
143
|
+
|
144
|
+
$(".market_listing_row").each((index, element) => {
|
145
|
+
const $row = $(element);
|
146
|
+
|
147
|
+
const cancelLink = $row.find(".market_listing_cancel_button > a").attr("href");
|
148
|
+
if (!cancelLink) return;
|
149
|
+
|
150
|
+
const paramsMatch = cancelLink.match(/\(([^)]*)\)/);
|
151
|
+
if (!paramsMatch) return;
|
152
|
+
|
153
|
+
let [, listingId, appId, contextId, itemId] = paramsMatch[1].split(",").map((param) => param.trim().replace(/^['"]|['"]$/g, ""));
|
154
|
+
|
155
|
+
appId = Number(appId);
|
156
|
+
contextId = Number(contextId);
|
157
|
+
itemId = Number(itemId);
|
158
|
+
|
159
|
+
const imageUrl = $row.find(`#mylisting_${listingId}_image`).attr("src") || null;
|
160
|
+
|
161
|
+
const buyerPaysElem = $row.find('.market_listing_price span[title="This is the price the buyer pays."]');
|
162
|
+
const buyerPaysPrice = StringUtils.cleanSpace(buyerPaysElem.text().replace(/[()]/g, ""));
|
163
|
+
|
164
|
+
const receiveElem = $row.find('.market_listing_price span[title="This is how much you will receive."]');
|
165
|
+
const receivePrice = StringUtils.cleanSpace(receiveElem.text().replace(/[()]/g, ""));
|
166
|
+
|
167
|
+
const itemName = $row.find(".market_listing_item_name_link").text() || $row.find(".market_listing_item_name").text();
|
168
|
+
|
169
|
+
const gameName = $row.find(".market_listing_game_name").text();
|
170
|
+
|
171
|
+
const listedDateElem = $row.find(".market_listing_game_name + .market_listing_listed_date_combined");
|
172
|
+
const listedDate = formatMarketHistoryDate(StringUtils.cleanSpace(listedDateElem.text()));
|
173
|
+
|
174
|
+
results.push({
|
175
|
+
listingId,
|
176
|
+
appId,
|
177
|
+
contextId,
|
178
|
+
itemId,
|
179
|
+
imageUrl,
|
180
|
+
buyerPaysPrice,
|
181
|
+
receivePrice,
|
182
|
+
itemName,
|
183
|
+
gameName,
|
184
|
+
listedDate,
|
185
|
+
});
|
186
|
+
});
|
187
|
+
|
188
|
+
return results;
|
189
|
+
}
|