@assetpay/steam-mobile 1.0.4 → 1.1.0
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/README.md +158 -4
- package/dist/index.d.mts +306 -42
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +510 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -6
package/dist/index.mjs
CHANGED
|
@@ -68,6 +68,7 @@ let EConfirmationType = /* @__PURE__ */ function(EConfirmationType) {
|
|
|
68
68
|
EConfirmationType[EConfirmationType["FeatureOptOut"] = 4] = "FeatureOptOut";
|
|
69
69
|
EConfirmationType[EConfirmationType["PhoneNumberChange"] = 5] = "PhoneNumberChange";
|
|
70
70
|
EConfirmationType[EConfirmationType["AccountRecovery"] = 6] = "AccountRecovery";
|
|
71
|
+
EConfirmationType[EConfirmationType["BuyOrder"] = 12] = "BuyOrder";
|
|
71
72
|
return EConfirmationType;
|
|
72
73
|
}({});
|
|
73
74
|
let EOfferFilter = /* @__PURE__ */ function(EOfferFilter) {
|
|
@@ -306,6 +307,13 @@ var RateLimitError = class extends SteamError {
|
|
|
306
307
|
this.unlockAt = Date.now() + this.retryAfterMs;
|
|
307
308
|
}
|
|
308
309
|
};
|
|
310
|
+
var ThrottledError = class extends SteamError {
|
|
311
|
+
statusCode = 403;
|
|
312
|
+
constructor(message = "Throttled (403)", options) {
|
|
313
|
+
super(message, options);
|
|
314
|
+
this.name = "ThrottledError";
|
|
315
|
+
}
|
|
316
|
+
};
|
|
309
317
|
var EscrowError = class extends SteamError {
|
|
310
318
|
escrowDays;
|
|
311
319
|
constructor(escrowDays, message) {
|
|
@@ -362,6 +370,24 @@ var PrivateInventoryError = class extends SteamError {
|
|
|
362
370
|
this.name = "PrivateInventoryError";
|
|
363
371
|
}
|
|
364
372
|
};
|
|
373
|
+
var MarketConfirmationLimitError = class extends SteamError {
|
|
374
|
+
constructor(message = "Too many listings pending confirmation", options) {
|
|
375
|
+
super(message, options);
|
|
376
|
+
this.name = "MarketConfirmationLimitError";
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
var WalletBalanceLimitError = class extends SteamError {
|
|
380
|
+
constructor(message = "Listing would exceed the maximum wallet balance", options) {
|
|
381
|
+
super(message, options);
|
|
382
|
+
this.name = "WalletBalanceLimitError";
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
var MarketBlockedError = class extends SteamError {
|
|
386
|
+
constructor(message = "This account is currently unable to use the Community Market", options) {
|
|
387
|
+
super(message, options);
|
|
388
|
+
this.name = "MarketBlockedError";
|
|
389
|
+
}
|
|
390
|
+
};
|
|
365
391
|
var LoginError = class extends SteamError {
|
|
366
392
|
extendedErrorMessage;
|
|
367
393
|
isTransient;
|
|
@@ -1292,7 +1318,7 @@ function queryParams(url) {
|
|
|
1292
1318
|
for (const [k, v] of new URL(url).searchParams) out[k] = v;
|
|
1293
1319
|
return out;
|
|
1294
1320
|
}
|
|
1295
|
-
function decodeEntities(s) {
|
|
1321
|
+
function decodeEntities$1(s) {
|
|
1296
1322
|
return s.replace(/"/g, "\"").replace(/�?39;/g, "'").replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
1297
1323
|
}
|
|
1298
1324
|
function parseOpenidForm(html) {
|
|
@@ -1303,14 +1329,14 @@ function parseOpenidForm(html) {
|
|
|
1303
1329
|
for (const tag of (form[2] ?? "").match(/<input\b[^>]*>/gi) ?? []) {
|
|
1304
1330
|
const name = tag.match(/\bname=["']([^"']*)["']/i)?.[1];
|
|
1305
1331
|
if (!name) continue;
|
|
1306
|
-
const value = decodeEntities(tag.match(/\bvalue=["']([^"']*)["']/i)?.[1] ?? "");
|
|
1332
|
+
const value = decodeEntities$1(tag.match(/\bvalue=["']([^"']*)["']/i)?.[1] ?? "");
|
|
1307
1333
|
fields.push({
|
|
1308
1334
|
name,
|
|
1309
1335
|
value
|
|
1310
1336
|
});
|
|
1311
1337
|
}
|
|
1312
1338
|
return {
|
|
1313
|
-
action: new URL(decodeEntities(action), STEAM_OPENID_URL).toString(),
|
|
1339
|
+
action: new URL(decodeEntities$1(action), STEAM_OPENID_URL).toString(),
|
|
1314
1340
|
fields
|
|
1315
1341
|
};
|
|
1316
1342
|
}
|
|
@@ -1472,6 +1498,16 @@ var CommunityNamespace = class {
|
|
|
1472
1498
|
input: { steamid: id }
|
|
1473
1499
|
})).response?.player_level ?? 0;
|
|
1474
1500
|
}
|
|
1501
|
+
async getTwoFactorStatus() {
|
|
1502
|
+
const body = await this.api.call({
|
|
1503
|
+
httpMethod: "POST",
|
|
1504
|
+
iface: "ITwoFactorService",
|
|
1505
|
+
method: "QueryStatus",
|
|
1506
|
+
input: { steamid: this.session.steamID.getSteamID64() }
|
|
1507
|
+
});
|
|
1508
|
+
if (!body.response) throw new SteamError("Malformed two-factor status response", { body });
|
|
1509
|
+
return body.response;
|
|
1510
|
+
}
|
|
1475
1511
|
async ensureApiKey(domain = "assetpay.gg") {
|
|
1476
1512
|
await this.session.getAccessToken();
|
|
1477
1513
|
const res = await this.http.get(`${URLS.community}/dev/apikey?l=english`, { responseType: "text" });
|
|
@@ -1623,6 +1659,9 @@ var ConfirmationManager = class {
|
|
|
1623
1659
|
}));
|
|
1624
1660
|
}
|
|
1625
1661
|
async respondToConfirmation(confID, confKey, time, key, accept) {
|
|
1662
|
+
const ids = Array.isArray(confID) ? confID : [confID];
|
|
1663
|
+
const keys = Array.isArray(confKey) ? confKey : [confKey];
|
|
1664
|
+
if (ids.length !== keys.length) throw new ConfirmationError("confID and confKey must have the same length");
|
|
1626
1665
|
const { tag, k } = splitKey(key, accept ? "allow" : "cancel");
|
|
1627
1666
|
const op = accept ? "allow" : "cancel";
|
|
1628
1667
|
const res = await this.http.post(`${URLS.community}/mobileconf/multiajaxop`, {
|
|
@@ -1631,13 +1670,13 @@ var ConfirmationManager = class {
|
|
|
1631
1670
|
...this.confParams(k, time, tag),
|
|
1632
1671
|
op
|
|
1633
1672
|
},
|
|
1634
|
-
multipart: [{
|
|
1673
|
+
multipart: ids.flatMap((id, i) => [{
|
|
1635
1674
|
name: "cid[]",
|
|
1636
|
-
value:
|
|
1675
|
+
value: id
|
|
1637
1676
|
}, {
|
|
1638
1677
|
name: "ck[]",
|
|
1639
|
-
value:
|
|
1640
|
-
}]
|
|
1678
|
+
value: keys[i]
|
|
1679
|
+
}])
|
|
1641
1680
|
});
|
|
1642
1681
|
if (res.statusCode !== 200) throw httpError(res);
|
|
1643
1682
|
if (res.body?.success) return;
|
|
@@ -1653,10 +1692,22 @@ var ConfirmationManager = class {
|
|
|
1653
1692
|
});
|
|
1654
1693
|
}
|
|
1655
1694
|
acceptConfirmation(confID, nonce) {
|
|
1656
|
-
return this.
|
|
1695
|
+
return this.actOnConfirmations([{
|
|
1696
|
+
id: confID,
|
|
1697
|
+
key: nonce
|
|
1698
|
+
}], true);
|
|
1657
1699
|
}
|
|
1658
1700
|
rejectConfirmation(confID, nonce) {
|
|
1659
|
-
return this.
|
|
1701
|
+
return this.actOnConfirmations([{
|
|
1702
|
+
id: confID,
|
|
1703
|
+
key: nonce
|
|
1704
|
+
}], false);
|
|
1705
|
+
}
|
|
1706
|
+
acceptConfirmations(confs) {
|
|
1707
|
+
return this.actOnConfirmations(confs, true);
|
|
1708
|
+
}
|
|
1709
|
+
rejectConfirmations(confs) {
|
|
1710
|
+
return this.actOnConfirmations(confs, false);
|
|
1660
1711
|
}
|
|
1661
1712
|
async acceptAll() {
|
|
1662
1713
|
const pending = await this.getPending();
|
|
@@ -1668,13 +1719,14 @@ var ConfirmationManager = class {
|
|
|
1668
1719
|
if (!conf) throw new ConfirmationError(`Could not find confirmation for object ${objectID}`);
|
|
1669
1720
|
await this.acceptConfirmation(conf.id, conf.key);
|
|
1670
1721
|
}
|
|
1671
|
-
async
|
|
1722
|
+
async actOnConfirmations(confs, accept) {
|
|
1723
|
+
if (confs.length === 0) return;
|
|
1672
1724
|
const secret = this.requireSecret();
|
|
1673
1725
|
const offset = await this.getTimeOffset();
|
|
1674
1726
|
const time = this.nextConfTime(offset);
|
|
1675
1727
|
const tag = accept ? "accept" : "cancel";
|
|
1676
1728
|
const key = getConfirmationKey(secret, time, tag);
|
|
1677
|
-
await this.respondToConfirmation(
|
|
1729
|
+
await this.respondToConfirmation(confs.map((c) => c.id), confs.map((c) => c.key), time, {
|
|
1678
1730
|
tag,
|
|
1679
1731
|
key
|
|
1680
1732
|
}, accept);
|
|
@@ -1877,6 +1929,445 @@ var SteamWebApi = class {
|
|
|
1877
1929
|
return response;
|
|
1878
1930
|
}
|
|
1879
1931
|
};
|
|
1932
|
+
//#endregion
|
|
1933
|
+
//#region src/market/prices.ts
|
|
1934
|
+
function getPriceValueAsInt(strAmount) {
|
|
1935
|
+
if (!strAmount) return 0;
|
|
1936
|
+
let amount = String(strAmount);
|
|
1937
|
+
amount = amount.replace(/,/g, ".");
|
|
1938
|
+
amount = amount.replace(".--", ".00");
|
|
1939
|
+
amount = amount.replace(/[^\d.]/g, "");
|
|
1940
|
+
if (amount.indexOf(".") !== -1) {
|
|
1941
|
+
const segments = amount.split(".");
|
|
1942
|
+
const last = segments[segments.length - 1] ?? "";
|
|
1943
|
+
if (!Number.isNaN(Number(last)) && last.length === 3 && segments[segments.length - 2] !== "0") amount = segments.join("");
|
|
1944
|
+
else amount = `${segments.slice(0, -1).join("")}.${last}`;
|
|
1945
|
+
}
|
|
1946
|
+
const cents = Number.parseFloat(amount) * 100;
|
|
1947
|
+
return Math.max(Math.floor(Number.isNaN(cents) ? 0 : cents + 1e-6), 0);
|
|
1948
|
+
}
|
|
1949
|
+
//#endregion
|
|
1950
|
+
//#region src/market/history.ts
|
|
1951
|
+
function parseMarketHistory(body) {
|
|
1952
|
+
const history = {
|
|
1953
|
+
events: [],
|
|
1954
|
+
sales: [],
|
|
1955
|
+
purchases: [],
|
|
1956
|
+
listingEvents: [],
|
|
1957
|
+
totalCount: body.total_count
|
|
1958
|
+
};
|
|
1959
|
+
if (!body.results_html) return history;
|
|
1960
|
+
const hovers = body.hovers ?? "";
|
|
1961
|
+
const assets = body.assets ?? {};
|
|
1962
|
+
const html = body.results_html.replace(/<!--[\s\S]*?-->/g, "");
|
|
1963
|
+
for (const row of findByClass(html, "market_listing_row", "market_recent_listing_row")) {
|
|
1964
|
+
const dates = findByClass(row.inner, "market_listing_listed_date");
|
|
1965
|
+
const itemName = textOf(row.inner, "market_listing_item_name");
|
|
1966
|
+
const gameName = textOf(row.inner, "market_listing_game_name");
|
|
1967
|
+
const actedOn = dates[0] ? elementText(dates[0]).trim() : "";
|
|
1968
|
+
const listedOn = dates[1] ? elementText(dates[1]).trim() : "";
|
|
1969
|
+
const displayPrice = textOf(row.inner, "market_listing_price").trim();
|
|
1970
|
+
const rowId = attrValue(row.attrs, "id") ?? "";
|
|
1971
|
+
const priceInCents = getPriceValueAsInt(displayPrice);
|
|
1972
|
+
if (priceInCents === 0 && displayPrice) throw new SteamError(`Market history price parsed to 0 cents for "${displayPrice}" in row ${rowId || "?"}`);
|
|
1973
|
+
const gainOrLoss = textOf(row.inner, "market_listing_gainorloss").trim();
|
|
1974
|
+
const type = gainOrLoss === "-" ? "sale" : gainOrLoss === "+" ? "purchase" : "listing_event";
|
|
1975
|
+
const idMatch = rowId.match(/^history_row_(\d+)_(\d+)$/);
|
|
1976
|
+
if (!idMatch) continue;
|
|
1977
|
+
const event = {
|
|
1978
|
+
historyId: rowId,
|
|
1979
|
+
listingid: idMatch[1],
|
|
1980
|
+
eventid: idMatch[2],
|
|
1981
|
+
itemName,
|
|
1982
|
+
gameName,
|
|
1983
|
+
listedOn,
|
|
1984
|
+
actedOn,
|
|
1985
|
+
displayPrice,
|
|
1986
|
+
priceInCents,
|
|
1987
|
+
type,
|
|
1988
|
+
marketName: null,
|
|
1989
|
+
appID: null,
|
|
1990
|
+
contextID: null,
|
|
1991
|
+
assetID: null,
|
|
1992
|
+
classID: null,
|
|
1993
|
+
instanceID: null,
|
|
1994
|
+
unOwnedContextID: null,
|
|
1995
|
+
unOwnedID: null
|
|
1996
|
+
};
|
|
1997
|
+
if (type !== "listing_event") attachAsset(event, rowId, hovers, assets);
|
|
1998
|
+
if (type === "sale") {
|
|
1999
|
+
const sale = {
|
|
2000
|
+
...event,
|
|
2001
|
+
type,
|
|
2002
|
+
receivedAmount: priceInCents
|
|
2003
|
+
};
|
|
2004
|
+
history.sales.push(sale);
|
|
2005
|
+
history.events.push(sale);
|
|
2006
|
+
} else if (type === "purchase") {
|
|
2007
|
+
const purchase = {
|
|
2008
|
+
...event,
|
|
2009
|
+
type,
|
|
2010
|
+
paidAmount: priceInCents
|
|
2011
|
+
};
|
|
2012
|
+
history.purchases.push(purchase);
|
|
2013
|
+
history.events.push(purchase);
|
|
2014
|
+
} else {
|
|
2015
|
+
const listingEvent = {
|
|
2016
|
+
...event,
|
|
2017
|
+
type
|
|
2018
|
+
};
|
|
2019
|
+
history.listingEvents.push(listingEvent);
|
|
2020
|
+
history.events.push(listingEvent);
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
return history;
|
|
2024
|
+
}
|
|
2025
|
+
function attachAsset(event, rowId, hovers, assets) {
|
|
2026
|
+
try {
|
|
2027
|
+
const args = hovers.split(`${rowId}_name`)[1].split(")")[0].split(",");
|
|
2028
|
+
event.appID = Number.parseInt(args[1], 10);
|
|
2029
|
+
event.contextID = args[2].split("'")[1] ?? null;
|
|
2030
|
+
event.assetID = args[3].split("'")[1] ?? null;
|
|
2031
|
+
if (event.contextID === null || event.assetID === null) return;
|
|
2032
|
+
const asset = assets[String(event.appID)]?.[event.contextID]?.[event.assetID];
|
|
2033
|
+
if (asset) {
|
|
2034
|
+
event.classID = asset.classid ?? null;
|
|
2035
|
+
event.instanceID = asset.instanceid ?? null;
|
|
2036
|
+
event.unOwnedContextID = asset.unowned_contextid ?? null;
|
|
2037
|
+
event.unOwnedID = asset.unowned_id ?? null;
|
|
2038
|
+
event.marketName = asset.market_hash_name ?? null;
|
|
2039
|
+
}
|
|
2040
|
+
} catch {}
|
|
2041
|
+
}
|
|
2042
|
+
const TAG = /<([a-zA-Z][\w:-]*)((?:"[^"]*"|'[^']*'|[^'">])*)>/g;
|
|
2043
|
+
const VOID_TAGS = /* @__PURE__ */ new Set([
|
|
2044
|
+
"area",
|
|
2045
|
+
"base",
|
|
2046
|
+
"br",
|
|
2047
|
+
"col",
|
|
2048
|
+
"embed",
|
|
2049
|
+
"hr",
|
|
2050
|
+
"img",
|
|
2051
|
+
"input",
|
|
2052
|
+
"link",
|
|
2053
|
+
"meta",
|
|
2054
|
+
"source",
|
|
2055
|
+
"track",
|
|
2056
|
+
"wbr"
|
|
2057
|
+
]);
|
|
2058
|
+
function findByClass(html, ...classes) {
|
|
2059
|
+
const found = [];
|
|
2060
|
+
for (const m of html.matchAll(TAG)) {
|
|
2061
|
+
const attrs = m[2] ?? "";
|
|
2062
|
+
const tokens = (attrValue(attrs, "class") ?? "").split(/\s+/);
|
|
2063
|
+
if (!classes.every((c) => tokens.includes(c))) continue;
|
|
2064
|
+
const tag = m[1].toLowerCase();
|
|
2065
|
+
const start = m.index + m[0].length;
|
|
2066
|
+
const empty = VOID_TAGS.has(tag) || attrs.trimEnd().endsWith("/");
|
|
2067
|
+
found.push({
|
|
2068
|
+
attrs,
|
|
2069
|
+
inner: empty ? "" : html.slice(start, closeIndex(html, tag, start))
|
|
2070
|
+
});
|
|
2071
|
+
}
|
|
2072
|
+
return found;
|
|
2073
|
+
}
|
|
2074
|
+
function closeIndex(html, tag, from) {
|
|
2075
|
+
const re = new RegExp(`<(/?)${tag}\\b((?:"[^"]*"|'[^']*'|[^'">])*)>`, "gi");
|
|
2076
|
+
re.lastIndex = from;
|
|
2077
|
+
let depth = 1;
|
|
2078
|
+
for (let m = re.exec(html); m; m = re.exec(html)) if (m[1]) {
|
|
2079
|
+
if (--depth === 0) return m.index;
|
|
2080
|
+
} else if (!(m[2] ?? "").trimEnd().endsWith("/")) depth++;
|
|
2081
|
+
return html.length;
|
|
2082
|
+
}
|
|
2083
|
+
const ATTR = /([^\s"'>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
2084
|
+
function attrValue(attrs, name) {
|
|
2085
|
+
for (const m of attrs.matchAll(ATTR)) if (m[1].toLowerCase() === name) return decodeEntities(m[2] ?? m[3] ?? m[4] ?? "");
|
|
2086
|
+
}
|
|
2087
|
+
function textOf(html, className) {
|
|
2088
|
+
return findByClass(html, className).map(elementText).join("");
|
|
2089
|
+
}
|
|
2090
|
+
function elementText(el) {
|
|
2091
|
+
return decodeEntities(el.inner.replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g, ""));
|
|
2092
|
+
}
|
|
2093
|
+
const NAMED_ENTITIES = {
|
|
2094
|
+
amp: "&",
|
|
2095
|
+
lt: "<",
|
|
2096
|
+
gt: ">",
|
|
2097
|
+
quot: "\"",
|
|
2098
|
+
apos: "'",
|
|
2099
|
+
nbsp: "\xA0"
|
|
2100
|
+
};
|
|
2101
|
+
function decodeEntities(s) {
|
|
2102
|
+
return s.replace(/&(#x[0-9a-f]+|#\d+|[a-z]+);/gi, (whole, ref) => {
|
|
2103
|
+
if (ref[0] === "#") {
|
|
2104
|
+
const code = ref[1] === "x" || ref[1] === "X" ? Number.parseInt(ref.slice(2), 16) : Number.parseInt(ref.slice(1), 10);
|
|
2105
|
+
return code <= 1114111 ? String.fromCodePoint(code) : whole;
|
|
2106
|
+
}
|
|
2107
|
+
return NAMED_ENTITIES[ref.toLowerCase()] ?? whole;
|
|
2108
|
+
});
|
|
2109
|
+
}
|
|
2110
|
+
//#endregion
|
|
2111
|
+
//#region src/market/MarketNamespace.ts
|
|
2112
|
+
const LISTINGS_PAGE_SIZE = 100;
|
|
2113
|
+
const MARKET_REFERER = `${URLS.community}/market/`;
|
|
2114
|
+
var MarketNamespace = class {
|
|
2115
|
+
http;
|
|
2116
|
+
session;
|
|
2117
|
+
confirmations;
|
|
2118
|
+
api;
|
|
2119
|
+
walletCurrency;
|
|
2120
|
+
constructor(http, session, confirmations, api) {
|
|
2121
|
+
this.http = http;
|
|
2122
|
+
this.session = session;
|
|
2123
|
+
this.confirmations = confirmations;
|
|
2124
|
+
this.api = api;
|
|
2125
|
+
}
|
|
2126
|
+
async getWalletDetails() {
|
|
2127
|
+
const body = await this.api.call({
|
|
2128
|
+
httpMethod: "POST",
|
|
2129
|
+
iface: "IUserAccountService",
|
|
2130
|
+
method: "GetClientWalletDetails",
|
|
2131
|
+
input: {
|
|
2132
|
+
include_balance_in_usd: 1,
|
|
2133
|
+
include_formatted_balance: 1
|
|
2134
|
+
}
|
|
2135
|
+
});
|
|
2136
|
+
if (!body.response) throw new SteamError("Malformed wallet details response", { body });
|
|
2137
|
+
const currency = body.response.currency_code;
|
|
2138
|
+
if (typeof currency === "number" && currency > 0) this.walletCurrency = currency;
|
|
2139
|
+
return body.response;
|
|
2140
|
+
}
|
|
2141
|
+
async getMyListings(options = {}) {
|
|
2142
|
+
await this.session.getAccessToken();
|
|
2143
|
+
const result = {
|
|
2144
|
+
listings: [],
|
|
2145
|
+
listings_to_confirm: [],
|
|
2146
|
+
buy_orders: [],
|
|
2147
|
+
num_active_listings: 0
|
|
2148
|
+
};
|
|
2149
|
+
let start = 0;
|
|
2150
|
+
for (;;) {
|
|
2151
|
+
const res = await this.http.get(`${URLS.community}/market/mylistings/`, {
|
|
2152
|
+
responseType: "json",
|
|
2153
|
+
searchParams: {
|
|
2154
|
+
norender: 1,
|
|
2155
|
+
currency: options.currency ?? this.walletCurrency,
|
|
2156
|
+
start,
|
|
2157
|
+
count: LISTINGS_PAGE_SIZE
|
|
2158
|
+
}
|
|
2159
|
+
});
|
|
2160
|
+
if (res.statusCode !== 200) throw httpError(res);
|
|
2161
|
+
const body = res.body;
|
|
2162
|
+
if (!body?.success) throw new SteamError("Steam returned success=false for /market/mylistings", { body });
|
|
2163
|
+
if (body.num_active_listings !== void 0) result.num_active_listings = body.num_active_listings;
|
|
2164
|
+
if (start === 0) {
|
|
2165
|
+
if (Array.isArray(body.buy_orders)) result.buy_orders = body.buy_orders;
|
|
2166
|
+
if (Array.isArray(body.listings_to_confirm)) result.listings_to_confirm = body.listings_to_confirm;
|
|
2167
|
+
}
|
|
2168
|
+
const page = body.listings ?? body.results?.listings ?? [];
|
|
2169
|
+
result.listings.push(...page);
|
|
2170
|
+
start += page.length;
|
|
2171
|
+
if (start >= result.num_active_listings || page.length === 0) return result;
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
async getMyHistory(options = {}) {
|
|
2175
|
+
await this.session.getAccessToken();
|
|
2176
|
+
const res = await this.http.get(`${URLS.community}/market/myhistory`, {
|
|
2177
|
+
responseType: "json",
|
|
2178
|
+
searchParams: {
|
|
2179
|
+
count: options.count ?? 500,
|
|
2180
|
+
start: options.start
|
|
2181
|
+
}
|
|
2182
|
+
});
|
|
2183
|
+
if (res.statusCode !== 200) throw httpError(res);
|
|
2184
|
+
if (!res.body?.success) throw new SteamError("Steam returned success=false for /market/myhistory", { body: res.body });
|
|
2185
|
+
return parseMarketHistory(res.body);
|
|
2186
|
+
}
|
|
2187
|
+
async getOrderbook(appid, marketHashName, options = {}) {
|
|
2188
|
+
await this.session.getAccessToken();
|
|
2189
|
+
const qp = encodeURIComponent(JSON.stringify([appid, marketHashName]));
|
|
2190
|
+
const res = await this.http.get(`${URLS.community}/market/orderbook?q=Load&qp=${qp}`, { responseType: "json" });
|
|
2191
|
+
if (res.statusCode === 403) throw new ThrottledError(void 0, { body: res.body });
|
|
2192
|
+
if (res.statusCode !== 200) throw httpError(res);
|
|
2193
|
+
const body = res.body;
|
|
2194
|
+
const payload = body && body.success === void 0 && body.data ? body.data : body;
|
|
2195
|
+
if (!payload?.success || !payload.data) throw new SteamError(`Bad orderbook response for ${marketHashName}`, { body });
|
|
2196
|
+
const data = payload.data;
|
|
2197
|
+
const expected = options.expectedCurrency ?? this.walletCurrency;
|
|
2198
|
+
if (expected && data.eCurrency !== expected) throw new SteamError(`Orderbook currency ${data.eCurrency} != expected ${expected} for ${marketHashName}`, { body });
|
|
2199
|
+
return data;
|
|
2200
|
+
}
|
|
2201
|
+
async getPriceHistory(appid, marketHashName) {
|
|
2202
|
+
await this.session.getAccessToken();
|
|
2203
|
+
const res = await this.http.get(`${URLS.community}/market/pricehistory?appid=${appid}&market_hash_name=${encodeURIComponent(marketHashName)}`, { responseType: "json" });
|
|
2204
|
+
if (res.statusCode !== 200) throw httpError(res);
|
|
2205
|
+
if (!res.body?.success) throw new SteamError(`Steam returned success=false for pricehistory of ${marketHashName}`, { body: res.body });
|
|
2206
|
+
return Array.isArray(res.body.prices) ? res.body.prices : [];
|
|
2207
|
+
}
|
|
2208
|
+
async getMarketItemDetails(appid, marketHashName) {
|
|
2209
|
+
await this.session.getAccessToken();
|
|
2210
|
+
const query = encodeURIComponent(`"${marketHashName}"`);
|
|
2211
|
+
const res = await this.http.get(`${URLS.community}/market/search/render/?query=${query}&start=0&count=10&search_descriptions=0&sort_column=quantity&sort_dir=desc&appid=${appid}&norender=1`, { responseType: "json" });
|
|
2212
|
+
if (res.statusCode !== 200) throw httpError(res);
|
|
2213
|
+
const body = res.body;
|
|
2214
|
+
if (!body || typeof body !== "object") throw new SteamError("Malformed market search response", { body });
|
|
2215
|
+
return (Array.isArray(body.results) ? body.results : []).find((r) => r.hash_name === marketHashName) ?? null;
|
|
2216
|
+
}
|
|
2217
|
+
async sellItem(options) {
|
|
2218
|
+
await this.session.getAccessToken();
|
|
2219
|
+
const sessionid = await this.http.getSessionId();
|
|
2220
|
+
const res = await this.http.post(`${URLS.community}/market/sellitem/`, {
|
|
2221
|
+
responseType: "json",
|
|
2222
|
+
form: {
|
|
2223
|
+
sessionid,
|
|
2224
|
+
appid: options.appid,
|
|
2225
|
+
contextid: options.contextid ?? "2",
|
|
2226
|
+
assetid: options.assetid,
|
|
2227
|
+
amount: options.amount ?? 1,
|
|
2228
|
+
price: options.price
|
|
2229
|
+
},
|
|
2230
|
+
headers: { Referer: MARKET_REFERER }
|
|
2231
|
+
});
|
|
2232
|
+
const body = res.body;
|
|
2233
|
+
if (res.statusCode === 200 && (body?.success === true || body?.success === 1)) return body;
|
|
2234
|
+
if (res.statusCode === 429 || res.statusCode === 401) throw httpError(res);
|
|
2235
|
+
if (body?.message) throw sellItemError(body.message, body);
|
|
2236
|
+
if (res.statusCode !== 200) throw httpError(res);
|
|
2237
|
+
throw new SteamError("Unknown error listing item", {
|
|
2238
|
+
...eresultOf(body),
|
|
2239
|
+
body
|
|
2240
|
+
});
|
|
2241
|
+
}
|
|
2242
|
+
async cancelListing(listingId) {
|
|
2243
|
+
await this.session.getAccessToken();
|
|
2244
|
+
const sessionid = await this.http.getSessionId();
|
|
2245
|
+
const res = await this.http.post(`${URLS.community}/market/removelisting/${listingId}`, {
|
|
2246
|
+
form: { sessionid },
|
|
2247
|
+
headers: { Referer: MARKET_REFERER }
|
|
2248
|
+
});
|
|
2249
|
+
if (res.statusCode === 200 || res.statusCode === 302 && redirectsToMarket(res)) return;
|
|
2250
|
+
throw httpError(res);
|
|
2251
|
+
}
|
|
2252
|
+
async createBuyOrder(options) {
|
|
2253
|
+
const currency = options.currency ?? this.walletCurrency;
|
|
2254
|
+
if (!currency) throw new SteamError("Wallet currency unknown — call getWalletDetails() or pass currency");
|
|
2255
|
+
await this.session.getAccessToken();
|
|
2256
|
+
const sessionid = await this.http.getSessionId();
|
|
2257
|
+
const referer = `${URLS.community}/market/listings/${options.appid}/${encodeURIComponent(options.marketHashName)}`;
|
|
2258
|
+
const form = {
|
|
2259
|
+
sessionid,
|
|
2260
|
+
currency,
|
|
2261
|
+
appid: options.appid,
|
|
2262
|
+
market_hash_name: options.marketHashName,
|
|
2263
|
+
price_total: options.priceTotal,
|
|
2264
|
+
quantity: options.quantity,
|
|
2265
|
+
confirmation: 0
|
|
2266
|
+
};
|
|
2267
|
+
const first = await this.postBuyOrder(form, referer);
|
|
2268
|
+
const direct = buyOrderId(first);
|
|
2269
|
+
if (direct) return direct;
|
|
2270
|
+
const confirmationId = first.body?.confirmation?.confirmation_id;
|
|
2271
|
+
if (!confirmationId) throw buyOrderError(first, `No confirmation_id (HTTP ${first.statusCode})`);
|
|
2272
|
+
await this.confirmations.acceptConfirmationForObject(String(confirmationId));
|
|
2273
|
+
const final = await this.postBuyOrder({
|
|
2274
|
+
...form,
|
|
2275
|
+
confirmation: String(confirmationId)
|
|
2276
|
+
}, referer);
|
|
2277
|
+
const orderId = buyOrderId(final);
|
|
2278
|
+
if (orderId) return orderId;
|
|
2279
|
+
throw buyOrderError(final, `Finalization failed (HTTP ${final.statusCode})`);
|
|
2280
|
+
}
|
|
2281
|
+
async cancelBuyOrder(buyOrderId) {
|
|
2282
|
+
await this.session.getAccessToken();
|
|
2283
|
+
const sessionid = await this.http.getSessionId();
|
|
2284
|
+
const res = await this.http.post(`${URLS.community}/market/cancelbuyorder/`, {
|
|
2285
|
+
responseType: "json",
|
|
2286
|
+
form: {
|
|
2287
|
+
sessionid,
|
|
2288
|
+
buy_orderid: buyOrderId
|
|
2289
|
+
},
|
|
2290
|
+
headers: { Referer: MARKET_REFERER }
|
|
2291
|
+
});
|
|
2292
|
+
if (res.statusCode !== 200) throw httpError(res);
|
|
2293
|
+
return res.body ?? {};
|
|
2294
|
+
}
|
|
2295
|
+
async confirmListings(expected) {
|
|
2296
|
+
if (Object.values(expected).reduce((sum, n) => sum + n, 0) === 0) return {
|
|
2297
|
+
confirmed: [],
|
|
2298
|
+
skipped: []
|
|
2299
|
+
};
|
|
2300
|
+
await this.session.getAccessToken();
|
|
2301
|
+
const listings = (await this.confirmations.getPending()).filter((c) => c.type === 3).sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
2302
|
+
const remaining = { ...expected };
|
|
2303
|
+
const confirmed = [];
|
|
2304
|
+
const skipped = [];
|
|
2305
|
+
for (const conf of listings) {
|
|
2306
|
+
const name = (remaining[conf.sending] ?? 0) > 0 ? conf.sending : Object.keys(remaining).find((n) => (remaining[n] ?? 0) > 0 && conf.sending !== "" && n.includes(conf.sending));
|
|
2307
|
+
if (name === void 0) {
|
|
2308
|
+
skipped.push(conf);
|
|
2309
|
+
continue;
|
|
2310
|
+
}
|
|
2311
|
+
confirmed.push(conf);
|
|
2312
|
+
remaining[name] = (remaining[name] ?? 0) - 1;
|
|
2313
|
+
}
|
|
2314
|
+
await this.confirmations.acceptConfirmations(confirmed);
|
|
2315
|
+
return {
|
|
2316
|
+
confirmed,
|
|
2317
|
+
skipped
|
|
2318
|
+
};
|
|
2319
|
+
}
|
|
2320
|
+
async rejectListings(types = [3]) {
|
|
2321
|
+
await this.session.getAccessToken();
|
|
2322
|
+
const matched = (await this.confirmations.getPending()).filter((c) => types.includes(c.type));
|
|
2323
|
+
await this.confirmations.rejectConfirmations(matched);
|
|
2324
|
+
return matched;
|
|
2325
|
+
}
|
|
2326
|
+
async postBuyOrder(form, referer) {
|
|
2327
|
+
const res = await this.http.post(`${URLS.community}/market/createbuyorder/`, {
|
|
2328
|
+
responseType: "json",
|
|
2329
|
+
form,
|
|
2330
|
+
headers: { Referer: referer }
|
|
2331
|
+
});
|
|
2332
|
+
if (res.statusCode === 429 || res.statusCode === 401) throw httpError(res);
|
|
2333
|
+
return res;
|
|
2334
|
+
}
|
|
2335
|
+
};
|
|
2336
|
+
function redirectsToMarket(res) {
|
|
2337
|
+
const location = res.headers.location;
|
|
2338
|
+
if (typeof location !== "string" || !URL.canParse(location, URLS.community)) return false;
|
|
2339
|
+
const { pathname } = new URL(location, URLS.community);
|
|
2340
|
+
return pathname === "/market/" || pathname === "/market";
|
|
2341
|
+
}
|
|
2342
|
+
function eresultOf(body) {
|
|
2343
|
+
const success = body?.success;
|
|
2344
|
+
return typeof success === "number" && success !== 1 ? { eresult: success } : {};
|
|
2345
|
+
}
|
|
2346
|
+
function sellItemError(message, body) {
|
|
2347
|
+
const options = {
|
|
2348
|
+
...eresultOf(body),
|
|
2349
|
+
body
|
|
2350
|
+
};
|
|
2351
|
+
const lower = message.toLowerCase();
|
|
2352
|
+
if (lower.includes("pending confirmation")) return new MarketConfirmationLimitError(message, options);
|
|
2353
|
+
if (lower.includes("maximum wallet balance")) return new WalletBalanceLimitError(message, options);
|
|
2354
|
+
if (lower.includes("unable to use the community market")) return new MarketBlockedError(message, options);
|
|
2355
|
+
return new SteamError(message, options);
|
|
2356
|
+
}
|
|
2357
|
+
function buyOrderId(res) {
|
|
2358
|
+
const body = res.body;
|
|
2359
|
+
const ok = body?.success === 1 || body?.success === true || body?.wallet_info?.success === 1;
|
|
2360
|
+
return res.statusCode === 200 && ok && body?.buy_orderid ? String(body.buy_orderid) : void 0;
|
|
2361
|
+
}
|
|
2362
|
+
function buyOrderError(res, fallback) {
|
|
2363
|
+
const body = res.body;
|
|
2364
|
+
const eresult = eresultOf(body);
|
|
2365
|
+
if (!body?.message && eresult.eresult === void 0 && ![200, 406].includes(res.statusCode)) return httpError(res);
|
|
2366
|
+
return new SteamError(body?.message || fallback, {
|
|
2367
|
+
...eresult,
|
|
2368
|
+
body
|
|
2369
|
+
});
|
|
2370
|
+
}
|
|
1880
2371
|
/**
|
|
1881
2372
|
* Describes the message CEconItemPreviewDataBlock.
|
|
1882
2373
|
* Use `create(CEconItemPreviewDataBlockSchema)` to create a new message.
|
|
@@ -2111,9 +2602,13 @@ async function fetchUserDetails(http, url, referer, myAccountId, partnerAccountI
|
|
|
2111
2602
|
if (res.statusCode !== 200) throw httpError(res);
|
|
2112
2603
|
const html = res.body;
|
|
2113
2604
|
checkCommunityError(html);
|
|
2114
|
-
if (!html.includes("g_rgAppContextData")) throw new SteamError("Failed to load the trade page for this user");
|
|
2605
|
+
if (!html.includes("g_rgAppContextData")) throw tradePageError(html) ?? new SteamError("Failed to load the trade page for this user");
|
|
2115
2606
|
return parseUserDetails(html, myAccountId, partnerAccountId);
|
|
2116
2607
|
}
|
|
2608
|
+
function tradePageError(html) {
|
|
2609
|
+
const message = html.match(/<div id="error_msg">\s*([^<]+)\s*<\/div>/)?.[1];
|
|
2610
|
+
return message ? parseStrError(message.replace(/\s+/g, " ").trim()) : void 0;
|
|
2611
|
+
}
|
|
2117
2612
|
function buildPartnerTradePageUrl(partnerAccountId, token) {
|
|
2118
2613
|
return `${URLS.community}/tradeoffer/new/?partner=${partnerAccountId}${token ? `&token=${token}` : ""}`;
|
|
2119
2614
|
}
|
|
@@ -2957,6 +3452,7 @@ var SteamMobile = class extends EventEmitter {
|
|
|
2957
3452
|
confirmations;
|
|
2958
3453
|
trade;
|
|
2959
3454
|
community;
|
|
3455
|
+
market;
|
|
2960
3456
|
identitySecret;
|
|
2961
3457
|
polling;
|
|
2962
3458
|
proxy;
|
|
@@ -2977,6 +3473,7 @@ var SteamMobile = class extends EventEmitter {
|
|
|
2977
3473
|
this.confirmations = new ConfirmationManager(this.http, this.session.steamID, options.identitySecret, profile);
|
|
2978
3474
|
this.trade = new TradeNamespace(this.api, this.http, this.session, this.confirmations);
|
|
2979
3475
|
this.community = new CommunityNamespace(this.http, this.session, this.confirmations, this.api);
|
|
3476
|
+
this.market = new MarketNamespace(this.http, this.session, this.confirmations, this.api);
|
|
2980
3477
|
this.session.on("refreshToken", (token) => this.emit("refreshToken", token));
|
|
2981
3478
|
this.session.on("sessionExpired", (error) => this.emit("sessionExpired", error));
|
|
2982
3479
|
this.session.on("debug", (message) => this.emit("debug", message));
|
|
@@ -3036,6 +3533,6 @@ var SteamMobile = class extends EventEmitter {
|
|
|
3036
3533
|
}
|
|
3037
3534
|
};
|
|
3038
3535
|
//#endregion
|
|
3039
|
-
export { ANDROID_PROFILE, AccessTokenError, AuthClient, CommunityNamespace, ConfirmationError, ConfirmationManager, CredentialSession, DEFAULT_CONTEXTID, DEFAULT_POLL_FULL_UPDATE_INTERVAL, DEFAULT_POLL_INTERVAL, DEFAULT_POLL_MAX_AGE_MS, DEFAULT_RATE_LIMIT_RETRY_MS, EAuthSessionGuardType, EAuthTokenPlatformType, EAuthTokenRevokeAction, EConfirmationMethod, EConfirmationType, EOfferFilter, EResult, ESessionPersistence, ETokenRenewalType, ETradeOfferState, ETradeStatus, EscrowError, FamilyViewError, HttpClient, HttpStatusError, IOS_PROFILE, ItemServerUnavailableError, LANG, LoginError, NewDeviceError, NoMobileAuthenticatorError, OfferLimitError, OpenIdError, Poller, PrivateInventoryError, ProxyError, RATE_LIMITS, RETRY_AFTER, RateLimitError, SessionManager, SteamError, SteamMobile, SteamSessionExpiredError, SteamWebApi, TERMINAL_AUTH_ERESULTS, TRANSIENT_ERESULTS, TargetCannotTradeError, TradeBanError, TradeNamespace, TradeOffer, URLS, WebApiClient, confirmOpenid, decodeJwt, decodePreviewToken, encodePreviewToken, getTradeHistory, getTradeOffersSummary, getTradeStatus, isTerminalAuthEResult, isTerminalState, isTransientEResult, loginWithCredentials, parseInventory, parseOpenidForm, parsePartnerInventory, resolveMobileProfile, resolveTarget, secondsUntilExpiry, steamOpenidLogin };
|
|
3536
|
+
export { ANDROID_PROFILE, AccessTokenError, AuthClient, CommunityNamespace, ConfirmationError, ConfirmationManager, CredentialSession, DEFAULT_CONTEXTID, DEFAULT_POLL_FULL_UPDATE_INTERVAL, DEFAULT_POLL_INTERVAL, DEFAULT_POLL_MAX_AGE_MS, DEFAULT_RATE_LIMIT_RETRY_MS, EAuthSessionGuardType, EAuthTokenPlatformType, EAuthTokenRevokeAction, EConfirmationMethod, EConfirmationType, EOfferFilter, EResult, ESessionPersistence, ETokenRenewalType, ETradeOfferState, ETradeStatus, EscrowError, FamilyViewError, HttpClient, HttpStatusError, IOS_PROFILE, ItemServerUnavailableError, LANG, LoginError, MarketBlockedError, MarketConfirmationLimitError, MarketNamespace, NewDeviceError, NoMobileAuthenticatorError, OfferLimitError, OpenIdError, Poller, PrivateInventoryError, ProxyError, RATE_LIMITS, RETRY_AFTER, RateLimitError, SessionManager, SteamError, SteamMobile, SteamSessionExpiredError, SteamWebApi, TERMINAL_AUTH_ERESULTS, TRANSIENT_ERESULTS, TargetCannotTradeError, ThrottledError, TradeBanError, TradeNamespace, TradeOffer, URLS, WalletBalanceLimitError, WebApiClient, confirmOpenid, decodeJwt, decodePreviewToken, encodePreviewToken, getPriceValueAsInt, getTradeHistory, getTradeOffersSummary, getTradeStatus, isTerminalAuthEResult, isTerminalState, isTransientEResult, loginWithCredentials, parseInventory, parseMarketHistory, parseOpenidForm, parsePartnerInventory, resolveMobileProfile, resolveTarget, secondsUntilExpiry, steamOpenidLogin };
|
|
3040
3537
|
|
|
3041
3538
|
//# sourceMappingURL=index.mjs.map
|