@assetpay/steam-mobile 1.0.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/dist/index.mjs ADDED
@@ -0,0 +1,2984 @@
1
+ import { Buffer as Buffer$1 } from "node:buffer";
2
+ import { create, fromBinary, toBinary, toJson } from "@bufbuild/protobuf";
3
+ import { fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2";
4
+ import { EventEmitter } from "node:events";
5
+ import SteamID from "steamid";
6
+ import { constants, createHash, createHmac, createPublicKey, publicEncrypt } from "node:crypto";
7
+ import { Impit } from "impit";
8
+ import { CookieJar } from "tough-cookie";
9
+ //#region src/core/constants.ts
10
+ const LANG = {
11
+ l: "english",
12
+ language: "en"
13
+ };
14
+ const URLS = {
15
+ community: "https://steamcommunity.com",
16
+ store: "https://store.steampowered.com",
17
+ help: "https://help.steampowered.com",
18
+ api: "https://api.steampowered.com"
19
+ };
20
+ const DEFAULT_CONTEXTID = "2";
21
+ const REFRESH_TOKEN_RENEW_THRESHOLD_SECONDS = 30 * 86400;
22
+ //#endregion
23
+ //#region src/core/enums.ts
24
+ let ETradeOfferState = /* @__PURE__ */ function(ETradeOfferState) {
25
+ ETradeOfferState[ETradeOfferState["Invalid"] = 1] = "Invalid";
26
+ ETradeOfferState[ETradeOfferState["Active"] = 2] = "Active";
27
+ ETradeOfferState[ETradeOfferState["Accepted"] = 3] = "Accepted";
28
+ ETradeOfferState[ETradeOfferState["Countered"] = 4] = "Countered";
29
+ ETradeOfferState[ETradeOfferState["Expired"] = 5] = "Expired";
30
+ ETradeOfferState[ETradeOfferState["Canceled"] = 6] = "Canceled";
31
+ ETradeOfferState[ETradeOfferState["Declined"] = 7] = "Declined";
32
+ ETradeOfferState[ETradeOfferState["InvalidItems"] = 8] = "InvalidItems";
33
+ ETradeOfferState[ETradeOfferState["CreatedNeedsConfirmation"] = 9] = "CreatedNeedsConfirmation";
34
+ ETradeOfferState[ETradeOfferState["CanceledBySecondFactor"] = 10] = "CanceledBySecondFactor";
35
+ ETradeOfferState[ETradeOfferState["InEscrow"] = 11] = "InEscrow";
36
+ /** Trade was reverted by the user (Trade protection update 2025) */
37
+ ETradeOfferState[ETradeOfferState["Reverted"] = 12] = "Reverted";
38
+ return ETradeOfferState;
39
+ }({});
40
+ let ETradeStatus = /* @__PURE__ */ function(ETradeStatus) {
41
+ ETradeStatus[ETradeStatus["Init"] = 0] = "Init";
42
+ ETradeStatus[ETradeStatus["PreCommitted"] = 1] = "PreCommitted";
43
+ ETradeStatus[ETradeStatus["Committed"] = 2] = "Committed";
44
+ ETradeStatus[ETradeStatus["Complete"] = 3] = "Complete";
45
+ ETradeStatus[ETradeStatus["Failed"] = 4] = "Failed";
46
+ ETradeStatus[ETradeStatus["PartialSupportRollback"] = 5] = "PartialSupportRollback";
47
+ ETradeStatus[ETradeStatus["FullSupportRollback"] = 6] = "FullSupportRollback";
48
+ ETradeStatus[ETradeStatus["SupportRollback_Selective"] = 7] = "SupportRollback_Selective";
49
+ ETradeStatus[ETradeStatus["RollbackFailed"] = 8] = "RollbackFailed";
50
+ ETradeStatus[ETradeStatus["RollbackAbandoned"] = 9] = "RollbackAbandoned";
51
+ ETradeStatus[ETradeStatus["InEscrow"] = 10] = "InEscrow";
52
+ ETradeStatus[ETradeStatus["EscrowRollback"] = 11] = "EscrowRollback";
53
+ /** Trade was reverted by the user (Trade protection update 2025) */
54
+ ETradeStatus[ETradeStatus["Reverted"] = 12] = "Reverted";
55
+ return ETradeStatus;
56
+ }({});
57
+ let EConfirmationMethod = /* @__PURE__ */ function(EConfirmationMethod) {
58
+ EConfirmationMethod[EConfirmationMethod["None"] = 0] = "None";
59
+ EConfirmationMethod[EConfirmationMethod["Email"] = 1] = "Email";
60
+ EConfirmationMethod[EConfirmationMethod["MobileApp"] = 2] = "MobileApp";
61
+ return EConfirmationMethod;
62
+ }({});
63
+ let EConfirmationType = /* @__PURE__ */ function(EConfirmationType) {
64
+ EConfirmationType[EConfirmationType["Invalid"] = 0] = "Invalid";
65
+ EConfirmationType[EConfirmationType["Generic"] = 1] = "Generic";
66
+ EConfirmationType[EConfirmationType["Trade"] = 2] = "Trade";
67
+ EConfirmationType[EConfirmationType["MarketListing"] = 3] = "MarketListing";
68
+ EConfirmationType[EConfirmationType["FeatureOptOut"] = 4] = "FeatureOptOut";
69
+ EConfirmationType[EConfirmationType["PhoneNumberChange"] = 5] = "PhoneNumberChange";
70
+ EConfirmationType[EConfirmationType["AccountRecovery"] = 6] = "AccountRecovery";
71
+ return EConfirmationType;
72
+ }({});
73
+ let EOfferFilter = /* @__PURE__ */ function(EOfferFilter) {
74
+ EOfferFilter[EOfferFilter["ActiveOnly"] = 1] = "ActiveOnly";
75
+ EOfferFilter[EOfferFilter["HistoricalOnly"] = 2] = "HistoricalOnly";
76
+ EOfferFilter[EOfferFilter["All"] = 3] = "All";
77
+ return EOfferFilter;
78
+ }({});
79
+ let EAuthTokenPlatformType = /* @__PURE__ */ function(EAuthTokenPlatformType) {
80
+ EAuthTokenPlatformType[EAuthTokenPlatformType["Unknown"] = 0] = "Unknown";
81
+ EAuthTokenPlatformType[EAuthTokenPlatformType["SteamClient"] = 1] = "SteamClient";
82
+ EAuthTokenPlatformType[EAuthTokenPlatformType["WebBrowser"] = 2] = "WebBrowser";
83
+ EAuthTokenPlatformType[EAuthTokenPlatformType["MobileApp"] = 3] = "MobileApp";
84
+ return EAuthTokenPlatformType;
85
+ }({});
86
+ let ESessionPersistence = /* @__PURE__ */ function(ESessionPersistence) {
87
+ ESessionPersistence[ESessionPersistence["Invalid"] = -1] = "Invalid";
88
+ ESessionPersistence[ESessionPersistence["Ephemeral"] = 0] = "Ephemeral";
89
+ ESessionPersistence[ESessionPersistence["Persistent"] = 1] = "Persistent";
90
+ return ESessionPersistence;
91
+ }({});
92
+ let EAuthSessionGuardType = /* @__PURE__ */ function(EAuthSessionGuardType) {
93
+ EAuthSessionGuardType[EAuthSessionGuardType["Unknown"] = 0] = "Unknown";
94
+ EAuthSessionGuardType[EAuthSessionGuardType["None"] = 1] = "None";
95
+ EAuthSessionGuardType[EAuthSessionGuardType["EmailCode"] = 2] = "EmailCode";
96
+ EAuthSessionGuardType[EAuthSessionGuardType["DeviceCode"] = 3] = "DeviceCode";
97
+ EAuthSessionGuardType[EAuthSessionGuardType["DeviceConfirmation"] = 4] = "DeviceConfirmation";
98
+ EAuthSessionGuardType[EAuthSessionGuardType["EmailConfirmation"] = 5] = "EmailConfirmation";
99
+ EAuthSessionGuardType[EAuthSessionGuardType["MachineToken"] = 6] = "MachineToken";
100
+ EAuthSessionGuardType[EAuthSessionGuardType["LegacyMachineAuth"] = 7] = "LegacyMachineAuth";
101
+ return EAuthSessionGuardType;
102
+ }({});
103
+ let EAuthTokenRevokeAction = /* @__PURE__ */ function(EAuthTokenRevokeAction) {
104
+ EAuthTokenRevokeAction[EAuthTokenRevokeAction["Logout"] = 0] = "Logout";
105
+ EAuthTokenRevokeAction[EAuthTokenRevokeAction["Permanent"] = 1] = "Permanent";
106
+ EAuthTokenRevokeAction[EAuthTokenRevokeAction["Replaced"] = 2] = "Replaced";
107
+ EAuthTokenRevokeAction[EAuthTokenRevokeAction["Support"] = 3] = "Support";
108
+ EAuthTokenRevokeAction[EAuthTokenRevokeAction["Consume"] = 4] = "Consume";
109
+ EAuthTokenRevokeAction[EAuthTokenRevokeAction["NonRememberedLogout"] = 5] = "NonRememberedLogout";
110
+ EAuthTokenRevokeAction[EAuthTokenRevokeAction["NonRememberedPermanent"] = 6] = "NonRememberedPermanent";
111
+ EAuthTokenRevokeAction[EAuthTokenRevokeAction["Automatic"] = 7] = "Automatic";
112
+ return EAuthTokenRevokeAction;
113
+ }({});
114
+ let ETokenRenewalType = /* @__PURE__ */ function(ETokenRenewalType) {
115
+ ETokenRenewalType[ETokenRenewalType["None"] = 0] = "None";
116
+ ETokenRenewalType[ETokenRenewalType["Allow"] = 1] = "Allow";
117
+ return ETokenRenewalType;
118
+ }({});
119
+ let EResult = /* @__PURE__ */ function(EResult) {
120
+ EResult[EResult["Invalid"] = 0] = "Invalid";
121
+ EResult[EResult["OK"] = 1] = "OK";
122
+ EResult[EResult["Fail"] = 2] = "Fail";
123
+ EResult[EResult["NoConnection"] = 3] = "NoConnection";
124
+ EResult[EResult["InvalidPassword"] = 5] = "InvalidPassword";
125
+ EResult[EResult["LoggedInElsewhere"] = 6] = "LoggedInElsewhere";
126
+ EResult[EResult["InvalidProtocolVer"] = 7] = "InvalidProtocolVer";
127
+ EResult[EResult["InvalidParam"] = 8] = "InvalidParam";
128
+ EResult[EResult["FileNotFound"] = 9] = "FileNotFound";
129
+ EResult[EResult["Busy"] = 10] = "Busy";
130
+ EResult[EResult["InvalidState"] = 11] = "InvalidState";
131
+ EResult[EResult["InvalidName"] = 12] = "InvalidName";
132
+ EResult[EResult["InvalidEmail"] = 13] = "InvalidEmail";
133
+ EResult[EResult["DuplicateName"] = 14] = "DuplicateName";
134
+ EResult[EResult["AccessDenied"] = 15] = "AccessDenied";
135
+ EResult[EResult["Timeout"] = 16] = "Timeout";
136
+ EResult[EResult["Banned"] = 17] = "Banned";
137
+ EResult[EResult["AccountNotFound"] = 18] = "AccountNotFound";
138
+ EResult[EResult["InvalidSteamID"] = 19] = "InvalidSteamID";
139
+ EResult[EResult["ServiceUnavailable"] = 20] = "ServiceUnavailable";
140
+ EResult[EResult["NotLoggedOn"] = 21] = "NotLoggedOn";
141
+ EResult[EResult["Pending"] = 22] = "Pending";
142
+ EResult[EResult["EncryptionFailure"] = 23] = "EncryptionFailure";
143
+ EResult[EResult["InsufficientPrivilege"] = 24] = "InsufficientPrivilege";
144
+ EResult[EResult["LimitExceeded"] = 25] = "LimitExceeded";
145
+ EResult[EResult["Revoked"] = 26] = "Revoked";
146
+ EResult[EResult["Expired"] = 27] = "Expired";
147
+ EResult[EResult["AlreadyRedeemed"] = 28] = "AlreadyRedeemed";
148
+ EResult[EResult["DuplicateRequest"] = 29] = "DuplicateRequest";
149
+ EResult[EResult["AlreadyOwned"] = 30] = "AlreadyOwned";
150
+ EResult[EResult["IPNotFound"] = 31] = "IPNotFound";
151
+ EResult[EResult["PersistFailed"] = 32] = "PersistFailed";
152
+ EResult[EResult["LockingFailed"] = 33] = "LockingFailed";
153
+ EResult[EResult["LogonSessionReplaced"] = 34] = "LogonSessionReplaced";
154
+ EResult[EResult["ConnectFailed"] = 35] = "ConnectFailed";
155
+ EResult[EResult["HandshakeFailed"] = 36] = "HandshakeFailed";
156
+ EResult[EResult["IOFailure"] = 37] = "IOFailure";
157
+ EResult[EResult["RemoteDisconnect"] = 38] = "RemoteDisconnect";
158
+ EResult[EResult["ShoppingCartNotFound"] = 39] = "ShoppingCartNotFound";
159
+ EResult[EResult["Blocked"] = 40] = "Blocked";
160
+ EResult[EResult["Ignored"] = 41] = "Ignored";
161
+ EResult[EResult["NoMatch"] = 42] = "NoMatch";
162
+ EResult[EResult["AccountDisabled"] = 43] = "AccountDisabled";
163
+ EResult[EResult["ServiceReadOnly"] = 44] = "ServiceReadOnly";
164
+ EResult[EResult["AccountNotFeatured"] = 45] = "AccountNotFeatured";
165
+ EResult[EResult["AdministratorOK"] = 46] = "AdministratorOK";
166
+ EResult[EResult["ContentVersion"] = 47] = "ContentVersion";
167
+ EResult[EResult["TryAnotherCM"] = 48] = "TryAnotherCM";
168
+ EResult[EResult["PasswordRequiredToKickSession"] = 49] = "PasswordRequiredToKickSession";
169
+ EResult[EResult["AlreadyLoggedInElsewhere"] = 50] = "AlreadyLoggedInElsewhere";
170
+ EResult[EResult["Suspended"] = 51] = "Suspended";
171
+ EResult[EResult["Cancelled"] = 52] = "Cancelled";
172
+ EResult[EResult["DataCorruption"] = 53] = "DataCorruption";
173
+ EResult[EResult["DiskFull"] = 54] = "DiskFull";
174
+ EResult[EResult["RemoteCallFailed"] = 55] = "RemoteCallFailed";
175
+ EResult[EResult["PasswordUnset"] = 56] = "PasswordUnset";
176
+ EResult[EResult["ExternalAccountUnlinked"] = 57] = "ExternalAccountUnlinked";
177
+ EResult[EResult["PSNTicketInvalid"] = 58] = "PSNTicketInvalid";
178
+ EResult[EResult["ExternalAccountAlreadyLinked"] = 59] = "ExternalAccountAlreadyLinked";
179
+ EResult[EResult["RemoteFileConflict"] = 60] = "RemoteFileConflict";
180
+ EResult[EResult["IllegalPassword"] = 61] = "IllegalPassword";
181
+ EResult[EResult["SameAsPreviousValue"] = 62] = "SameAsPreviousValue";
182
+ EResult[EResult["AccountLogonDenied"] = 63] = "AccountLogonDenied";
183
+ EResult[EResult["CannotUseOldPassword"] = 64] = "CannotUseOldPassword";
184
+ EResult[EResult["InvalidLoginAuthCode"] = 65] = "InvalidLoginAuthCode";
185
+ EResult[EResult["AccountLogonDeniedNoMail"] = 66] = "AccountLogonDeniedNoMail";
186
+ EResult[EResult["HardwareNotCapableOfIPT"] = 67] = "HardwareNotCapableOfIPT";
187
+ EResult[EResult["IPTInitError"] = 68] = "IPTInitError";
188
+ EResult[EResult["ParentalControlRestricted"] = 69] = "ParentalControlRestricted";
189
+ EResult[EResult["FacebookQueryError"] = 70] = "FacebookQueryError";
190
+ EResult[EResult["ExpiredLoginAuthCode"] = 71] = "ExpiredLoginAuthCode";
191
+ EResult[EResult["IPLoginRestrictionFailed"] = 72] = "IPLoginRestrictionFailed";
192
+ EResult[EResult["AccountLockedDown"] = 73] = "AccountLockedDown";
193
+ EResult[EResult["AccountLogonDeniedVerifiedEmailRequired"] = 74] = "AccountLogonDeniedVerifiedEmailRequired";
194
+ EResult[EResult["NoMatchingURL"] = 75] = "NoMatchingURL";
195
+ EResult[EResult["BadResponse"] = 76] = "BadResponse";
196
+ EResult[EResult["RequirePasswordReEntry"] = 77] = "RequirePasswordReEntry";
197
+ EResult[EResult["ValueOutOfRange"] = 78] = "ValueOutOfRange";
198
+ EResult[EResult["UnexpectedError"] = 79] = "UnexpectedError";
199
+ EResult[EResult["Disabled"] = 80] = "Disabled";
200
+ EResult[EResult["InvalidCEGSubmission"] = 81] = "InvalidCEGSubmission";
201
+ EResult[EResult["RestrictedDevice"] = 82] = "RestrictedDevice";
202
+ EResult[EResult["RegionLocked"] = 83] = "RegionLocked";
203
+ EResult[EResult["RateLimitExceeded"] = 84] = "RateLimitExceeded";
204
+ EResult[EResult["AccountLoginDeniedNeedTwoFactor"] = 85] = "AccountLoginDeniedNeedTwoFactor";
205
+ EResult[EResult["ItemDeleted"] = 86] = "ItemDeleted";
206
+ EResult[EResult["AccountLoginDeniedThrottle"] = 87] = "AccountLoginDeniedThrottle";
207
+ EResult[EResult["TwoFactorCodeMismatch"] = 88] = "TwoFactorCodeMismatch";
208
+ EResult[EResult["TwoFactorActivationCodeMismatch"] = 89] = "TwoFactorActivationCodeMismatch";
209
+ EResult[EResult["AccountAssociatedToMultiplePartners"] = 90] = "AccountAssociatedToMultiplePartners";
210
+ EResult[EResult["NotModified"] = 91] = "NotModified";
211
+ EResult[EResult["NoMobileDevice"] = 92] = "NoMobileDevice";
212
+ EResult[EResult["TimeNotSynced"] = 93] = "TimeNotSynced";
213
+ EResult[EResult["SMSCodeFailed"] = 94] = "SMSCodeFailed";
214
+ EResult[EResult["AccountLimitExceeded"] = 95] = "AccountLimitExceeded";
215
+ EResult[EResult["AccountActivityLimitExceeded"] = 96] = "AccountActivityLimitExceeded";
216
+ EResult[EResult["PhoneActivityLimitExceeded"] = 97] = "PhoneActivityLimitExceeded";
217
+ EResult[EResult["RefundToWallet"] = 98] = "RefundToWallet";
218
+ EResult[EResult["EmailSendFailure"] = 99] = "EmailSendFailure";
219
+ EResult[EResult["NotSettled"] = 100] = "NotSettled";
220
+ EResult[EResult["NeedCaptcha"] = 101] = "NeedCaptcha";
221
+ EResult[EResult["GSLTDenied"] = 102] = "GSLTDenied";
222
+ EResult[EResult["GSOwnerDenied"] = 103] = "GSOwnerDenied";
223
+ EResult[EResult["InvalidItemType"] = 104] = "InvalidItemType";
224
+ EResult[EResult["IPBanned"] = 105] = "IPBanned";
225
+ EResult[EResult["GSLTExpired"] = 106] = "GSLTExpired";
226
+ EResult[EResult["InsufficientFunds"] = 107] = "InsufficientFunds";
227
+ EResult[EResult["TooManyPending"] = 108] = "TooManyPending";
228
+ EResult[EResult["NoSiteLicensesFound"] = 109] = "NoSiteLicensesFound";
229
+ EResult[EResult["WGNetworkSendExceeded"] = 110] = "WGNetworkSendExceeded";
230
+ EResult[EResult["AccountNotFriends"] = 111] = "AccountNotFriends";
231
+ EResult[EResult["LimitedUserAccount"] = 112] = "LimitedUserAccount";
232
+ return EResult;
233
+ }({});
234
+ //#endregion
235
+ //#region src/core/eresults.ts
236
+ const TRANSIENT_ERESULTS = /* @__PURE__ */ new Set([
237
+ 20,
238
+ 10,
239
+ 16,
240
+ 48,
241
+ 55
242
+ ]);
243
+ function isTransientEResult(eresult) {
244
+ return eresult !== void 0 && TRANSIENT_ERESULTS.has(eresult);
245
+ }
246
+ const TERMINAL_AUTH_ERESULTS = /* @__PURE__ */ new Set([
247
+ 5,
248
+ 15,
249
+ 26,
250
+ 27
251
+ ]);
252
+ function isTerminalAuthEResult(eresult) {
253
+ return eresult !== void 0 && TERMINAL_AUTH_ERESULTS.has(eresult);
254
+ }
255
+ //#endregion
256
+ //#region src/core/errors.ts
257
+ const DEFAULT_RATE_LIMIT_RETRY_MS = 6e4;
258
+ var SteamError = class extends Error {
259
+ eresult;
260
+ body;
261
+ constructor(message, options) {
262
+ super(message, options?.cause !== void 0 ? { cause: options.cause } : void 0);
263
+ this.name = "SteamError";
264
+ if (options?.eresult !== void 0) this.eresult = options.eresult;
265
+ if (options?.body !== void 0) this.body = options.body;
266
+ }
267
+ };
268
+ var ProxyError = class extends SteamError {
269
+ constructor(message, options) {
270
+ super(message, options);
271
+ this.name = "ProxyError";
272
+ }
273
+ };
274
+ var HttpStatusError = class extends SteamError {
275
+ statusCode;
276
+ constructor(statusCode, message, body) {
277
+ super(message ?? `HTTP error ${statusCode}`, { body });
278
+ this.name = "HttpStatusError";
279
+ this.statusCode = statusCode;
280
+ }
281
+ };
282
+ var SteamSessionExpiredError = class extends SteamError {
283
+ constructor(message = "Not Logged In") {
284
+ super(message);
285
+ this.name = "SteamSessionExpiredError";
286
+ }
287
+ };
288
+ var OpenIdError = class extends SteamError {
289
+ constructor(message) {
290
+ super(message);
291
+ this.name = "OpenIdError";
292
+ }
293
+ };
294
+ var RateLimitError = class extends SteamError {
295
+ statusCode;
296
+ retryAfterMs;
297
+ unlockAt;
298
+ constructor(options = {}) {
299
+ super(options.message ?? "Rate limit exceeded", {
300
+ ...options.eresult !== void 0 ? { eresult: options.eresult } : {},
301
+ body: options.body
302
+ });
303
+ this.name = "RateLimitError";
304
+ this.statusCode = options.statusCode;
305
+ this.retryAfterMs = options.retryAfterMs ?? 6e4;
306
+ this.unlockAt = Date.now() + this.retryAfterMs;
307
+ }
308
+ };
309
+ var EscrowError = class extends SteamError {
310
+ escrowDays;
311
+ constructor(escrowDays, message) {
312
+ super(message ?? `Trade would be held in escrow for ${escrowDays} days`);
313
+ this.name = "EscrowError";
314
+ this.escrowDays = escrowDays;
315
+ }
316
+ };
317
+ var TradeBanError = class extends SteamError {
318
+ constructor(message = "Trade ban") {
319
+ super(message);
320
+ this.name = "TradeBanError";
321
+ }
322
+ };
323
+ var OfferLimitError = class extends SteamError {
324
+ constructor(message = "Sent too many trade offers") {
325
+ super(message, { eresult: 25 });
326
+ this.name = "OfferLimitError";
327
+ }
328
+ };
329
+ var NewDeviceError = class extends SteamError {
330
+ constructor(message = "You have logged in from a new device", options) {
331
+ super(message, options);
332
+ this.name = "NewDeviceError";
333
+ }
334
+ };
335
+ var TargetCannotTradeError = class extends SteamError {
336
+ constructor(message = "This user is not available to trade", options) {
337
+ super(message, options);
338
+ this.name = "TargetCannotTradeError";
339
+ }
340
+ };
341
+ var ItemServerUnavailableError = class extends SteamError {
342
+ constructor(message = "Steam is currently unable to contact the game's item server", options) {
343
+ super(message, { eresult: options?.eresult ?? 102 });
344
+ this.name = "ItemServerUnavailableError";
345
+ }
346
+ };
347
+ var ConfirmationError = class extends SteamError {
348
+ constructor(message) {
349
+ super(message);
350
+ this.name = "ConfirmationError";
351
+ }
352
+ };
353
+ var FamilyViewError = class extends SteamError {
354
+ constructor(message = "Family View Restricted") {
355
+ super(message);
356
+ this.name = "FamilyViewError";
357
+ }
358
+ };
359
+ var PrivateInventoryError = class extends SteamError {
360
+ constructor(message = "This profile's inventory is private.") {
361
+ super(message);
362
+ this.name = "PrivateInventoryError";
363
+ }
364
+ };
365
+ var LoginError = class extends SteamError {
366
+ extendedErrorMessage;
367
+ isTransient;
368
+ constructor(message, options) {
369
+ super(message, options);
370
+ this.name = "LoginError";
371
+ this.extendedErrorMessage = options?.extendedErrorMessage;
372
+ this.isTransient = isTransientEResult(options?.eresult);
373
+ }
374
+ };
375
+ var NoMobileAuthenticatorError = class extends LoginError {
376
+ constructor(message = "account has no mobile authenticator attached; sharedSecret cannot be used (account uses email Steam Guard)") {
377
+ super(message);
378
+ this.name = "NoMobileAuthenticatorError";
379
+ }
380
+ };
381
+ //#endregion
382
+ //#region src/protobufs/steammessages_auth_pb.ts
383
+ /**
384
+ * Describes the file steammessages_auth.proto.
385
+ */
386
+ const file_steammessages_auth = /*@__PURE__*/ fileDesc("ChhzdGVhbW1lc3NhZ2VzX2F1dGgucHJvdG8iRwovQ0F1dGhlbnRpY2F0aW9uX0dldFBhc3N3b3JkUlNBUHVibGljS2V5X1JlcXVlc3QSFAoMYWNjb3VudF9uYW1lGAEgASgJInMKMENBdXRoZW50aWNhdGlvbl9HZXRQYXNzd29yZFJTQVB1YmxpY0tleV9SZXNwb25zZRIVCg1wdWJsaWNrZXlfbW9kGAEgASgJEhUKDXB1YmxpY2tleV9leHAYAiABKAkSEQoJdGltZXN0YW1wGAMgASgEIqkCCh1DQXV0aGVudGljYXRpb25fRGV2aWNlRGV0YWlscxIcChRkZXZpY2VfZnJpZW5kbHlfbmFtZRgBIAEoCRJQCg1wbGF0Zm9ybV90eXBlGAIgASgOMhcuRUF1dGhUb2tlblBsYXRmb3JtVHlwZToga19FQXV0aFRva2VuUGxhdGZvcm1UeXBlX1Vua25vd24SDwoHb3NfdHlwZRgDIAEoBRIaChJnYW1pbmdfZGV2aWNlX3R5cGUYBCABKA0SFAoMY2xpZW50X2NvdW50GAUgASgNEhIKCm1hY2hpbmVfaWQYBiABKAwSQQoIYXBwX3R5cGUYByABKA4yEi5FQXV0aFRva2VuQXBwVHlwZToba19FQXV0aFRva2VuQXBwVHlwZV9Vbmtub3duIpUBCiNDQXV0aGVudGljYXRpb25fQWxsb3dlZENvbmZpcm1hdGlvbhJSChFjb25maXJtYXRpb25fdHlwZRgBIAEoDjIWLkVBdXRoU2Vzc2lvbkd1YXJkVHlwZTofa19FQXV0aFNlc3Npb25HdWFyZFR5cGVfVW5rbm93bhIaChJhc3NvY2lhdGVkX21lc3NhZ2UYAiABKAki7gMKNkNBdXRoZW50aWNhdGlvbl9CZWdpbkF1dGhTZXNzaW9uVmlhQ3JlZGVudGlhbHNfUmVxdWVzdBIcChRkZXZpY2VfZnJpZW5kbHlfbmFtZRgBIAEoCRIUCgxhY2NvdW50X25hbWUYAiABKAkSGgoSZW5jcnlwdGVkX3Bhc3N3b3JkGAMgASgJEhwKFGVuY3J5cHRpb25fdGltZXN0YW1wGAQgASgEEhYKDnJlbWVtYmVyX2xvZ2luGAUgASgIElAKDXBsYXRmb3JtX3R5cGUYBiABKA4yFy5FQXV0aFRva2VuUGxhdGZvcm1UeXBlOiBrX0VBdXRoVG9rZW5QbGF0Zm9ybVR5cGVfVW5rbm93bhJLCgtwZXJzaXN0ZW5jZRgHIAEoDjIULkVTZXNzaW9uUGVyc2lzdGVuY2U6IGtfRVNlc3Npb25QZXJzaXN0ZW5jZV9QZXJzaXN0ZW50EhsKCndlYnNpdGVfaWQYCCABKAk6B1Vua25vd24SNgoOZGV2aWNlX2RldGFpbHMYCSABKAsyHi5DQXV0aGVudGljYXRpb25fRGV2aWNlRGV0YWlscxISCgpndWFyZF9kYXRhGAogASgJEhAKCGxhbmd1YWdlGAsgASgNEhQKCXFvc19sZXZlbBgMIAEoBToBMiKbAgo3Q0F1dGhlbnRpY2F0aW9uX0JlZ2luQXV0aFNlc3Npb25WaWFDcmVkZW50aWFsc19SZXNwb25zZRIRCgljbGllbnRfaWQYASABKAQSEgoKcmVxdWVzdF9pZBgCIAEoDBIQCghpbnRlcnZhbBgDIAEoAhJDChVhbGxvd2VkX2NvbmZpcm1hdGlvbnMYBCADKAsyJC5DQXV0aGVudGljYXRpb25fQWxsb3dlZENvbmZpcm1hdGlvbhIPCgdzdGVhbWlkGAUgASgEEhIKCndlYWtfdG9rZW4YBiABKAkSHQoVYWdyZWVtZW50X3Nlc3Npb25fdXJsGAcgASgJEh4KFmV4dGVuZGVkX2Vycm9yX21lc3NhZ2UYCCABKAkibwotQ0F1dGhlbnRpY2F0aW9uX1BvbGxBdXRoU2Vzc2lvblN0YXR1c19SZXF1ZXN0EhEKCWNsaWVudF9pZBgBIAEoBBISCgpyZXF1ZXN0X2lkGAIgASgMEhcKD3Rva2VuX3RvX3Jldm9rZRgDIAEoBiL8AQouQ0F1dGhlbnRpY2F0aW9uX1BvbGxBdXRoU2Vzc2lvblN0YXR1c19SZXNwb25zZRIVCg1uZXdfY2xpZW50X2lkGAEgASgEEhkKEW5ld19jaGFsbGVuZ2VfdXJsGAIgASgJEhUKDXJlZnJlc2hfdG9rZW4YAyABKAkSFAoMYWNjZXNzX3Rva2VuGAQgASgJEh4KFmhhZF9yZW1vdGVfaW50ZXJhY3Rpb24YBSABKAgSFAoMYWNjb3VudF9uYW1lGAYgASgJEhYKDm5ld19ndWFyZF9kYXRhGAcgASgJEh0KFWFncmVlbWVudF9zZXNzaW9uX3VybBgIIAEoCSK7AQo7Q0F1dGhlbnRpY2F0aW9uX1VwZGF0ZUF1dGhTZXNzaW9uV2l0aFN0ZWFtR3VhcmRDb2RlX1JlcXVlc3QSEQoJY2xpZW50X2lkGAEgASgEEg8KB3N0ZWFtaWQYAiABKAYSDAoEY29kZRgDIAEoCRJKCgljb2RlX3R5cGUYBCABKA4yFi5FQXV0aFNlc3Npb25HdWFyZFR5cGU6H2tfRUF1dGhTZXNzaW9uR3VhcmRUeXBlX1Vua25vd24iXQo8Q0F1dGhlbnRpY2F0aW9uX1VwZGF0ZUF1dGhTZXNzaW9uV2l0aFN0ZWFtR3VhcmRDb2RlX1Jlc3BvbnNlEh0KFWFncmVlbWVudF9zZXNzaW9uX3VybBgHIAEoCSKgAQoyQ0F1dGhlbnRpY2F0aW9uX0FjY2Vzc1Rva2VuX0dlbmVyYXRlRm9yQXBwX1JlcXVlc3QSFQoNcmVmcmVzaF90b2tlbhgBIAEoCRIPCgdzdGVhbWlkGAIgASgGEkIKDHJlbmV3YWxfdHlwZRgDIAEoDjISLkVUb2tlblJlbmV3YWxUeXBlOhhrX0VUb2tlblJlbmV3YWxUeXBlX05vbmUiYgozQ0F1dGhlbnRpY2F0aW9uX0FjY2Vzc1Rva2VuX0dlbmVyYXRlRm9yQXBwX1Jlc3BvbnNlEhQKDGFjY2Vzc190b2tlbhgBIAEoCRIVCg1yZWZyZXNoX3Rva2VuGAIgASgJIjMKMUNBdXRoZW50aWNhdGlvbl9HZXRBdXRoU2Vzc2lvbnNGb3JBY2NvdW50X1JlcXVlc3QiSAoyQ0F1dGhlbnRpY2F0aW9uX0dldEF1dGhTZXNzaW9uc0ZvckFjY291bnRfUmVzcG9uc2USEgoKY2xpZW50X2lkcxgBIAMoBCKwAQorQ0F1dGhlbnRpY2F0aW9uX1JlZnJlc2hUb2tlbl9SZXZva2VfUmVxdWVzdBIQCgh0b2tlbl9pZBgBIAEoBhIPCgdzdGVhbWlkGAIgASgGEksKDXJldm9rZV9hY3Rpb24YAyABKA4yFy5FQXV0aFRva2VuUmV2b2tlQWN0aW9uOhtrX0VBdXRoVG9rZW5SZXZva2VQZXJtYW5lbnQSEQoJc2lnbmF0dXJlGAQgASgMIi4KLENBdXRoZW50aWNhdGlvbl9SZWZyZXNoVG9rZW5fUmV2b2tlX1Jlc3BvbnNlKrkBChZFQXV0aFRva2VuUGxhdGZvcm1UeXBlEiQKIGtfRUF1dGhUb2tlblBsYXRmb3JtVHlwZV9Vbmtub3duEAASKAoka19FQXV0aFRva2VuUGxhdGZvcm1UeXBlX1N0ZWFtQ2xpZW50EAESJwoja19FQXV0aFRva2VuUGxhdGZvcm1UeXBlX1dlYkJyb3dzZXIQAhImCiJrX0VBdXRoVG9rZW5QbGF0Zm9ybVR5cGVfTW9iaWxlQXBwEAMqhQEKEUVBdXRoVG9rZW5BcHBUeXBlEh8KG2tfRUF1dGhUb2tlbkFwcFR5cGVfVW5rbm93bhAAEicKI2tfRUF1dGhUb2tlbkFwcFR5cGVfTW9iaWxlX1N0ZWFtQXBwEAESJgoia19FQXV0aFRva2VuQXBwVHlwZV9Nb2JpbGVfQ2hhdEFwcBACKuUCChVFQXV0aFNlc3Npb25HdWFyZFR5cGUSIwofa19FQXV0aFNlc3Npb25HdWFyZFR5cGVfVW5rbm93bhAAEiAKHGtfRUF1dGhTZXNzaW9uR3VhcmRUeXBlX05vbmUQARIlCiFrX0VBdXRoU2Vzc2lvbkd1YXJkVHlwZV9FbWFpbENvZGUQAhImCiJrX0VBdXRoU2Vzc2lvbkd1YXJkVHlwZV9EZXZpY2VDb2RlEAMSLgoqa19FQXV0aFNlc3Npb25HdWFyZFR5cGVfRGV2aWNlQ29uZmlybWF0aW9uEAQSLQopa19FQXV0aFNlc3Npb25HdWFyZFR5cGVfRW1haWxDb25maXJtYXRpb24QBRIoCiRrX0VBdXRoU2Vzc2lvbkd1YXJkVHlwZV9NYWNoaW5lVG9rZW4QBhItCilrX0VBdXRoU2Vzc2lvbkd1YXJkVHlwZV9MZWdhY3lNYWNoaW5lQXV0aBAHKlAKEUVUb2tlblJlbmV3YWxUeXBlEhwKGGtfRVRva2VuUmVuZXdhbFR5cGVfTm9uZRAAEh0KGWtfRVRva2VuUmVuZXdhbFR5cGVfQWxsb3cQASqvAgoWRUF1dGhUb2tlblJldm9rZUFjdGlvbhIcChhrX0VBdXRoVG9rZW5SZXZva2VMb2dvdXQQABIfChtrX0VBdXRoVG9rZW5SZXZva2VQZXJtYW5lbnQQARIeChprX0VBdXRoVG9rZW5SZXZva2VSZXBsYWNlZBACEh0KGWtfRUF1dGhUb2tlblJldm9rZVN1cHBvcnQQAxIdChlrX0VBdXRoVG9rZW5SZXZva2VDb25zdW1lEAQSKQola19FQXV0aFRva2VuUmV2b2tlTm9uUmVtZW1iZXJlZExvZ291dBAFEiwKKGtfRUF1dGhUb2tlblJldm9rZU5vblJlbWVtYmVyZWRQZXJtYW5lbnQQBhIfChtrX0VBdXRoVG9rZW5SZXZva2VBdXRvbWF0aWMQByqMAQoTRVNlc3Npb25QZXJzaXN0ZW5jZRIqCh1rX0VTZXNzaW9uUGVyc2lzdGVuY2VfSW52YWxpZBD///////////8BEiMKH2tfRVNlc3Npb25QZXJzaXN0ZW5jZV9FcGhlbWVyYWwQABIkCiBrX0VTZXNzaW9uUGVyc2lzdGVuY2VfUGVyc2lzdGVudBAB");
387
+ /**
388
+ * Describes the message CAuthentication_GetPasswordRSAPublicKey_Request.
389
+ * Use `create(CAuthentication_GetPasswordRSAPublicKey_RequestSchema)` to create a new message.
390
+ */
391
+ const CAuthentication_GetPasswordRSAPublicKey_RequestSchema = /*@__PURE__*/ messageDesc(file_steammessages_auth, 0);
392
+ /**
393
+ * Describes the message CAuthentication_GetPasswordRSAPublicKey_Response.
394
+ * Use `create(CAuthentication_GetPasswordRSAPublicKey_ResponseSchema)` to create a new message.
395
+ */
396
+ const CAuthentication_GetPasswordRSAPublicKey_ResponseSchema = /*@__PURE__*/ messageDesc(file_steammessages_auth, 1);
397
+ /**
398
+ * Describes the message CAuthentication_DeviceDetails.
399
+ * Use `create(CAuthentication_DeviceDetailsSchema)` to create a new message.
400
+ */
401
+ const CAuthentication_DeviceDetailsSchema = /*@__PURE__*/ messageDesc(file_steammessages_auth, 2);
402
+ /**
403
+ * Describes the message CAuthentication_BeginAuthSessionViaCredentials_Request.
404
+ * Use `create(CAuthentication_BeginAuthSessionViaCredentials_RequestSchema)` to create a new message.
405
+ */
406
+ const CAuthentication_BeginAuthSessionViaCredentials_RequestSchema = /*@__PURE__*/ messageDesc(file_steammessages_auth, 4);
407
+ /**
408
+ * Describes the message CAuthentication_BeginAuthSessionViaCredentials_Response.
409
+ * Use `create(CAuthentication_BeginAuthSessionViaCredentials_ResponseSchema)` to create a new message.
410
+ */
411
+ const CAuthentication_BeginAuthSessionViaCredentials_ResponseSchema = /*@__PURE__*/ messageDesc(file_steammessages_auth, 5);
412
+ /**
413
+ * Describes the message CAuthentication_PollAuthSessionStatus_Request.
414
+ * Use `create(CAuthentication_PollAuthSessionStatus_RequestSchema)` to create a new message.
415
+ */
416
+ const CAuthentication_PollAuthSessionStatus_RequestSchema = /*@__PURE__*/ messageDesc(file_steammessages_auth, 6);
417
+ /**
418
+ * Describes the message CAuthentication_PollAuthSessionStatus_Response.
419
+ * Use `create(CAuthentication_PollAuthSessionStatus_ResponseSchema)` to create a new message.
420
+ */
421
+ const CAuthentication_PollAuthSessionStatus_ResponseSchema = /*@__PURE__*/ messageDesc(file_steammessages_auth, 7);
422
+ /**
423
+ * Describes the message CAuthentication_UpdateAuthSessionWithSteamGuardCode_Request.
424
+ * Use `create(CAuthentication_UpdateAuthSessionWithSteamGuardCode_RequestSchema)` to create a new message.
425
+ */
426
+ const CAuthentication_UpdateAuthSessionWithSteamGuardCode_RequestSchema = /*@__PURE__*/ messageDesc(file_steammessages_auth, 8);
427
+ /**
428
+ * Describes the message CAuthentication_UpdateAuthSessionWithSteamGuardCode_Response.
429
+ * Use `create(CAuthentication_UpdateAuthSessionWithSteamGuardCode_ResponseSchema)` to create a new message.
430
+ */
431
+ const CAuthentication_UpdateAuthSessionWithSteamGuardCode_ResponseSchema = /*@__PURE__*/ messageDesc(file_steammessages_auth, 9);
432
+ /**
433
+ * Describes the message CAuthentication_AccessToken_GenerateForApp_Request.
434
+ * Use `create(CAuthentication_AccessToken_GenerateForApp_RequestSchema)` to create a new message.
435
+ */
436
+ const CAuthentication_AccessToken_GenerateForApp_RequestSchema = /*@__PURE__*/ messageDesc(file_steammessages_auth, 10);
437
+ /**
438
+ * Describes the message CAuthentication_AccessToken_GenerateForApp_Response.
439
+ * Use `create(CAuthentication_AccessToken_GenerateForApp_ResponseSchema)` to create a new message.
440
+ */
441
+ const CAuthentication_AccessToken_GenerateForApp_ResponseSchema = /*@__PURE__*/ messageDesc(file_steammessages_auth, 11);
442
+ /**
443
+ * Describes the message CAuthentication_GetAuthSessionsForAccount_Request.
444
+ * Use `create(CAuthentication_GetAuthSessionsForAccount_RequestSchema)` to create a new message.
445
+ */
446
+ const CAuthentication_GetAuthSessionsForAccount_RequestSchema = /*@__PURE__*/ messageDesc(file_steammessages_auth, 12);
447
+ /**
448
+ * Describes the message CAuthentication_GetAuthSessionsForAccount_Response.
449
+ * Use `create(CAuthentication_GetAuthSessionsForAccount_ResponseSchema)` to create a new message.
450
+ */
451
+ const CAuthentication_GetAuthSessionsForAccount_ResponseSchema = /*@__PURE__*/ messageDesc(file_steammessages_auth, 13);
452
+ /**
453
+ * Describes the message CAuthentication_RefreshToken_Revoke_Request.
454
+ * Use `create(CAuthentication_RefreshToken_Revoke_RequestSchema)` to create a new message.
455
+ */
456
+ const CAuthentication_RefreshToken_Revoke_RequestSchema = /*@__PURE__*/ messageDesc(file_steammessages_auth, 14);
457
+ /**
458
+ * Describes the message CAuthentication_RefreshToken_Revoke_Response.
459
+ * Use `create(CAuthentication_RefreshToken_Revoke_ResponseSchema)` to create a new message.
460
+ */
461
+ const CAuthentication_RefreshToken_Revoke_ResponseSchema = /*@__PURE__*/ messageDesc(file_steammessages_auth, 15);
462
+ //#endregion
463
+ //#region src/session/protoTransport.ts
464
+ function createProtoTransport(http) {
465
+ return async (req) => {
466
+ const base64 = req.body && req.body.length > 0 ? Buffer$1.from(req.body).toString("base64") : "";
467
+ const searchParams = {};
468
+ if (req.accessToken) {
469
+ searchParams.access_token = req.accessToken;
470
+ searchParams.spoof_steamid = req.spoofSteamid ?? "";
471
+ }
472
+ if (req.origin) searchParams.origin = "SteamMobile";
473
+ let res;
474
+ if (req.method === "GET") {
475
+ searchParams.input_protobuf_encoded = base64;
476
+ res = await http.get(req.url, {
477
+ responseType: "buffer",
478
+ searchParams
479
+ });
480
+ } else {
481
+ const bodyOpt = req.multipart ? { multipart: [{
482
+ name: "input_protobuf_encoded",
483
+ value: base64
484
+ }] } : { form: { input_protobuf_encoded: base64 } };
485
+ res = await http.post(req.url, {
486
+ responseType: "buffer",
487
+ ...Object.keys(searchParams).length > 0 ? { searchParams } : {},
488
+ ...bodyOpt
489
+ });
490
+ }
491
+ return {
492
+ status: res.statusCode,
493
+ eresult: headerValue$1(res.headers["x-eresult"]) ?? null,
494
+ errorMessage: headerValue$1(res.headers["x-error_message"]) ?? null,
495
+ body: res.body
496
+ };
497
+ };
498
+ }
499
+ function createProtoPost(http) {
500
+ const transport = createProtoTransport(http);
501
+ return (url, base64Body, accessToken) => transport({
502
+ url,
503
+ method: "POST",
504
+ body: Buffer$1.from(base64Body, "base64"),
505
+ ...accessToken ? { accessToken } : {}
506
+ });
507
+ }
508
+ function headerValue$1(v) {
509
+ return Array.isArray(v) ? v[0] : v;
510
+ }
511
+ //#endregion
512
+ //#region src/auth/AuthClient.ts
513
+ var AuthClient = class {
514
+ transport;
515
+ constructor(http) {
516
+ this.transport = createProtoTransport(http);
517
+ }
518
+ async getPasswordRSAPublicKey(accountName) {
519
+ const body = toBinary(CAuthentication_GetPasswordRSAPublicKey_RequestSchema, create(CAuthentication_GetPasswordRSAPublicKey_RequestSchema, { accountName }));
520
+ return fromBinary(CAuthentication_GetPasswordRSAPublicKey_ResponseSchema, await this.send({
521
+ apiMethod: "GetPasswordRSAPublicKey",
522
+ method: "GET",
523
+ body,
524
+ origin: true
525
+ }));
526
+ }
527
+ async beginAuthSessionViaCredentials(req) {
528
+ const body = toBinary(CAuthentication_BeginAuthSessionViaCredentials_RequestSchema, create(CAuthentication_BeginAuthSessionViaCredentials_RequestSchema, req));
529
+ return fromBinary(CAuthentication_BeginAuthSessionViaCredentials_ResponseSchema, await this.send({
530
+ apiMethod: "BeginAuthSessionViaCredentials",
531
+ method: "POST",
532
+ body,
533
+ multipart: true
534
+ }));
535
+ }
536
+ async pollAuthSessionStatus(clientId, requestId) {
537
+ const body = toBinary(CAuthentication_PollAuthSessionStatus_RequestSchema, create(CAuthentication_PollAuthSessionStatus_RequestSchema, {
538
+ clientId,
539
+ requestId
540
+ }));
541
+ return fromBinary(CAuthentication_PollAuthSessionStatus_ResponseSchema, await this.send({
542
+ apiMethod: "PollAuthSessionStatus",
543
+ method: "POST",
544
+ body,
545
+ multipart: true
546
+ }));
547
+ }
548
+ async updateAuthSessionWithSteamGuardCode(clientId, steamid, code, codeType) {
549
+ const body = toBinary(CAuthentication_UpdateAuthSessionWithSteamGuardCode_RequestSchema, create(CAuthentication_UpdateAuthSessionWithSteamGuardCode_RequestSchema, {
550
+ clientId,
551
+ steamid,
552
+ code,
553
+ codeType
554
+ }));
555
+ return fromBinary(CAuthentication_UpdateAuthSessionWithSteamGuardCode_ResponseSchema, await this.send({
556
+ apiMethod: "UpdateAuthSessionWithSteamGuardCode",
557
+ method: "POST",
558
+ body,
559
+ multipart: true
560
+ }));
561
+ }
562
+ async getAuthSessionsForAccount(accessToken) {
563
+ const body = toBinary(CAuthentication_GetAuthSessionsForAccount_RequestSchema, create(CAuthentication_GetAuthSessionsForAccount_RequestSchema, {}));
564
+ return fromBinary(CAuthentication_GetAuthSessionsForAccount_ResponseSchema, await this.send({
565
+ apiMethod: "GetAuthSessionsForAccount",
566
+ method: "GET",
567
+ body,
568
+ accessToken,
569
+ origin: true
570
+ }));
571
+ }
572
+ async revokeRefreshToken(accessToken, revokeAction) {
573
+ const body = toBinary(CAuthentication_RefreshToken_Revoke_RequestSchema, create(CAuthentication_RefreshToken_Revoke_RequestSchema, { revokeAction }));
574
+ return fromBinary(CAuthentication_RefreshToken_Revoke_ResponseSchema, await this.send({
575
+ apiMethod: "RevokeRefreshToken",
576
+ method: "POST",
577
+ body,
578
+ accessToken,
579
+ multipart: true
580
+ }));
581
+ }
582
+ async send(opts) {
583
+ const url = `${URLS.api}/IAuthenticationService/${opts.apiMethod}/v1`;
584
+ const res = await this.transport({
585
+ url,
586
+ method: opts.method,
587
+ ...opts.body ? { body: opts.body } : {},
588
+ ...opts.accessToken ? { accessToken: opts.accessToken } : {},
589
+ ...opts.origin ? { origin: true } : {},
590
+ ...opts.multipart ? { multipart: true } : {}
591
+ });
592
+ ensureOk(res, opts.apiMethod);
593
+ return toUint8$1(res.body);
594
+ }
595
+ };
596
+ function ensureOk(res, apiMethod) {
597
+ if (res.status === 429) throw new RateLimitError({
598
+ message: `IAuthenticationService/${apiMethod} HTTP 429`,
599
+ ...res.eresult ? { eresult: Number(res.eresult) } : {}
600
+ });
601
+ if (res.status < 200 || res.status >= 300) throw new LoginError(`IAuthenticationService/${apiMethod} HTTP ${res.status}`, {
602
+ ...res.eresult ? { eresult: Number(res.eresult) } : {},
603
+ ...res.errorMessage ? { extendedErrorMessage: res.errorMessage } : {}
604
+ });
605
+ const er = res.eresult;
606
+ if (er && er !== "1") {
607
+ const code = Number(er);
608
+ if (code === 2 && res.body.length > 0) return;
609
+ const msg = `${apiMethod}: ${EResult[code] ?? "EResult"} (${er})${res.errorMessage ? ` ${res.errorMessage}` : ""}`;
610
+ if (code === 84) throw new RateLimitError({
611
+ message: msg,
612
+ eresult: code
613
+ });
614
+ throw new LoginError(msg, {
615
+ eresult: code,
616
+ ...res.errorMessage ? { extendedErrorMessage: res.errorMessage } : {}
617
+ });
618
+ }
619
+ }
620
+ function toUint8$1(input) {
621
+ return input instanceof Uint8Array && !Buffer$1.isBuffer(input) ? input : new Uint8Array(input);
622
+ }
623
+ //#endregion
624
+ //#region src/crypto/rsa.ts
625
+ function encryptPassword(password, modHex, expHex) {
626
+ return publicEncrypt({
627
+ key: createPublicKey({
628
+ key: {
629
+ kty: "RSA",
630
+ n: Buffer$1.from(modHex, "hex").toString("base64url"),
631
+ e: Buffer$1.from(expHex, "hex").toString("base64url")
632
+ },
633
+ format: "jwk"
634
+ }),
635
+ padding: constants.RSA_PKCS1_PADDING
636
+ }, Buffer$1.from(password, "utf8")).toString("base64");
637
+ }
638
+ //#endregion
639
+ //#region src/crypto/steamTotp.ts
640
+ const CODE_CHARS = "23456789BCDFGHJKMNPQRTVWXY";
641
+ function time(timeOffset = 0) {
642
+ return Math.floor(Date.now() / 1e3) + timeOffset;
643
+ }
644
+ function getAuthCode(secret, timeOffset = 0) {
645
+ const key = bufferizeSecret(secret);
646
+ const buffer = Buffer$1.allocUnsafe(8);
647
+ buffer.writeUInt32BE(0, 0);
648
+ buffer.writeUInt32BE(Math.floor(time(timeOffset) / 30), 4);
649
+ const hmac = createHmac("sha1", key).update(buffer).digest();
650
+ const start = (hmac[19] ?? 0) & 15;
651
+ let fullcode = hmac.subarray(start, start + 4).readUInt32BE(0) & 2147483647;
652
+ let code = "";
653
+ for (let i = 0; i < 5; i++) {
654
+ code += CODE_CHARS.charAt(fullcode % 26);
655
+ fullcode /= 26;
656
+ }
657
+ return code;
658
+ }
659
+ function getConfirmationKey(identitySecret, t, tag) {
660
+ const key = bufferizeSecret(identitySecret);
661
+ const dataLen = 8 + (tag ? Math.min(tag.length, 32) : 0);
662
+ const buffer = Buffer$1.allocUnsafe(dataLen);
663
+ buffer.writeBigUInt64BE(BigInt(t), 0);
664
+ if (tag) buffer.write(tag, 8);
665
+ return createHmac("sha1", key).update(buffer).digest("base64");
666
+ }
667
+ function getDeviceID(steamID) {
668
+ const salt = process.env.STEAM_TOTP_SALT ?? "";
669
+ return `android:${createHash("sha1").update(steamID + salt).digest("hex").replace(/^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12}).*$/, "$1-$2-$3-$4-$5")}`;
670
+ }
671
+ function bufferizeSecret(secret) {
672
+ if (typeof secret !== "string") return secret;
673
+ return /[0-9a-f]{40}/i.test(secret) ? Buffer$1.from(secret, "hex") : Buffer$1.from(secret, "base64");
674
+ }
675
+ //#endregion
676
+ //#region src/auth/deviceDetails.ts
677
+ function buildDeviceDetails(profile) {
678
+ return create(CAuthentication_DeviceDetailsSchema, {
679
+ deviceFriendlyName: profile.deviceFriendlyName,
680
+ platformType: 3,
681
+ osType: profile.osType,
682
+ gamingDeviceType: profile.gamingDeviceType,
683
+ ...profile.appType !== void 0 ? { appType: profile.appType } : {}
684
+ });
685
+ }
686
+ //#endregion
687
+ //#region src/auth/CredentialSession.ts
688
+ const DEFAULT_POLL_TIMEOUT_MS = 18e4;
689
+ const DEFAULT_POLL_INTERVAL_MS = 5e3;
690
+ var CredentialSession = class extends EventEmitter {
691
+ profile;
692
+ pollTimeoutMs;
693
+ auth;
694
+ clientId = 0n;
695
+ requestId = /* @__PURE__ */ new Uint8Array();
696
+ pollIntervalMs = DEFAULT_POLL_INTERVAL_MS;
697
+ allowedConfirmations = [];
698
+ deadline = 0;
699
+ pollTimer;
700
+ settled = false;
701
+ remoteInteractionEmitted = false;
702
+ steamID;
703
+ username = "";
704
+ accessToken;
705
+ refreshToken;
706
+ constructor(http, profile, pollTimeoutMs = DEFAULT_POLL_TIMEOUT_MS) {
707
+ super();
708
+ this.profile = profile;
709
+ this.pollTimeoutMs = pollTimeoutMs;
710
+ this.auth = new AuthClient(http);
711
+ }
712
+ async start(opts) {
713
+ this.username = opts.username;
714
+ const rsa = await this.auth.getPasswordRSAPublicKey(opts.username);
715
+ const encryptedPassword = encryptPassword(opts.password, rsa.publickeyMod, rsa.publickeyExp);
716
+ const begin = await this.auth.beginAuthSessionViaCredentials({
717
+ accountName: opts.username,
718
+ encryptedPassword,
719
+ encryptionTimestamp: rsa.timestamp,
720
+ rememberLogin: true,
721
+ persistence: 1,
722
+ websiteId: "Mobile",
723
+ deviceDetails: buildDeviceDetails(this.profile),
724
+ language: 0
725
+ });
726
+ if (begin.requestId.length === 0) throw new LoginError(begin.extendedErrorMessage || "login was rejected by Steam", { ...begin.extendedErrorMessage ? { extendedErrorMessage: begin.extendedErrorMessage } : {} });
727
+ this.clientId = begin.clientId;
728
+ this.requestId = begin.requestId;
729
+ if (begin.steamid) this.steamID = new SteamID(begin.steamid.toString());
730
+ if (begin.interval > 0) this.pollIntervalMs = Math.round(begin.interval * 1e3);
731
+ this.allowedConfirmations = begin.allowedConfirmations.map((c) => Number(c.confirmationType));
732
+ this.emit("debug", `begin OK: confirmations=[${this.allowedConfirmations.join(",")}]`);
733
+ await this.answerConfirmations(opts);
734
+ this.deadline = Date.now() + this.pollTimeoutMs;
735
+ this.schedulePoll();
736
+ }
737
+ async submitSteamGuardCode(code) {
738
+ const trimmed = code?.trim();
739
+ if (!trimmed) throw new LoginError("a Steam Guard code is required");
740
+ const codeType = this.allowedConfirmations.includes(3) ? 3 : 2;
741
+ await this.submitCode(trimmed, codeType);
742
+ }
743
+ stop() {
744
+ this.settled = true;
745
+ if (this.pollTimer) clearTimeout(this.pollTimer);
746
+ this.pollTimer = void 0;
747
+ }
748
+ async answerConfirmations(opts) {
749
+ const allowed = this.allowedConfirmations;
750
+ if (allowed.includes(3) && opts.sharedSecret) {
751
+ const code = getAuthCode(opts.sharedSecret);
752
+ this.emit("debug", "answering DeviceCode with generated TOTP");
753
+ await this.submitCode(code, 3);
754
+ return;
755
+ }
756
+ if (opts.sharedSecret && !allowed.includes(3)) throw new NoMobileAuthenticatorError();
757
+ if (opts.steamGuardCode) {
758
+ const codeType = allowed.includes(3) ? 3 : 2;
759
+ await this.submitCode(opts.steamGuardCode, codeType);
760
+ return;
761
+ }
762
+ if (allowed.includes(4) || allowed.includes(5)) {
763
+ this.emitRemoteInteraction();
764
+ return;
765
+ }
766
+ const codeGuard = allowed.find((t) => t === 2 || t === 3);
767
+ if (codeGuard !== void 0) this.emit("steamGuardRequired", {
768
+ type: codeGuard,
769
+ message: codeGuard === 2 ? "an email Steam Guard code is required" : "a device (TOTP) Steam Guard code is required"
770
+ });
771
+ }
772
+ async submitCode(code, codeType) {
773
+ if (!this.steamID) throw new LoginError("cannot submit Steam Guard code before begin");
774
+ await this.auth.updateAuthSessionWithSteamGuardCode(this.clientId, BigInt(this.steamID.getSteamID64()), code, codeType);
775
+ }
776
+ schedulePoll() {
777
+ if (this.settled) return;
778
+ this.pollTimer = setTimeout(() => {
779
+ this.poll().catch((err) => this.fail(err instanceof Error ? err : new Error(String(err))));
780
+ }, this.pollIntervalMs);
781
+ }
782
+ async poll() {
783
+ if (this.settled) return;
784
+ let res;
785
+ try {
786
+ res = await this.auth.pollAuthSessionStatus(this.clientId, this.requestId);
787
+ } catch (err) {
788
+ if (Date.now() >= this.deadline) {
789
+ this.stop();
790
+ this.emit("timeout");
791
+ return;
792
+ }
793
+ this.emit("debug", `poll error, retrying: ${err.message}`);
794
+ this.schedulePoll();
795
+ return;
796
+ }
797
+ if (res.hadRemoteInteraction) this.emitRemoteInteraction();
798
+ if (res.newClientId) this.clientId = res.newClientId;
799
+ if (res.accessToken) this.accessToken = res.accessToken;
800
+ if (res.refreshToken) {
801
+ this.refreshToken = res.refreshToken;
802
+ if (res.accountName) this.username = res.accountName;
803
+ this.stop();
804
+ this.emit("authenticated");
805
+ return;
806
+ }
807
+ if (Date.now() >= this.deadline) {
808
+ this.stop();
809
+ this.emit("timeout");
810
+ return;
811
+ }
812
+ this.schedulePoll();
813
+ }
814
+ emitRemoteInteraction() {
815
+ if (this.remoteInteractionEmitted) return;
816
+ this.remoteInteractionEmitted = true;
817
+ this.emit("remoteInteraction");
818
+ }
819
+ fail(error) {
820
+ if (this.settled) return;
821
+ this.stop();
822
+ this.emit("error", error);
823
+ }
824
+ };
825
+ //#endregion
826
+ //#region src/core/mobileProfile.ts
827
+ const IOS_PROFILE = {
828
+ mobileClient: "ios",
829
+ mobileClientVersion: "777777 3.10.9",
830
+ apiUserAgent: "Steam%20Mobile/10472498 CFNetwork/3860.600.12 Darwin/25.5.0",
831
+ webUserAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X; Valve Steam App Version/3) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",
832
+ deviceFriendlyName: "iPhone18,3",
833
+ osType: -600,
834
+ gamingDeviceType: 528,
835
+ appType: 1
836
+ };
837
+ const ANDROID_PROFILE = {
838
+ mobileClient: "android",
839
+ mobileClientVersion: "777777 3.10.3",
840
+ apiUserAgent: "okhttp/4.9.2",
841
+ webUserAgent: "Mozilla/5.0 (Linux; Android 14; SM-S921B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36",
842
+ deviceFriendlyName: "Galaxy S25",
843
+ osType: -500,
844
+ gamingDeviceType: 528
845
+ };
846
+ const PRESETS = {
847
+ ios: IOS_PROFILE,
848
+ android: ANDROID_PROFILE
849
+ };
850
+ function resolveMobileProfile(input) {
851
+ if (input === void 0) return { ...IOS_PROFILE };
852
+ if (typeof input === "string") return { ...PRESETS[input] };
853
+ return {
854
+ ...input.mobileClient === "android" ? ANDROID_PROFILE : IOS_PROFILE,
855
+ ...input
856
+ };
857
+ }
858
+ //#endregion
859
+ //#region src/http/HttpClient.ts
860
+ const COOKIE_HOSTS = [
861
+ URLS.community,
862
+ URLS.store,
863
+ URLS.help,
864
+ URLS.api
865
+ ];
866
+ const IMPIT_BROWSER = {
867
+ ios: {
868
+ web: "ios18",
869
+ native: "ios18"
870
+ },
871
+ android: {
872
+ web: "chrome",
873
+ native: "okhttp5"
874
+ }
875
+ };
876
+ const BROWSER_ACCEPT$1 = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
877
+ function clean(input) {
878
+ if (!input) return void 0;
879
+ const out = {};
880
+ for (const [k, v] of Object.entries(input)) if (v !== void 0) out[k] = String(v);
881
+ return out;
882
+ }
883
+ function buildMultipart(fields, boundary) {
884
+ let out = "";
885
+ for (const { name, value } of fields) out += `--${boundary}\r\ncontent-disposition: form-data; name="${name}"\r\n\r\n${value}\r\n`;
886
+ return `${out}--${boundary}--\r\n`;
887
+ }
888
+ var HttpClient = class {
889
+ jar;
890
+ webClient;
891
+ nativeClient;
892
+ profile;
893
+ proxy;
894
+ constructor(opts) {
895
+ this.jar = new CookieJar();
896
+ this.profile = opts.profile;
897
+ this.proxy = opts.proxy;
898
+ const makeClient = (browser) => new Impit({
899
+ browser,
900
+ cookieJar: this.jar,
901
+ followRedirects: false,
902
+ timeout: 5e4,
903
+ ...opts.proxy ? { proxyUrl: opts.proxy } : {}
904
+ });
905
+ const fp = IMPIT_BROWSER[this.profile.mobileClient];
906
+ this.webClient = makeClient(fp.web);
907
+ this.nativeClient = fp.native === fp.web ? this.webClient : makeClient(fp.native);
908
+ for (const raw of [
909
+ `mobileClient=${this.profile.mobileClient}`,
910
+ `mobileClientVersion=${this.profile.mobileClientVersion}`,
911
+ "Steam_Language=english"
912
+ ]) for (const host of COOKIE_HOSTS) this.jar.setCookieSync(raw, host);
913
+ }
914
+ async request(method, url, opts = {}) {
915
+ const res = await this.perform(method, url, opts);
916
+ const location = firstHeader(res.headers.location);
917
+ if (res.statusCode >= 300 && res.statusCode < 400 && location?.includes("/market/eligibilitycheck")) {
918
+ await this.perform("GET", location, opts.signal ? { signal: opts.signal } : {});
919
+ return this.perform(method, url, opts);
920
+ }
921
+ return res;
922
+ }
923
+ async perform(method, url, opts = {}) {
924
+ const isNative = url.startsWith(URLS.api) || url.includes("/mobileconf/");
925
+ const headers = {
926
+ "User-Agent": isNative ? this.profile.apiUserAgent : this.profile.webUserAgent,
927
+ "Accept-Language": "en-US,en;q=0.9"
928
+ };
929
+ if (isNative) headers.Accept = "application/json, text/plain, */*";
930
+ else {
931
+ headers.Accept = opts.responseType === "json" ? "application/json, text/plain, */*" : BROWSER_ACCEPT$1;
932
+ if (method !== "GET") {
933
+ headers.Origin = URLS.community;
934
+ if (opts.referer) headers.Referer = opts.referer;
935
+ }
936
+ }
937
+ let body;
938
+ if (opts.multipart) {
939
+ const boundary = `----steamMobile${randomSessionId()}`;
940
+ body = buildMultipart(opts.multipart, boundary);
941
+ headers["Content-Type"] = `multipart/form-data; boundary=${boundary}`;
942
+ } else if (opts.form) body = new URLSearchParams(clean(opts.form));
943
+ else if (opts.json !== void 0) {
944
+ body = JSON.stringify(opts.json);
945
+ headers["Content-Type"] = "application/json";
946
+ } else if (opts.body !== void 0) body = opts.body;
947
+ Object.assign(headers, opts.headers);
948
+ const target = new URL(url);
949
+ const sp = clean(opts.searchParams);
950
+ if (sp) for (const [k, v] of Object.entries(sp)) target.searchParams.set(k, v);
951
+ try {
952
+ const res = await (isNative ? this.nativeClient : this.webClient).fetch(target.toString(), {
953
+ method,
954
+ headers,
955
+ ...body !== void 0 ? { body } : {},
956
+ ...opts.signal ? { signal: opts.signal } : {},
957
+ ...opts.timeoutMs !== void 0 ? { timeout: opts.timeoutMs } : {}
958
+ });
959
+ const responseType = opts.responseType ?? "text";
960
+ let parsed;
961
+ if (responseType === "buffer") parsed = Buffer.from(await res.bytes());
962
+ else if (responseType === "json") {
963
+ const text = await res.text();
964
+ parsed = text ? safeJsonParse(text) : void 0;
965
+ } else parsed = await res.text();
966
+ return {
967
+ statusCode: res.status,
968
+ headers: headersToRecord(res.headers),
969
+ body: parsed
970
+ };
971
+ } catch (err) {
972
+ throw this.wrapTransportError(err);
973
+ }
974
+ }
975
+ wrapTransportError(err) {
976
+ if (this.proxy && err instanceof Error && !isAbortError(err)) return new ProxyError(`proxy request failed: ${err.message}`, { cause: err });
977
+ return err;
978
+ }
979
+ get(url, opts) {
980
+ return this.request("GET", url, opts);
981
+ }
982
+ post(url, opts) {
983
+ return this.request("POST", url, opts);
984
+ }
985
+ async setCookie(rawCookie) {
986
+ for (const host of COOKIE_HOSTS) await this.jar.setCookie(rawCookie, host);
987
+ }
988
+ async getCookie(key, url = URLS.community) {
989
+ return (await this.jar.getCookies(url)).find((c) => c.key === key)?.value;
990
+ }
991
+ async getSessionId() {
992
+ const existing = (await this.jar.getCookies(URLS.community)).find((c) => c.key === "sessionid");
993
+ if (existing) return existing.value;
994
+ const sessionId = randomSessionId();
995
+ await this.setCookie(`sessionid=${sessionId}`);
996
+ return sessionId;
997
+ }
998
+ };
999
+ function randomSessionId() {
1000
+ const bytes = /* @__PURE__ */ new Uint8Array(12);
1001
+ globalThis.crypto.getRandomValues(bytes);
1002
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
1003
+ }
1004
+ function firstHeader(v) {
1005
+ return Array.isArray(v) ? v[0] : v;
1006
+ }
1007
+ function headersToRecord(h) {
1008
+ const out = {};
1009
+ h.forEach((value, key) => {
1010
+ out[key] = value;
1011
+ });
1012
+ return out;
1013
+ }
1014
+ function safeJsonParse(text) {
1015
+ try {
1016
+ return JSON.parse(text);
1017
+ } catch {
1018
+ return;
1019
+ }
1020
+ }
1021
+ function isAbortError(err) {
1022
+ if (typeof err !== "object" || err === null) return false;
1023
+ const e = err;
1024
+ return e.name === "AbortError" || e.code === "ERR_ABORTED";
1025
+ }
1026
+ //#endregion
1027
+ //#region src/auth/loginWithCredentials.ts
1028
+ function loginWithCredentials(opts) {
1029
+ const profile = resolveMobileProfile(opts.mobileProfile);
1030
+ const session = new CredentialSession(new HttpClient({
1031
+ ...opts.proxy ? { proxy: opts.proxy } : {},
1032
+ profile
1033
+ }), profile);
1034
+ return new Promise((resolve, reject) => {
1035
+ const { signal } = opts;
1036
+ const onAbort = () => {
1037
+ session.stop();
1038
+ reject(new LoginError("login aborted"));
1039
+ };
1040
+ const cleanup = () => signal?.removeEventListener("abort", onAbort);
1041
+ const fail = (err) => {
1042
+ session.stop();
1043
+ cleanup();
1044
+ reject(err);
1045
+ };
1046
+ if (signal?.aborted) {
1047
+ reject(new LoginError("login aborted"));
1048
+ return;
1049
+ }
1050
+ signal?.addEventListener("abort", onAbort, { once: true });
1051
+ session.on("error", fail);
1052
+ session.on("timeout", () => fail(new LoginError("login timed out waiting for confirmation")));
1053
+ session.on("authenticated", () => {
1054
+ cleanup();
1055
+ if (!session.refreshToken || !session.steamID) {
1056
+ reject(new LoginError("login completed without a refresh token"));
1057
+ return;
1058
+ }
1059
+ resolve({
1060
+ refreshToken: session.refreshToken,
1061
+ accessToken: session.accessToken,
1062
+ steamId: session.steamID.getSteamID64(),
1063
+ username: session.username
1064
+ });
1065
+ });
1066
+ session.on("steamGuardRequired", (info) => {
1067
+ const handler = opts.onSteamGuardRequired;
1068
+ if (!handler) {
1069
+ fail(new LoginError(`${info.message}; pass sharedSecret, steamGuardCode, or onSteamGuardRequired`));
1070
+ return;
1071
+ }
1072
+ Promise.resolve(handler(info)).then((code) => session.submitSteamGuardCode(code)).catch((err) => fail(err instanceof Error ? err : new Error(String(err))));
1073
+ });
1074
+ session.start(opts).catch((err) => fail(err instanceof Error ? err : new Error(String(err))));
1075
+ });
1076
+ }
1077
+ //#endregion
1078
+ //#region src/core/paginate.ts
1079
+ async function paginate(fetchPage) {
1080
+ const all = [];
1081
+ let cursor;
1082
+ do {
1083
+ const { items, next } = await fetchPage(cursor);
1084
+ all.push(...items);
1085
+ cursor = next;
1086
+ } while (cursor !== void 0);
1087
+ return all;
1088
+ }
1089
+ //#endregion
1090
+ //#region src/core/parseStrError.ts
1091
+ function parseStrError(message) {
1092
+ const match = message.match(/\((\d+)\)$/);
1093
+ const eresult = match?.[1] !== void 0 ? Number(match[1]) : void 0;
1094
+ const opts = eresult !== void 0 ? { eresult } : void 0;
1095
+ if (/You cannot trade with .* because they have a trade ban\./.test(message)) return new TradeBanError(message);
1096
+ if (/sent too many trade offers/.test(message)) return new OfferLimitError(message);
1097
+ if (/You have logged in from a new device/.test(message)) return new NewDeviceError(message, opts);
1098
+ if (/is not available to trade\. More information will be shown to/.test(message)) return new TargetCannotTradeError(message, opts);
1099
+ if (/unable to contact the game's item server/.test(message)) return new ItemServerUnavailableError(message, { eresult: eresult ?? 20 });
1100
+ if (/inventory privacy is set to|inventory is (?:currently )?private|profile's inventory is private/i.test(message)) return new PrivateInventoryError(message);
1101
+ return new SteamError(message, opts);
1102
+ }
1103
+ //#endregion
1104
+ //#region src/core/rateLimits.ts
1105
+ const RATE_LIMITS = {
1106
+ partnerInventory: {
1107
+ type: "window",
1108
+ windowMs: 12e4,
1109
+ max: 30
1110
+ },
1111
+ inventory: null,
1112
+ GetTradeHistory: {
1113
+ type: "bucket",
1114
+ capacity: 25,
1115
+ refillMs: 15e3
1116
+ },
1117
+ GetTradeHoldDurations: {
1118
+ type: "bucket",
1119
+ capacity: 3750,
1120
+ refillMs: 5
1121
+ },
1122
+ GetTradeOffer: {
1123
+ type: "bucket",
1124
+ capacity: 3750,
1125
+ refillMs: 5
1126
+ },
1127
+ GetTradeOffers: {
1128
+ type: "bucket",
1129
+ capacity: 85,
1130
+ refillMs: 125
1131
+ },
1132
+ GetTradeOffersSummary: {
1133
+ type: "bucket",
1134
+ capacity: 85,
1135
+ refillMs: 125
1136
+ },
1137
+ GetTradeStatus: {
1138
+ type: "bucket",
1139
+ capacity: 25,
1140
+ refillMs: 2e3
1141
+ }
1142
+ };
1143
+ function retryAfterMs(limit) {
1144
+ if (!limit) return null;
1145
+ return limit.type === "bucket" ? limit.refillMs : limit.windowMs;
1146
+ }
1147
+ const RETRY_AFTER = Object.fromEntries(Object.entries(RATE_LIMITS).map(([key, limit]) => [key, retryAfterMs(limit)]));
1148
+ //#endregion
1149
+ //#region src/http/checkers.ts
1150
+ function locationHeader(res) {
1151
+ const loc = res.headers.location;
1152
+ return Array.isArray(loc) ? loc[0] ?? "" : loc ?? "";
1153
+ }
1154
+ function httpError(res, rateLimitWindowMs) {
1155
+ if (res.statusCode === 429) return new RateLimitError({
1156
+ statusCode: 429,
1157
+ body: res.body,
1158
+ ...typeof rateLimitWindowMs === "number" ? { retryAfterMs: rateLimitWindowMs } : {}
1159
+ });
1160
+ if (res.statusCode === 401) return new SteamSessionExpiredError();
1161
+ if (res.statusCode >= 300 && res.statusCode <= 399) {
1162
+ const loc = locationHeader(res);
1163
+ if (loc.includes("/login")) return new SteamSessionExpiredError();
1164
+ if (loc.includes("eligibilitycheck")) return new SteamError("Steam redirected to the market eligibility check — this account is limited / not trade-eligible");
1165
+ }
1166
+ return new HttpStatusError(res.statusCode, void 0, res.body);
1167
+ }
1168
+ function checkCommunityError(html) {
1169
+ if (typeof html !== "string") return;
1170
+ if (html.match(/<h1>Sorry!<\/h1>/)) throw new SteamError(html.match(/<h3>(.+)<\/h3>/)?.[1] ?? "Unknown error occurred");
1171
+ if (html.includes("g_steamID = false;") && html.includes("<title>Sign In</title>")) throw new SteamSessionExpiredError();
1172
+ }
1173
+ //#endregion
1174
+ //#region src/http/tradePageError.ts
1175
+ async function fetchTradePageErrorMessage(http, steamId, token) {
1176
+ const accountId = new SteamID(steamId).accountid;
1177
+ const url = `${URLS.community}/tradeoffer/new/?partner=${accountId}${token ? `&token=${token}` : ""}`;
1178
+ const res = await http.get(url, {
1179
+ responseType: "text",
1180
+ headers: { Referer: `${URLS.community}/profiles/${steamId}` }
1181
+ });
1182
+ if (res.statusCode !== 200) return void 0;
1183
+ return res.body.match(/<div id="error_msg">\s*([^<]+)\s*<\/div>/)?.[1]?.replace(/\s+/g, " ").trim() || void 0;
1184
+ }
1185
+ async function inventoryFailureError(http, steamId, token, bodyMessage) {
1186
+ return parseStrError(await fetchTradePageErrorMessage(http, steamId, token).catch(() => void 0) ?? bodyMessage ?? "Failed to load inventory");
1187
+ }
1188
+ //#endregion
1189
+ //#region src/models/EconItem.ts
1190
+ function toBool(value) {
1191
+ return typeof value === "boolean" ? value : Number(value ?? 0) !== 0;
1192
+ }
1193
+ function toInt(value) {
1194
+ return value ? Number.parseInt(String(value), 10) : 0;
1195
+ }
1196
+ function asRecord(value) {
1197
+ return value && !Array.isArray(value) ? value : {};
1198
+ }
1199
+ function buildDescriptionMap(descriptions) {
1200
+ const map = /* @__PURE__ */ new Map();
1201
+ for (const d of descriptions ?? []) map.set(`${d.appid}_${d.classid}_${d.instanceid ?? "0"}`, d);
1202
+ return map;
1203
+ }
1204
+ function buildItem(asset, description, properties, fallbackContextId) {
1205
+ return {
1206
+ ...asset,
1207
+ ...description,
1208
+ appid: Number(asset.appid ?? description?.appid ?? 0),
1209
+ contextid: (asset.contextid ?? fallbackContextId).toString(),
1210
+ assetid: (asset.assetid ?? asset.id ?? "").toString(),
1211
+ classid: asset.classid.toString(),
1212
+ instanceid: (asset.instanceid ?? "0").toString(),
1213
+ amount: toInt(asset.amount),
1214
+ ...asset.currencyid ? { currencyid: asset.currencyid } : {},
1215
+ name: description?.name ?? "",
1216
+ market_name: description?.market_name ?? "",
1217
+ market_hash_name: description?.market_hash_name ?? "",
1218
+ type: description?.type ?? "",
1219
+ icon_url: description?.icon_url ?? "",
1220
+ tradable: toBool(description?.tradable),
1221
+ marketable: toBool(description?.marketable),
1222
+ commodity: toBool(description?.commodity),
1223
+ market_tradable_restriction: toInt(description?.market_tradable_restriction),
1224
+ market_marketable_restriction: toInt(description?.market_marketable_restriction),
1225
+ descriptions: description?.descriptions ?? [],
1226
+ owner_descriptions: description?.owner_descriptions ?? [],
1227
+ actions: description?.actions ?? [],
1228
+ market_actions: description?.market_actions ?? [],
1229
+ fraudwarnings: description?.fraudwarnings ?? [],
1230
+ tags: description?.tags ?? [],
1231
+ asset_properties: properties
1232
+ };
1233
+ }
1234
+ function parseInventory(body, contextid, tradableOnly = false) {
1235
+ const descriptions = /* @__PURE__ */ new Map();
1236
+ for (const d of body.descriptions ?? []) descriptions.set(`${d.classid}_${d.instanceid ?? "0"}`, d);
1237
+ const properties = /* @__PURE__ */ new Map();
1238
+ for (const p of body.asset_properties ?? []) properties.set(p.assetid, p.asset_properties);
1239
+ const items = [];
1240
+ for (const asset of body.assets ?? []) {
1241
+ const assetid = (asset.assetid ?? asset.id ?? "").toString();
1242
+ const description = descriptions.get(`${asset.classid}_${asset.instanceid ?? "0"}`);
1243
+ if (tradableOnly && !toBool(description?.tradable)) continue;
1244
+ items.push(buildItem(asset, description, properties.get(assetid) ?? [], contextid));
1245
+ }
1246
+ return items;
1247
+ }
1248
+ function parsePartnerInventory(body, contextid, tradableOnly = false) {
1249
+ const inventory = asRecord(body.rgInventory);
1250
+ const descriptions = asRecord(body.rgDescriptions);
1251
+ const properties = asRecord(body.rgAssetProperties);
1252
+ const assets = Object.values(inventory).sort((a, b) => Number(a.pos ?? 0) - Number(b.pos ?? 0));
1253
+ const items = [];
1254
+ for (const asset of assets) {
1255
+ const description = descriptions[`${asset.classid}_${asset.instanceid ?? "0"}`];
1256
+ if (tradableOnly && !toBool(description?.tradable)) continue;
1257
+ const assetid = (asset.assetid ?? asset.id ?? "").toString();
1258
+ items.push(buildItem(asset, description, properties[assetid] ?? [], contextid));
1259
+ }
1260
+ return items;
1261
+ }
1262
+ //#endregion
1263
+ //#region src/community/openid.ts
1264
+ const STEAM_OPENID_URL = `${URLS.community}/openid/login`;
1265
+ const DEFAULT_MAX_REDIRECTS = 10;
1266
+ const BROWSER_ACCEPT = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
1267
+ function locationOf(res) {
1268
+ const loc = res.headers.location;
1269
+ return (Array.isArray(loc) ? loc[0] : loc) || void 0;
1270
+ }
1271
+ function isRedirect(res) {
1272
+ return res.statusCode >= 300 && res.statusCode < 400 && locationOf(res) !== void 0;
1273
+ }
1274
+ function queryParams(url) {
1275
+ const out = {};
1276
+ for (const [k, v] of new URL(url).searchParams) out[k] = v;
1277
+ return out;
1278
+ }
1279
+ function decodeEntities(s) {
1280
+ return s.replace(/&quot;/g, "\"").replace(/&#0?39;/g, "'").replace(/&apos;/g, "'").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
1281
+ }
1282
+ function parseOpenidForm(html) {
1283
+ const form = [...html.matchAll(/<form\b([^>]*?)>([\s\S]*?)<\/form>/gi)].find((m) => /openid\/login/i.test(m[1] ?? ""));
1284
+ const action = form?.[1]?.match(/\baction=["']([^"']+)["']/i)?.[1];
1285
+ if (!form || !action) return null;
1286
+ const fields = [];
1287
+ for (const tag of (form[2] ?? "").match(/<input\b[^>]*>/gi) ?? []) {
1288
+ const name = tag.match(/\bname=["']([^"']*)["']/i)?.[1];
1289
+ if (!name) continue;
1290
+ const value = decodeEntities(tag.match(/\bvalue=["']([^"']*)["']/i)?.[1] ?? "");
1291
+ fields.push({
1292
+ name,
1293
+ value
1294
+ });
1295
+ }
1296
+ return {
1297
+ action: new URL(decodeEntities(action), STEAM_OPENID_URL).toString(),
1298
+ fields
1299
+ };
1300
+ }
1301
+ async function confirmOpenid(http, interstitialHtml, referer) {
1302
+ const form = parseOpenidForm(interstitialHtml);
1303
+ if (!form) throw new OpenIdError("no Steam OpenID form in the page; the web session is not authenticated");
1304
+ const res = await http.post(form.action, {
1305
+ multipart: form.fields,
1306
+ headers: { Accept: BROWSER_ACCEPT },
1307
+ ...referer ? { referer } : {}
1308
+ });
1309
+ const location = isRedirect(res) ? locationOf(res) : void 0;
1310
+ if (!location) throw new OpenIdError(`Steam returned no OpenID assertion redirect (HTTP ${res.statusCode})`);
1311
+ const params = queryParams(location);
1312
+ const steamId = (params["openid.claimed_id"] ?? params["openid.identity"] ?? "").match(/\/id\/(\d{17})/)?.[1];
1313
+ if (!steamId) throw new OpenIdError("OpenID assertion carried no steamid");
1314
+ return {
1315
+ location,
1316
+ params,
1317
+ steamId
1318
+ };
1319
+ }
1320
+ async function steamOpenidLogin(http, options) {
1321
+ const maxRedirects = options.maxRedirects ?? DEFAULT_MAX_REDIRECTS;
1322
+ await http.getSessionId();
1323
+ const get = (u) => http.get(u, { headers: { Accept: BROWSER_ACCEPT } });
1324
+ let url = options.initiateUrl;
1325
+ let res = await get(url);
1326
+ for (let i = 0; i < maxRedirects && isRedirect(res); i++) {
1327
+ url = new URL(locationOf(res), url).toString();
1328
+ res = await get(url);
1329
+ }
1330
+ const confirmation = await confirmOpenid(http, res.body, url);
1331
+ url = confirmation.location;
1332
+ res = await get(url);
1333
+ for (let i = 0; i < maxRedirects && isRedirect(res); i++) {
1334
+ url = new URL(locationOf(res), url).toString();
1335
+ res = await get(url);
1336
+ }
1337
+ const cookieHost = options.cookieHost ? `https://${options.cookieHost}` : new URL(url).origin;
1338
+ const cookies = (await http.jar.getCookies(cookieHost)).map((c) => ({
1339
+ name: c.key,
1340
+ value: c.value
1341
+ }));
1342
+ return {
1343
+ steamId: confirmation.steamId,
1344
+ finalUrl: url,
1345
+ cookies
1346
+ };
1347
+ }
1348
+ //#endregion
1349
+ //#region src/community/tradeProtection.ts
1350
+ async function acknowledgeTradeProtection(http) {
1351
+ const sessionid = await http.getSessionId();
1352
+ const res = await http.post(`${URLS.community}/trade/new/acknowledge`, { form: {
1353
+ sessionid,
1354
+ message: 1
1355
+ } });
1356
+ if (res.statusCode !== 200) throw httpError(res);
1357
+ }
1358
+ //#endregion
1359
+ //#region src/community/CommunityNamespace.ts
1360
+ const INVENTORY_PAGE_SIZE = 2e3;
1361
+ var CommunityNamespace = class {
1362
+ http;
1363
+ session;
1364
+ confirmations;
1365
+ api;
1366
+ constructor(http, session, confirmations, api) {
1367
+ this.http = http;
1368
+ this.session = session;
1369
+ this.confirmations = confirmations;
1370
+ this.api = api;
1371
+ }
1372
+ acknowledgeTradeProtection() {
1373
+ return acknowledgeTradeProtection(this.http);
1374
+ }
1375
+ async getTradeURL() {
1376
+ await this.session.getAccessToken();
1377
+ const steamId = this.session.steamID.getSteamID64();
1378
+ const res = await this.http.get(`${URLS.community}/profiles/${steamId}/tradeoffers/privacy`, {
1379
+ responseType: "text",
1380
+ headers: { Referer: `${URLS.community}/profiles/${steamId}` }
1381
+ });
1382
+ if (res.statusCode !== 200) throw httpError(res);
1383
+ const html = res.body;
1384
+ checkCommunityError(html);
1385
+ const match = html.match(/https?:\/\/(?:www\.)?steamcommunity\.com\/tradeoffer\/new\/\?partner=\d+(?:&|&amp;)token=([a-zA-Z0-9\-_]+)/);
1386
+ if (!match?.[1]) throw new SteamError("Failed to parse trade URL from the privacy page");
1387
+ return {
1388
+ url: match[0].replace(/&amp;/g, "&"),
1389
+ token: match[1]
1390
+ };
1391
+ }
1392
+ async changeTradeURL() {
1393
+ await this.session.getAccessToken();
1394
+ const steamId = this.session.steamID.getSteamID64();
1395
+ const sessionid = await this.http.getSessionId();
1396
+ const res = await this.http.post(`${URLS.community}/profiles/${steamId}/tradeoffers/newtradeurl`, {
1397
+ responseType: "json",
1398
+ form: { sessionid }
1399
+ });
1400
+ if (res.statusCode !== 200) throw httpError(res);
1401
+ const body = res.body;
1402
+ const token = typeof body === "string" ? body : String(body?.token ?? "");
1403
+ if (!token) throw new SteamError("Failed to parse the new trade token");
1404
+ const accountId = this.session.steamID.accountid;
1405
+ return {
1406
+ url: `${URLS.community}/tradeoffer/new/?partner=${accountId}&token=${token}`,
1407
+ token
1408
+ };
1409
+ }
1410
+ async getProfile(steamId) {
1411
+ await this.session.getAccessToken();
1412
+ const id = steamId ?? this.session.steamID.getSteamID64();
1413
+ const res = await this.http.get(`${URLS.community}/profiles/${id}?xml=1`, { responseType: "text" });
1414
+ if (res.statusCode !== 200) throw httpError(res);
1415
+ const xml = res.body;
1416
+ checkCommunityError(xml);
1417
+ if (!xml.includes("<steamID64>")) throw new SteamError("Failed to load the profile XML");
1418
+ const memberSince = xmlValue(xml, "memberSince");
1419
+ const created = memberSince ? new Date(memberSince) : null;
1420
+ return {
1421
+ steamId: id,
1422
+ personaName: xmlValue(xml, "steamID") ?? "",
1423
+ avatar: xmlValue(xml, "avatarFull") ?? "",
1424
+ accountCreated: created && !Number.isNaN(created.getTime()) ? created : null,
1425
+ tradeBanState: xmlValue(xml, "tradeBanState") ?? "None",
1426
+ isLimited: xmlValue(xml, "isLimitedAccount") === "1",
1427
+ vacBanned: xmlValue(xml, "vacBanned") === "1",
1428
+ privacyState: xmlValue(xml, "privacyState") ?? ""
1429
+ };
1430
+ }
1431
+ async getWebTradeEligibility() {
1432
+ await this.session.getAccessToken();
1433
+ const res = await this.http.get(`${URLS.community}/market/eligibilitycheck/`, {
1434
+ responseType: "text",
1435
+ headers: { Referer: `${URLS.community}/` }
1436
+ });
1437
+ if (res.statusCode >= 400) throw httpError(res);
1438
+ const raw = await this.http.getCookie("webTradeEligibility");
1439
+ if (!raw) throw new SteamError("Steam did not return a webTradeEligibility cookie");
1440
+ try {
1441
+ return JSON.parse(decodeURIComponent(raw));
1442
+ } catch {
1443
+ throw new SteamError("Failed to parse the webTradeEligibility cookie");
1444
+ }
1445
+ }
1446
+ async openidLogin(options) {
1447
+ await this.session.getAccessToken();
1448
+ return steamOpenidLogin(this.http, options);
1449
+ }
1450
+ async getSteamLevel(steamId) {
1451
+ const id = steamId ?? this.session.steamID.getSteamID64();
1452
+ return (await this.api.call({
1453
+ httpMethod: "GET",
1454
+ iface: "IPlayerService",
1455
+ method: "GetSteamLevel",
1456
+ input: { steamid: id }
1457
+ })).response?.player_level ?? 0;
1458
+ }
1459
+ async ensureApiKey(domain = "assetpay.gg") {
1460
+ await this.session.getAccessToken();
1461
+ const res = await this.http.get(`${URLS.community}/dev/apikey?l=english`, { responseType: "text" });
1462
+ if (res.statusCode !== 200) throw httpError(res);
1463
+ const body = res.body;
1464
+ const key = body.match(/<p>Key:\s*([0-9A-F]+)<\/p>/i)?.[1];
1465
+ if (key) return key;
1466
+ if (/validated email address|Steam Guard Mobile Authenticator|<h2>Access Denied<\/h2>/i.test(body)) return null;
1467
+ return this.requestApiKey(domain);
1468
+ }
1469
+ async requestApiKey(domain, requestId = "0") {
1470
+ const sessionid = await this.http.getSessionId();
1471
+ const res = await this.http.post(`${URLS.community}/dev/requestkey`, {
1472
+ responseType: "json",
1473
+ form: {
1474
+ domain,
1475
+ request_id: requestId,
1476
+ sessionid,
1477
+ agreeToTerms: "true"
1478
+ }
1479
+ });
1480
+ if (res.statusCode !== 200) throw httpError(res);
1481
+ const body = res.body ?? {};
1482
+ if (body.api_key) return body.api_key;
1483
+ if (body.request_id) {
1484
+ try {
1485
+ await this.confirmations.acceptConfirmationForObject(body.request_id);
1486
+ } catch {
1487
+ return null;
1488
+ }
1489
+ return this.requestApiKey(domain, body.request_id);
1490
+ }
1491
+ return null;
1492
+ }
1493
+ async getInventory(appid, contextid = "2", options = {}) {
1494
+ await this.session.getAccessToken();
1495
+ const ownId = this.session.steamID.getSteamID64();
1496
+ const steamId = options.steamId ?? ownId;
1497
+ const tradableOnly = options.tradableOnly ?? false;
1498
+ return steamId === ownId ? this.getOwnInventoryItems(steamId, appid, contextid, tradableOnly) : this.getTheirInventory(steamId, appid, contextid, tradableOnly);
1499
+ }
1500
+ getOwnInventoryItems(steamId, appid, contextid, tradableOnly) {
1501
+ const url = `${URLS.community}/profiles/${steamId}/inventory/json/${appid}/${contextid}`;
1502
+ return paginate(async (start) => {
1503
+ const res = await this.http.get(url, {
1504
+ responseType: "json",
1505
+ searchParams: {
1506
+ trading: tradableOnly ? 1 : 0,
1507
+ preserve_bbcode: 1,
1508
+ l: LANG.l,
1509
+ ...start !== void 0 ? { start } : {}
1510
+ },
1511
+ headers: { Referer: `${URLS.community}/profiles/${steamId}/inventory` }
1512
+ });
1513
+ if (res.statusCode !== 200) throw httpError(res, RETRY_AFTER.inventory);
1514
+ const body = res.body;
1515
+ if (!body?.success) throw new SteamError(body?.error ?? body?.Error ?? "Malformed inventory response");
1516
+ const next = body.more && typeof body.more_start === "number" && body.more_start > (start ?? 0) ? body.more_start : void 0;
1517
+ return {
1518
+ items: parsePartnerInventory(body, contextid, tradableOnly),
1519
+ next
1520
+ };
1521
+ });
1522
+ }
1523
+ getTheirInventory(steamId, appid, contextid, tradableOnly) {
1524
+ const url = `${URLS.community}/inventory/${steamId}/${appid}/${contextid}`;
1525
+ return paginate(async (startAssetId) => {
1526
+ const res = await this.http.get(url, {
1527
+ responseType: "json",
1528
+ searchParams: {
1529
+ l: LANG.l,
1530
+ count: INVENTORY_PAGE_SIZE,
1531
+ raw_asset_properties: 1,
1532
+ preserve_bbcode: 1,
1533
+ start_assetid: startAssetId
1534
+ },
1535
+ headers: { Referer: `${URLS.community}/profiles/${steamId}/inventory` }
1536
+ });
1537
+ const body = res.body;
1538
+ if (res.statusCode === 403 && !body) throw await inventoryFailureError(this.http, steamId, void 0, "This profile is private.");
1539
+ if (res.statusCode === 500 && body?.error) throw parseStrError(body.error);
1540
+ if (res.statusCode !== 200) throw httpError(res, RETRY_AFTER.inventory);
1541
+ if (!body?.success) throw await inventoryFailureError(this.http, steamId, void 0, body?.error ?? body?.Error);
1542
+ if (!body.assets) return {
1543
+ items: [],
1544
+ next: void 0
1545
+ };
1546
+ return {
1547
+ items: parseInventory(body, contextid, tradableOnly),
1548
+ next: body.more_items && body.last_assetid ? body.last_assetid : void 0
1549
+ };
1550
+ });
1551
+ }
1552
+ };
1553
+ function xmlValue(xml, tag) {
1554
+ return xml.match(new RegExp(`<${tag}>(?:<!\\[CDATA\\[)?([\\s\\S]*?)(?:\\]\\]>)?</${tag}>`))?.[1]?.trim() || void 0;
1555
+ }
1556
+ //#endregion
1557
+ //#region src/http/queryTime.ts
1558
+ async function queryServerTimeOffset(http) {
1559
+ const res = await http.post(`${URLS.api}/ITwoFactorService/QueryTime/v1/`, { responseType: "json" });
1560
+ if (res.statusCode !== 200) throw httpError(res);
1561
+ const serverTime = Number(res.body?.response?.server_time);
1562
+ if (!serverTime) throw new SteamError("Failed to query Steam server time");
1563
+ return serverTime - time();
1564
+ }
1565
+ //#endregion
1566
+ //#region src/community/confirmations.ts
1567
+ const TIME_OFFSET_TTL_MS = 720 * 60 * 1e3;
1568
+ var ConfirmationManager = class {
1569
+ http;
1570
+ steamID;
1571
+ identitySecret;
1572
+ profile;
1573
+ timeOffset;
1574
+ timeOffsetAt = 0;
1575
+ usedConfTimes = [];
1576
+ constructor(http, steamID, identitySecret, profile) {
1577
+ this.http = http;
1578
+ this.steamID = steamID;
1579
+ this.identitySecret = identitySecret;
1580
+ this.profile = profile;
1581
+ }
1582
+ get hasIdentitySecret() {
1583
+ return Boolean(this.identitySecret);
1584
+ }
1585
+ deviceId() {
1586
+ const android = getDeviceID(this.steamID.getSteamID64());
1587
+ return this.profile.mobileClient === "ios" ? android.replace(/^android:/, "").toUpperCase() : android;
1588
+ }
1589
+ async getConfirmations(time, key) {
1590
+ const { tag, k } = splitKey(key, "conf");
1591
+ const body = await this.getlist(k, time, tag);
1592
+ if (!body?.success) {
1593
+ if (body?.needauth) throw new SteamSessionExpiredError();
1594
+ throw new ConfirmationError(String(body?.message ?? body?.detail ?? "Failed to get confirmation list"));
1595
+ }
1596
+ return (body.conf ?? []).map((c) => ({
1597
+ id: String(c.id),
1598
+ type: Number(c.type),
1599
+ creator: String(c.creator_id),
1600
+ key: String(c.nonce),
1601
+ title: `${c.type_name ?? "Confirm"} - ${c.headline ?? ""}`,
1602
+ receiving: Number(c.type) === 2 ? String(c.summary?.[1] ?? "") : "",
1603
+ sending: String(c.summary?.[0] ?? ""),
1604
+ time: (/* @__PURE__ */ new Date(Number(c.creation_time) * 1e3)).toISOString(),
1605
+ timestamp: /* @__PURE__ */ new Date(Number(c.creation_time) * 1e3),
1606
+ icon: String(c.icon ?? "")
1607
+ }));
1608
+ }
1609
+ async respondToConfirmation(confID, confKey, time, key, accept) {
1610
+ const { tag, k } = splitKey(key, accept ? "allow" : "cancel");
1611
+ const op = accept ? "allow" : "cancel";
1612
+ const res = await this.http.post(`${URLS.community}/mobileconf/multiajaxop`, {
1613
+ responseType: "json",
1614
+ searchParams: {
1615
+ ...this.confParams(k, time, tag),
1616
+ op
1617
+ },
1618
+ multipart: [{
1619
+ name: "cid[]",
1620
+ value: confID
1621
+ }, {
1622
+ name: "ck[]",
1623
+ value: confKey
1624
+ }]
1625
+ });
1626
+ if (res.statusCode !== 200) throw httpError(res);
1627
+ if (res.body?.success) return;
1628
+ throw new ConfirmationError(String(res.body?.message ?? "Could not act on confirmation"));
1629
+ }
1630
+ async getPending() {
1631
+ const secret = this.requireSecret();
1632
+ const listTime = time(await this.getTimeOffset());
1633
+ const listKey = getConfirmationKey(secret, listTime, "list");
1634
+ return this.getConfirmations(listTime, {
1635
+ tag: "list",
1636
+ key: listKey
1637
+ });
1638
+ }
1639
+ acceptConfirmation(confID, nonce) {
1640
+ return this.actOnConfirmation(confID, nonce, true);
1641
+ }
1642
+ rejectConfirmation(confID, nonce) {
1643
+ return this.actOnConfirmation(confID, nonce, false);
1644
+ }
1645
+ async acceptAll() {
1646
+ const pending = await this.getPending();
1647
+ for (const c of pending) await this.acceptConfirmation(c.id, c.key);
1648
+ return pending;
1649
+ }
1650
+ async acceptConfirmationForObject(objectID) {
1651
+ const conf = (await this.getPending()).find((c) => c.creator === String(objectID));
1652
+ if (!conf) throw new ConfirmationError(`Could not find confirmation for object ${objectID}`);
1653
+ await this.acceptConfirmation(conf.id, conf.key);
1654
+ }
1655
+ async actOnConfirmation(confID, nonce, accept) {
1656
+ const secret = this.requireSecret();
1657
+ const offset = await this.getTimeOffset();
1658
+ const time = this.nextConfTime(offset);
1659
+ const tag = accept ? "accept" : "cancel";
1660
+ const key = getConfirmationKey(secret, time, tag);
1661
+ await this.respondToConfirmation(confID, nonce, time, {
1662
+ tag,
1663
+ key
1664
+ }, accept);
1665
+ }
1666
+ requireSecret() {
1667
+ if (!this.identitySecret) throw new ConfirmationError("identitySecret is required to respond to confirmations");
1668
+ return this.identitySecret;
1669
+ }
1670
+ nextConfTime(offset) {
1671
+ let time$1 = time(offset);
1672
+ let localOffset = 0;
1673
+ while (this.usedConfTimes.includes(time$1)) time$1 = time(offset) + ++localOffset;
1674
+ this.usedConfTimes.push(time$1);
1675
+ if (this.usedConfTimes.length > 60) this.usedConfTimes.splice(0, this.usedConfTimes.length - 60);
1676
+ return time$1;
1677
+ }
1678
+ async getTimeOffset() {
1679
+ if (this.timeOffset !== void 0 && Date.now() - this.timeOffsetAt < TIME_OFFSET_TTL_MS) return this.timeOffset;
1680
+ const offset = await queryServerTimeOffset(this.http);
1681
+ this.timeOffset = offset;
1682
+ this.timeOffsetAt = Date.now();
1683
+ return offset;
1684
+ }
1685
+ confParams(key, time, tag) {
1686
+ return {
1687
+ p: this.deviceId(),
1688
+ a: this.steamID.getSteamID64(),
1689
+ k: key,
1690
+ t: time,
1691
+ m: "react",
1692
+ tag
1693
+ };
1694
+ }
1695
+ async getlist(key, time, tag) {
1696
+ const res = await this.http.get(`${URLS.community}/mobileconf/getlist`, {
1697
+ responseType: "json",
1698
+ searchParams: this.confParams(key, time, tag)
1699
+ });
1700
+ if (res.statusCode !== 200) throw httpError(res);
1701
+ return res.body;
1702
+ }
1703
+ };
1704
+ function splitKey(key, fallbackTag) {
1705
+ return typeof key === "object" ? {
1706
+ tag: key.tag,
1707
+ k: key.key
1708
+ } : {
1709
+ tag: fallbackTag,
1710
+ k: key
1711
+ };
1712
+ }
1713
+ //#endregion
1714
+ //#region src/core/offerState.ts
1715
+ const NON_TERMINAL = /* @__PURE__ */ new Set([
1716
+ 2,
1717
+ 9,
1718
+ 11
1719
+ ]);
1720
+ function isTerminalState(state) {
1721
+ return !NON_TERMINAL.has(state);
1722
+ }
1723
+ //#endregion
1724
+ //#region src/core/target.ts
1725
+ function resolveTarget(target) {
1726
+ if ("tradeUrl" in target && target.tradeUrl) {
1727
+ const params = new URL(target.tradeUrl).searchParams;
1728
+ const partner = params.get("partner");
1729
+ if (!partner) throw new SteamError("invalid trade URL: missing partner");
1730
+ const accountId = Number(partner);
1731
+ if (!Number.isInteger(accountId) || accountId <= 0) throw new SteamError(`invalid trade URL: non-numeric partner '${partner}'`);
1732
+ return {
1733
+ steamId: SteamID.fromIndividualAccountID(accountId).getSteamID64(),
1734
+ token: params.get("token") ?? void 0
1735
+ };
1736
+ }
1737
+ if ("steamId" in target && target.steamId) return {
1738
+ steamId: target.steamId,
1739
+ token: target.token
1740
+ };
1741
+ throw new SteamError("invalid target: provide tradeUrl or steamId");
1742
+ }
1743
+ //#endregion
1744
+ //#region src/http/webApi.ts
1745
+ var WebApiClient = class {
1746
+ http;
1747
+ getAccessToken;
1748
+ constructor(http, getAccessToken) {
1749
+ this.http = http;
1750
+ this.getAccessToken = getAccessToken;
1751
+ }
1752
+ async call(params) {
1753
+ const { httpMethod, iface, method, version = 1, input = {}, retryAfterMs = null } = params;
1754
+ const accessToken = await this.getAccessToken();
1755
+ const url = `${URLS.api}/${iface}/${method}/v${version}/`;
1756
+ const payload = {
1757
+ origin: "SteamMobile",
1758
+ ...input,
1759
+ access_token: accessToken
1760
+ };
1761
+ if (process.env.DEBUG_HTTP === "1") {
1762
+ const qs = new URLSearchParams(Object.entries(payload).filter(([, v]) => v !== void 0).map(([k, v]) => [k, String(v)]));
1763
+ console.log(`[http] ${httpMethod} ${url}?${qs.toString()}`);
1764
+ }
1765
+ const res = await this.http.request(httpMethod, url, {
1766
+ responseType: "json",
1767
+ ...httpMethod === "GET" ? { searchParams: payload } : { form: payload }
1768
+ });
1769
+ if (res.statusCode !== 200) throw httpError(res, retryAfterMs);
1770
+ const header = res.headers["x-eresult"];
1771
+ let eresult = Array.isArray(header) ? header[0] : header;
1772
+ const errorMessage = headerValue(res.headers["x-error_message"]);
1773
+ const body = res.body;
1774
+ const responseObj = body && typeof body === "object" ? body.response : void 0;
1775
+ if (eresult === "2" && body && (Object.keys(body).length > 1 || responseObj && Object.keys(responseObj).length > 0)) eresult = "1";
1776
+ if (eresult !== void 0 && eresult !== "1") {
1777
+ const code = Number(eresult);
1778
+ const msg = `${EResult[code] ?? "EResult"} (${eresult})${errorMessage ? `: ${errorMessage}` : ""}`;
1779
+ if (code === 21) throw new SteamSessionExpiredError(msg);
1780
+ if (code === 84) throw new RateLimitError({
1781
+ message: msg,
1782
+ body,
1783
+ eresult: 84,
1784
+ ...typeof retryAfterMs === "number" ? { retryAfterMs } : {}
1785
+ });
1786
+ throw new SteamError(msg, {
1787
+ eresult: code,
1788
+ body
1789
+ });
1790
+ }
1791
+ if (!body || typeof body !== "object") throw new SteamError("Invalid API response", { body });
1792
+ return body;
1793
+ }
1794
+ };
1795
+ function headerValue(v) {
1796
+ return Array.isArray(v) ? v[0] : v;
1797
+ }
1798
+ //#endregion
1799
+ //#region src/keyApi/SteamWebApi.ts
1800
+ const GET_PLAYER_BANS_BATCH = 100;
1801
+ const GET_PLAYER_SUMMARIES_BATCH = 100;
1802
+ var SteamWebApi = class {
1803
+ http;
1804
+ apiKey;
1805
+ constructor(options) {
1806
+ this.apiKey = options.apiKey;
1807
+ this.http = options.http ?? new HttpClient({
1808
+ ...options.proxy ? { proxy: options.proxy } : {},
1809
+ profile: resolveMobileProfile()
1810
+ });
1811
+ }
1812
+ async getPlayerBans(steamIds) {
1813
+ if (steamIds.length === 0) return [];
1814
+ const out = [];
1815
+ for (let i = 0; i < steamIds.length; i += GET_PLAYER_BANS_BATCH) {
1816
+ const chunk = steamIds.slice(i, i + GET_PLAYER_BANS_BATCH);
1817
+ const res = await this.http.get(`${URLS.api}/ISteamUser/GetPlayerBans/v1/`, {
1818
+ responseType: "json",
1819
+ searchParams: {
1820
+ key: this.apiKey,
1821
+ steamids: chunk.join(",")
1822
+ }
1823
+ });
1824
+ if (res.statusCode !== 200) throw httpError(res);
1825
+ const players = res.body?.players;
1826
+ if (!Array.isArray(players)) throw new SteamError("Invalid GetPlayerBans response", { body: res.body });
1827
+ out.push(...players);
1828
+ }
1829
+ return out;
1830
+ }
1831
+ async getPlayerSummaries(steamIds) {
1832
+ if (steamIds.length === 0) return [];
1833
+ const out = [];
1834
+ for (let i = 0; i < steamIds.length; i += GET_PLAYER_SUMMARIES_BATCH) {
1835
+ const chunk = steamIds.slice(i, i + GET_PLAYER_SUMMARIES_BATCH);
1836
+ const res = await this.http.get(`${URLS.api}/ISteamUser/GetPlayerSummaries/v2/`, {
1837
+ responseType: "json",
1838
+ searchParams: {
1839
+ key: this.apiKey,
1840
+ steamids: chunk.join(",")
1841
+ }
1842
+ });
1843
+ if (res.statusCode !== 200) throw httpError(res);
1844
+ const players = res.body?.response?.players;
1845
+ if (!Array.isArray(players)) throw new SteamError("Invalid GetPlayerSummaries response", { body: res.body });
1846
+ out.push(...players);
1847
+ }
1848
+ return out;
1849
+ }
1850
+ async getBadges(steamId) {
1851
+ const res = await this.http.get(`${URLS.api}/IPlayerService/GetBadges/v1/`, {
1852
+ responseType: "json",
1853
+ searchParams: {
1854
+ key: this.apiKey,
1855
+ steamid: steamId
1856
+ }
1857
+ });
1858
+ if (res.statusCode !== 200) throw httpError(res);
1859
+ const response = res.body?.response;
1860
+ if (!response || !Array.isArray(response.badges)) throw new SteamError("Invalid GetBadges response", { body: res.body });
1861
+ return response;
1862
+ }
1863
+ };
1864
+ /**
1865
+ * Describes the message CEconItemPreviewDataBlock.
1866
+ * Use `create(CEconItemPreviewDataBlockSchema)` to create a new message.
1867
+ */
1868
+ const CEconItemPreviewDataBlockSchema = /*@__PURE__*/ messageDesc(/* @__PURE__ */ fileDesc("Chdjc2dvX2Vjb25fcHJldmlldy5wcm90byK0CQoZQ0Vjb25JdGVtUHJldmlld0RhdGFCbG9jaxIWCglhY2NvdW50aWQYASABKA1IAIgBARITCgZpdGVtaWQYAiABKARIAYgBARIVCghkZWZpbmRleBgDIAEoDUgCiAEBEhcKCnBhaW50aW5kZXgYBCABKA1IA4gBARITCgZyYXJpdHkYBSABKA1IBIgBARIUCgdxdWFsaXR5GAYgASgNSAWIAQESFgoJcGFpbnR3ZWFyGAcgASgNSAaIAQESFgoJcGFpbnRzZWVkGAggASgNSAeIAQESHwoSa2lsbGVhdGVyc2NvcmV0eXBlGAkgASgNSAiIAQESGwoOa2lsbGVhdGVydmFsdWUYCiABKA1ICYgBARIXCgpjdXN0b21uYW1lGAsgASgJSAqIAQESNAoIc3RpY2tlcnMYDCADKAsyIi5DRWNvbkl0ZW1QcmV2aWV3RGF0YUJsb2NrLlN0aWNrZXISFgoJaW52ZW50b3J5GA0gASgNSAuIAQESEwoGb3JpZ2luGA4gASgNSAyIAQESFAoHcXVlc3RpZBgPIAEoDUgNiAEBEhcKCmRyb3ByZWFzb24YECABKA1IDogBARIXCgptdXNpY2luZGV4GBEgASgNSA+IAQESFQoIZW50aW5kZXgYEiABKAVIEIgBARIVCghwZXRpbmRleBgTIAEoDUgRiAEBEjUKCWtleWNoYWlucxgUIAMoCzIiLkNFY29uSXRlbVByZXZpZXdEYXRhQmxvY2suU3RpY2tlchISCgVzdHlsZRgVIAEoDUgSiAEBGtsCCgdTdGlja2VyEhEKBHNsb3QYASABKA1IAIgBARIXCgpzdGlja2VyX2lkGAIgASgNSAGIAQESEQoEd2VhchgDIAEoAkgCiAEBEhIKBXNjYWxlGAQgASgCSAOIAQESFQoIcm90YXRpb24YBSABKAJIBIgBARIUCgd0aW50X2lkGAYgASgNSAWIAQESFQoIb2Zmc2V0X3gYByABKAJIBogBARIVCghvZmZzZXRfeRgIIAEoAkgHiAEBEhUKCG9mZnNldF96GAkgASgCSAiIAQESFAoHcGF0dGVybhgKIAEoDUgJiAEBQgcKBV9zbG90Qg0KC19zdGlja2VyX2lkQgcKBV93ZWFyQggKBl9zY2FsZUILCglfcm90YXRpb25CCgoIX3RpbnRfaWRCCwoJX29mZnNldF94QgsKCV9vZmZzZXRfeUILCglfb2Zmc2V0X3pCCgoIX3BhdHRlcm5CDAoKX2FjY291bnRpZEIJCgdfaXRlbWlkQgsKCV9kZWZpbmRleEINCgtfcGFpbnRpbmRleEIJCgdfcmFyaXR5QgoKCF9xdWFsaXR5QgwKCl9wYWludHdlYXJCDAoKX3BhaW50c2VlZEIVChNfa2lsbGVhdGVyc2NvcmV0eXBlQhEKD19raWxsZWF0ZXJ2YWx1ZUINCgtfY3VzdG9tbmFtZUIMCgpfaW52ZW50b3J5QgkKB19vcmlnaW5CCgoIX3F1ZXN0aWRCDQoLX2Ryb3ByZWFzb25CDQoLX211c2ljaW5kZXhCCwoJX2VudGluZGV4QgsKCV9wZXRpbmRleEIICgZfc3R5bGViBnByb3RvMw"), 0);
1869
+ //#endregion
1870
+ //#region src/models/inspect.ts
1871
+ const HEX_RE = /^[0-9a-fA-F]+$/;
1872
+ const wearView = /* @__PURE__ */ new DataView(/* @__PURE__ */ new ArrayBuffer(4));
1873
+ /**
1874
+ * Decode a CS2 masked preview token — the asset_properties propertyid-6
1875
+ * "certificate" hex — into a plain JSON object (uint64 ids as strings, camelCase
1876
+ * fields, only set fields present). Layout is `[xorKey][protobuf][crc32]`: every
1877
+ * byte after the key is XOR'd with it and the trailing 4-byte crc32 dropped.
1878
+ * `paintwear` is returned as the float wear (0..1), not its raw uint32 bits.
1879
+ * Returns `null` for non-hex input.
1880
+ */
1881
+ function decodePreviewToken(hex) {
1882
+ if (!hex || hex.length % 2 !== 0 || hex.length < 12 || !HEX_RE.test(hex)) return null;
1883
+ try {
1884
+ const bytes = Buffer$1.from(hex, "hex");
1885
+ const xorKey = bytes[0];
1886
+ for (let i = 1; i < bytes.length; i++) bytes[i] = bytes[i] ^ xorKey;
1887
+ const json = toJson(CEconItemPreviewDataBlockSchema, fromBinary(CEconItemPreviewDataBlockSchema, bytes.subarray(1, bytes.length - 4)));
1888
+ if (typeof json.paintwear === "number") {
1889
+ wearView.setUint32(0, json.paintwear >>> 0, true);
1890
+ json.paintwear = wearView.getFloat32(0, true);
1891
+ }
1892
+ return json;
1893
+ } catch {
1894
+ return null;
1895
+ }
1896
+ }
1897
+ //#endregion
1898
+ //#region src/session/tokens.ts
1899
+ const GENERATE_TOKEN_URL = "https://api.steampowered.com/IAuthenticationService/GenerateAccessTokenForApp/v1";
1900
+ var AccessTokenError = class extends Error {
1901
+ eresult;
1902
+ constructor(message, eresult) {
1903
+ super(message);
1904
+ this.eresult = eresult;
1905
+ this.name = "AccessTokenError";
1906
+ }
1907
+ };
1908
+ function decodeJwt(token) {
1909
+ try {
1910
+ const payload = token.split(".")[1];
1911
+ if (!payload) return null;
1912
+ const json = Buffer$1.from(payload.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf8");
1913
+ return JSON.parse(json);
1914
+ } catch {
1915
+ return null;
1916
+ }
1917
+ }
1918
+ function secondsUntilExpiry(token) {
1919
+ const exp = decodeJwt(token)?.exp;
1920
+ return typeof exp === "number" ? exp - Math.floor(Date.now() / 1e3) : NaN;
1921
+ }
1922
+ function encodeGenerateForAppRequest(refreshToken, renew = false) {
1923
+ const sub = decodeJwt(refreshToken)?.sub;
1924
+ if (!sub) throw new AccessTokenError("invalid refresh token: no steamid (sub) in payload");
1925
+ let steamId;
1926
+ try {
1927
+ steamId = BigInt(sub);
1928
+ } catch {
1929
+ throw new AccessTokenError(`invalid refresh token: non-numeric steamid '${sub}'`);
1930
+ }
1931
+ const msg = create(CAuthentication_AccessToken_GenerateForApp_RequestSchema, {
1932
+ refreshToken,
1933
+ steamid: steamId,
1934
+ renewalType: renew ? 1 : 0
1935
+ });
1936
+ return Buffer$1.from(toBinary(CAuthentication_AccessToken_GenerateForApp_RequestSchema, msg));
1937
+ }
1938
+ function decodeGenerateForAppResponse(input) {
1939
+ let accessToken;
1940
+ let refreshToken;
1941
+ try {
1942
+ const msg = fromBinary(CAuthentication_AccessToken_GenerateForApp_ResponseSchema, toUint8(input));
1943
+ accessToken = msg.accessToken;
1944
+ refreshToken = msg.refreshToken;
1945
+ } catch {
1946
+ throw new AccessTokenError("malformed GenerateAccessTokenForApp response");
1947
+ }
1948
+ if (!accessToken) throw new AccessTokenError("response had no access_token field");
1949
+ return refreshToken ? {
1950
+ accessToken,
1951
+ refreshToken
1952
+ } : { accessToken };
1953
+ }
1954
+ function toUint8(input) {
1955
+ return input instanceof Uint8Array && !Buffer$1.isBuffer(input) ? input : new Uint8Array(input);
1956
+ }
1957
+ const ERESULT_OK = "1";
1958
+ async function mintAccessToken(refreshToken, renew, post) {
1959
+ const { status, eresult, errorMessage, body } = await post(GENERATE_TOKEN_URL, encodeGenerateForAppRequest(refreshToken, renew).toString("base64"));
1960
+ if (status < 200 || status >= 300) throw new AccessTokenError(`GenerateAccessTokenForApp HTTP ${status} (eresult=${eresult ?? "?"}${errorMessage ? `, ${errorMessage}` : ""})`, eresult ? Number(eresult) : void 0);
1961
+ if (eresult && eresult !== ERESULT_OK) throw new AccessTokenError(`GenerateAccessTokenForApp failed: eresult=${eresult}${errorMessage ? ` (${errorMessage})` : ""}`, Number(eresult));
1962
+ if (!body.length) throw new AccessTokenError(`GenerateAccessTokenForApp empty response (eresult=${eresult ?? "?"})`);
1963
+ return decodeGenerateForAppResponse(body);
1964
+ }
1965
+ //#endregion
1966
+ //#region src/session/SessionManager.ts
1967
+ var SessionManager = class extends EventEmitter {
1968
+ refreshToken;
1969
+ accessToken;
1970
+ steamID;
1971
+ http;
1972
+ protoPost;
1973
+ minting;
1974
+ revoked = false;
1975
+ constructor(http, refreshToken) {
1976
+ super();
1977
+ const sub = decodeJwt(refreshToken)?.sub;
1978
+ if (!sub) throw new SteamSessionExpiredError("invalid refresh token: no steamid (sub) in payload");
1979
+ this.http = http;
1980
+ this.protoPost = createProtoPost(http);
1981
+ this.refreshToken = refreshToken;
1982
+ this.steamID = new SteamID(sub);
1983
+ }
1984
+ async getAccessToken() {
1985
+ if (this.revoked) throw new SteamSessionExpiredError("session has been revoked (logged out)");
1986
+ if (this.accessToken && secondsUntilExpiry(this.accessToken) > 300) return this.accessToken;
1987
+ if (!this.minting) this.minting = this.mint().finally(() => {
1988
+ this.minting = void 0;
1989
+ });
1990
+ return this.minting;
1991
+ }
1992
+ async listSessions() {
1993
+ const accessToken = await this.getAccessToken();
1994
+ return (await new AuthClient(this.http).getAuthSessionsForAccount(accessToken)).clientIds;
1995
+ }
1996
+ async logout(action = 0) {
1997
+ const accessToken = await this.getAccessToken();
1998
+ await new AuthClient(this.http).revokeRefreshToken(accessToken, action);
1999
+ this.revoked = true;
2000
+ this.accessToken = void 0;
2001
+ this.emit("debug", "refresh token revoked (logout)");
2002
+ }
2003
+ async setRefreshToken(refreshToken) {
2004
+ const sub = decodeJwt(refreshToken)?.sub;
2005
+ if (!sub) throw new SteamSessionExpiredError("invalid refresh token: no steamid (sub) in payload");
2006
+ if (sub !== this.steamID.getSteamID64()) throw new SteamError(`refresh token is for a different account (${sub} != ${this.steamID.getSteamID64()})`);
2007
+ this.refreshToken = refreshToken;
2008
+ this.revoked = false;
2009
+ this.accessToken = void 0;
2010
+ this.emit("refreshToken", refreshToken);
2011
+ await this.getAccessToken();
2012
+ }
2013
+ async mint() {
2014
+ const refreshExpiresIn = secondsUntilExpiry(this.refreshToken);
2015
+ if (refreshExpiresIn <= 0) throw this.expire("refresh token has expired — re-authentication required");
2016
+ const renew = refreshExpiresIn < REFRESH_TOKEN_RENEW_THRESHOLD_SECONDS;
2017
+ let result;
2018
+ try {
2019
+ result = await mintAccessToken(this.refreshToken, renew, this.protoPost);
2020
+ } catch (err) {
2021
+ if (err instanceof AccessTokenError && isTerminalAuthFailure(err)) throw this.expire(`refresh token rejected by Steam: ${err.message}`);
2022
+ throw err;
2023
+ }
2024
+ this.accessToken = result.accessToken;
2025
+ if (result.refreshToken && result.refreshToken !== this.refreshToken) {
2026
+ this.refreshToken = result.refreshToken;
2027
+ this.emit("refreshToken", result.refreshToken);
2028
+ }
2029
+ await this.applyLoginCookie();
2030
+ this.emit("debug", `minted access token${renew ? " + renewed refresh token" : ""}, expires in ${Math.round(secondsUntilExpiry(result.accessToken))}s`);
2031
+ return result.accessToken;
2032
+ }
2033
+ expire(message) {
2034
+ const err = new SteamSessionExpiredError(message);
2035
+ this.emit("sessionExpired", err);
2036
+ return err;
2037
+ }
2038
+ async applyLoginCookie() {
2039
+ const value = encodeURIComponent(`${this.steamID.getSteamID64()}||${this.accessToken}`);
2040
+ await this.http.setCookie(`steamLoginSecure=${value}`);
2041
+ }
2042
+ };
2043
+ function isTerminalAuthFailure(err) {
2044
+ return isTerminalAuthEResult(err.eresult);
2045
+ }
2046
+ //#endregion
2047
+ //#region src/community/userDetails.ts
2048
+ async function fetchUserDetails(http, url, referer, myAccountId, partnerAccountId) {
2049
+ const res = await http.get(url, {
2050
+ responseType: "text",
2051
+ headers: { Referer: referer }
2052
+ });
2053
+ if (res.statusCode !== 200) throw httpError(res);
2054
+ const html = res.body;
2055
+ checkCommunityError(html);
2056
+ if (!html.includes("g_rgAppContextData")) throw new SteamError("Failed to load the trade page for this user");
2057
+ return parseUserDetails(html, myAccountId, partnerAccountId);
2058
+ }
2059
+ function buildPartnerTradePageUrl(partnerAccountId, token) {
2060
+ return `${URLS.community}/tradeoffer/new/?partner=${partnerAccountId}${token ? `&token=${token}` : ""}`;
2061
+ }
2062
+ function parseUserDetails(html, myAccountId, partnerAccountId) {
2063
+ const myEscrowDays = matchInt(html, /var g_daysMyEscrow = (\d+);/);
2064
+ const theirEscrowDays = matchInt(html, /var g_daysTheirEscrow = (\d+);/);
2065
+ if (myEscrowDays === null || theirEscrowDays === null) throw new SteamError("Failed to parse escrow durations from the trade page");
2066
+ return {
2067
+ me: {
2068
+ personaName: matchJsString(html, "g_strYourPersonaName") ?? "",
2069
+ contexts: matchJson(html, /g_rgAppContextData\s*=\s*(\{.*?\});/),
2070
+ escrowDays: myEscrowDays,
2071
+ ...matchAvatars(html, myAccountId)
2072
+ },
2073
+ them: {
2074
+ personaName: matchJsString(html, "g_strTradePartnerPersonaName") ?? "",
2075
+ contexts: matchJson(html, /g_rgPartnerAppContextData\s*=\s*(\{.*?\});/),
2076
+ escrowDays: theirEscrowDays,
2077
+ probation: /g_bTradePartnerProbation\s*=\s*(?:true|1)\b/.test(html),
2078
+ ...matchAvatars(html, partnerAccountId)
2079
+ }
2080
+ };
2081
+ }
2082
+ function matchInt(html, re) {
2083
+ const m = html.match(re);
2084
+ return m?.[1] !== void 0 ? Number.parseInt(m[1], 10) : null;
2085
+ }
2086
+ function matchJson(html, re) {
2087
+ const m = html.match(re);
2088
+ if (!m?.[1]) return null;
2089
+ try {
2090
+ return JSON.parse(m[1]);
2091
+ } catch {
2092
+ return null;
2093
+ }
2094
+ }
2095
+ function matchJsString(html, varName) {
2096
+ const re = new RegExp(`var ${varName}\\s*=\\s*("(?:\\\\.|[^"\\\\])*");`);
2097
+ const m = html.match(re);
2098
+ if (!m?.[1]) return void 0;
2099
+ try {
2100
+ return JSON.parse(m[1]);
2101
+ } catch {
2102
+ return m[1].slice(1, -1);
2103
+ }
2104
+ }
2105
+ function matchAvatars(html, accountId) {
2106
+ const re = new RegExp(`<img src="([^"]+)"(?: alt="[^"]*")? data-miniprofile="${accountId}">`);
2107
+ const m = html.match(re);
2108
+ if (!m?.[1]) return {
2109
+ avatarIcon: void 0,
2110
+ avatarMedium: void 0,
2111
+ avatarFull: void 0
2112
+ };
2113
+ const icon = m[1];
2114
+ return {
2115
+ avatarIcon: icon,
2116
+ avatarMedium: icon.replace(/\.jpg$/, "_medium.jpg"),
2117
+ avatarFull: icon.replace(/\.jpg$/, "_full.jpg")
2118
+ };
2119
+ }
2120
+ //#endregion
2121
+ //#region src/trade/exchange.ts
2122
+ function parseTrade(trade, descriptions) {
2123
+ const toItem = (a) => buildItem(a, descriptions.get(`${a.appid}_${a.classid}_${a.instanceid ?? "0"}`), [], a.contextid);
2124
+ return {
2125
+ status: trade.status,
2126
+ tradeInitTime: /* @__PURE__ */ new Date(trade.time_init * 1e3),
2127
+ settlementTime: trade.time_settlement ? /* @__PURE__ */ new Date(trade.time_settlement * 1e3) : null,
2128
+ receivedItems: (trade.assets_received ?? []).map(toItem),
2129
+ sentItems: (trade.assets_given ?? []).map(toItem),
2130
+ usedInventoryFallback: false
2131
+ };
2132
+ }
2133
+ async function getTradeStatus(api, tradeId) {
2134
+ const body = await api.call({
2135
+ httpMethod: "GET",
2136
+ iface: "IEconService",
2137
+ method: "GetTradeStatus",
2138
+ retryAfterMs: RETRY_AFTER.GetTradeStatus,
2139
+ input: {
2140
+ tradeid: tradeId,
2141
+ get_descriptions: 1,
2142
+ ...LANG
2143
+ }
2144
+ });
2145
+ const trades = body.response?.trades ?? [];
2146
+ const trade = trades.find((t) => t.tradeid === tradeId) ?? (trades.length === 1 ? trades[0] : void 0);
2147
+ if (!trade) throw new SteamError(`Trade ${tradeId} not found in GetTradeStatus response`);
2148
+ return parseTrade(trade, buildDescriptionMap(body.response?.descriptions));
2149
+ }
2150
+ async function getTradeHistory(api, opts = {}) {
2151
+ const resp = (await api.call({
2152
+ httpMethod: "GET",
2153
+ iface: "IEconService",
2154
+ method: "GetTradeHistory",
2155
+ retryAfterMs: RETRY_AFTER.GetTradeHistory,
2156
+ input: {
2157
+ max_trades: opts.maxTrades ?? 100,
2158
+ get_descriptions: 1,
2159
+ include_failed: opts.includeFailed ? 1 : 0,
2160
+ include_total: opts.includeTotal ? 1 : 0,
2161
+ ...opts.startAfterTime !== void 0 ? { start_after_time: opts.startAfterTime } : {},
2162
+ ...opts.startAfterTradeId !== void 0 ? { start_after_tradeid: opts.startAfterTradeId } : {},
2163
+ ...opts.navigatingBack ? { navigating_back: 1 } : {},
2164
+ ...LANG
2165
+ }
2166
+ })).response ?? {};
2167
+ const descriptions = buildDescriptionMap(resp.descriptions);
2168
+ return {
2169
+ trades: (resp.trades ?? []).map((t) => ({
2170
+ ...parseTrade(t, descriptions),
2171
+ tradeId: t.tradeid,
2172
+ partnerSteamId: t.steamid_other
2173
+ })),
2174
+ more: resp.more ?? false,
2175
+ totalTrades: resp.total_trades
2176
+ };
2177
+ }
2178
+ async function getTradeOffersSummary(api) {
2179
+ const r = (await api.call({
2180
+ httpMethod: "GET",
2181
+ iface: "IEconService",
2182
+ method: "GetTradeOffersSummary",
2183
+ retryAfterMs: RETRY_AFTER.GetTradeOffersSummary,
2184
+ input: { time_last_visit: 0 }
2185
+ })).response ?? {};
2186
+ return {
2187
+ pending_received_count: r.pending_received_count ?? 0,
2188
+ new_received_count: r.new_received_count ?? 0,
2189
+ updated_received_count: r.updated_received_count ?? 0,
2190
+ historical_received_count: r.historical_received_count ?? 0,
2191
+ pending_sent_count: r.pending_sent_count ?? 0,
2192
+ newly_accepted_sent_count: r.newly_accepted_sent_count ?? 0,
2193
+ updated_sent_count: r.updated_sent_count ?? 0,
2194
+ historical_sent_count: r.historical_sent_count ?? 0,
2195
+ escrow_received_count: r.escrow_received_count ?? 0,
2196
+ escrow_sent_count: r.escrow_sent_count ?? 0
2197
+ };
2198
+ }
2199
+ //#endregion
2200
+ //#region src/trade/polling.ts
2201
+ const DEFAULT_POLL_INTERVAL = 1e4;
2202
+ const DEFAULT_POLL_FULL_UPDATE_INTERVAL = 3e5;
2203
+ const DEFAULT_POLL_MAX_AGE_MS = 2592e6;
2204
+ const POLL_BACKDATING_BUFFER = 1800;
2205
+ const MINIMUM_POLL_INTERVAL = 1e3;
2206
+ const OFFER_MAX_LIFETIME_MS = 12096e5;
2207
+ function emptyPollData() {
2208
+ return {
2209
+ offersSince: 0,
2210
+ sent: {},
2211
+ received: {},
2212
+ timestamps: {}
2213
+ };
2214
+ }
2215
+ var Poller = class {
2216
+ source;
2217
+ pollData;
2218
+ pollInterval;
2219
+ fullInterval;
2220
+ maxAgeMs;
2221
+ cancelTime;
2222
+ store;
2223
+ timer;
2224
+ running = false;
2225
+ stopped = true;
2226
+ loaded = false;
2227
+ constructor(source, options = {}) {
2228
+ this.source = source;
2229
+ this.pollInterval = Math.max(options.pollInterval ?? 1e4, MINIMUM_POLL_INTERVAL);
2230
+ this.fullInterval = options.pollFullUpdateInterval ?? 3e5;
2231
+ this.maxAgeMs = options.maxAgeMs ?? 2592e6;
2232
+ this.cancelTime = options.cancelTime;
2233
+ this.store = options.store;
2234
+ this.pollData = options.pollData ?? emptyPollData();
2235
+ }
2236
+ start() {
2237
+ this.stopped = false;
2238
+ this.schedule(0);
2239
+ }
2240
+ stop() {
2241
+ this.stopped = true;
2242
+ if (this.timer) clearTimeout(this.timer);
2243
+ this.timer = void 0;
2244
+ }
2245
+ async poll(forceFull = false) {
2246
+ if (this.running) return this.pollInterval;
2247
+ this.running = true;
2248
+ try {
2249
+ if (this.store && !this.loaded) await this.loadFromStore();
2250
+ let result;
2251
+ try {
2252
+ result = await this.runCycle(forceFull);
2253
+ } catch (err) {
2254
+ const error = err;
2255
+ this.source.emit("debug", `Trade offer poll failed: ${error.message}`);
2256
+ this.source.emit("pollFailure", error);
2257
+ return this.backoffDelay(error);
2258
+ }
2259
+ if (this.store && result.changed) await this.persist();
2260
+ return this.pollInterval;
2261
+ } finally {
2262
+ this.running = false;
2263
+ }
2264
+ }
2265
+ async pollOnce(forceFull = false) {
2266
+ if (this.store) await this.loadFromStore();
2267
+ const { changes, changed } = await this.runCycle(forceFull);
2268
+ if (this.store && changed) await this.persist();
2269
+ return {
2270
+ changes,
2271
+ pollData: this.pollData
2272
+ };
2273
+ }
2274
+ async loadFromStore() {
2275
+ const loaded = await this.store?.load();
2276
+ if (loaded) this.pollData = loaded;
2277
+ this.loaded = true;
2278
+ }
2279
+ async persist() {
2280
+ try {
2281
+ await this.store?.save(this.pollData);
2282
+ } catch (err) {
2283
+ this.source.emit("debug", `pollData store save failed: ${err.message}`);
2284
+ }
2285
+ }
2286
+ schedule(ms) {
2287
+ if (this.stopped) return;
2288
+ if (this.timer) clearTimeout(this.timer);
2289
+ this.timer = setTimeout(() => {
2290
+ this.poll().then((delay) => this.schedule(delay));
2291
+ }, Math.max(0, ms));
2292
+ }
2293
+ backoffDelay(err) {
2294
+ if (err instanceof RateLimitError) {
2295
+ const wait = err.unlockAt - Date.now();
2296
+ if (wait > this.pollInterval) return Math.max(wait, MINIMUM_POLL_INTERVAL);
2297
+ }
2298
+ return this.pollInterval;
2299
+ }
2300
+ async runCycle(forceFull) {
2301
+ const before = JSON.stringify(this.pollData);
2302
+ const sinceBase = this.pollData.offersSince ? this.pollData.offersSince - POLL_BACKDATING_BUFFER : 0;
2303
+ let fullUpdate = forceFull;
2304
+ let cutoff = sinceBase;
2305
+ if (forceFull || Date.now() - (this.pollData.lastFullUpdate ?? 0) >= this.fullInterval) {
2306
+ fullUpdate = true;
2307
+ this.pollData.lastFullUpdate = Date.now();
2308
+ cutoff = this.sweepCutoffSeconds();
2309
+ }
2310
+ this.source.emit("debug", `Polling trade offers since ${cutoff}${fullUpdate ? " (full update)" : ""}`);
2311
+ const result = await this.source.getTradeOffers(fullUpdate ? 3 : 1, /* @__PURE__ */ new Date(cutoff * 1e3));
2312
+ const changes = this.process(result.sent, result.received, fullUpdate);
2313
+ if (this.cancelTime !== void 0) this.autoCancel(result.sent, this.cancelTime);
2314
+ this.prune(seenIds(result.sent, result.received));
2315
+ const changed = JSON.stringify(this.pollData) !== before;
2316
+ this.source.emit("pollSuccess");
2317
+ if (changed) this.source.emit("pollData", this.pollData);
2318
+ return {
2319
+ changes,
2320
+ changed
2321
+ };
2322
+ }
2323
+ autoCancel(sentList, cancelTime) {
2324
+ const now = Date.now();
2325
+ for (const offer of sentList) {
2326
+ if (offer.state !== 2 || !offer.updated) continue;
2327
+ if (now - offer.updated.getTime() < cancelTime) continue;
2328
+ offer.cancel().then(() => this.source.emit("sentOfferCanceled", offer, "cancelTime"), (err) => this.source.emit("debug", `Can't auto-cancel offer #${offer.id}: ${err.message}`));
2329
+ }
2330
+ }
2331
+ sweepCutoffSeconds() {
2332
+ return Math.max(1, Math.floor((Date.now() - this.maxAgeMs) / 1e3));
2333
+ }
2334
+ process(sentList, receivedList, fullUpdate) {
2335
+ const changes = [];
2336
+ const { sent, received, timestamps } = this.pollData;
2337
+ let hasGlitched = false;
2338
+ for (const offer of sentList) {
2339
+ if (!offer.id) continue;
2340
+ const known = sent[offer.id];
2341
+ if (known === void 0) this.record(changes, {
2342
+ type: "unknownOfferSent",
2343
+ offer
2344
+ });
2345
+ else if (offer.state !== known) {
2346
+ if (offer.glitched) {
2347
+ hasGlitched = true;
2348
+ continue;
2349
+ }
2350
+ this.record(changes, {
2351
+ type: "sentOfferChanged",
2352
+ offer,
2353
+ oldState: known
2354
+ });
2355
+ }
2356
+ sent[offer.id] = offer.state;
2357
+ this.stamp(timestamps, offer);
2358
+ }
2359
+ for (const offer of receivedList) {
2360
+ if (!offer.id) continue;
2361
+ if (offer.glitched) {
2362
+ hasGlitched = true;
2363
+ continue;
2364
+ }
2365
+ const known = received[offer.id];
2366
+ if (known === void 0 && offer.state === 2) this.record(changes, {
2367
+ type: "newOffer",
2368
+ offer
2369
+ });
2370
+ else if (known !== void 0 && offer.state !== known) this.record(changes, {
2371
+ type: "receivedOfferChanged",
2372
+ offer,
2373
+ oldState: known
2374
+ });
2375
+ received[offer.id] = offer.state;
2376
+ this.stamp(timestamps, offer);
2377
+ }
2378
+ if (!hasGlitched) {
2379
+ let latest = this.pollData.offersSince;
2380
+ for (const offer of [...sentList, ...receivedList]) {
2381
+ const updated = offer.updated ? Math.floor(offer.updated.getTime() / 1e3) : 0;
2382
+ if (updated > latest) latest = updated;
2383
+ }
2384
+ if (fullUpdate) latest = Math.max(latest, Math.floor(Date.now() / 1e3));
2385
+ this.pollData.offersSince = latest;
2386
+ }
2387
+ return changes;
2388
+ }
2389
+ record(changes, change) {
2390
+ changes.push(change);
2391
+ switch (change.type) {
2392
+ case "newOffer":
2393
+ this.source.emit("newOffer", change.offer);
2394
+ break;
2395
+ case "unknownOfferSent":
2396
+ this.source.emit("unknownOfferSent", change.offer);
2397
+ break;
2398
+ case "sentOfferChanged":
2399
+ this.source.emit("sentOfferChanged", change.offer, change.oldState);
2400
+ break;
2401
+ case "receivedOfferChanged":
2402
+ this.source.emit("receivedOfferChanged", change.offer, change.oldState);
2403
+ break;
2404
+ }
2405
+ const previousState = "oldState" in change ? change.oldState : void 0;
2406
+ this.source.emit("offerUpdate", {
2407
+ offer: change.offer,
2408
+ ...previousState !== void 0 ? { previousState } : {}
2409
+ });
2410
+ }
2411
+ prune(seen) {
2412
+ const cutoff = Math.floor((Date.now() - this.maxAgeMs - OFFER_MAX_LIFETIME_MS) / 1e3);
2413
+ this.pruneMap(this.pollData.sent, seen, cutoff);
2414
+ this.pruneMap(this.pollData.received, seen, cutoff);
2415
+ }
2416
+ pruneMap(map, seen, cutoff) {
2417
+ for (const id of Object.keys(map)) {
2418
+ const state = map[id];
2419
+ const created = this.pollData.timestamps[id];
2420
+ if (!seen.has(id) && state !== void 0 && isTerminalState(state) && created !== void 0 && created > 0 && created < cutoff) {
2421
+ delete map[id];
2422
+ delete this.pollData.timestamps[id];
2423
+ }
2424
+ }
2425
+ }
2426
+ stamp(timestamps, offer) {
2427
+ if (offer.id && offer.created) timestamps[offer.id] = Math.floor(offer.created.getTime() / 1e3);
2428
+ }
2429
+ };
2430
+ function seenIds(sent, received) {
2431
+ const ids = /* @__PURE__ */ new Set();
2432
+ for (const offer of [...sent, ...received]) if (offer.id) ids.add(offer.id);
2433
+ return ids;
2434
+ }
2435
+ //#endregion
2436
+ //#region src/trade/TradeOffer.ts
2437
+ const TWO_WEEKS_MS = 12096e5;
2438
+ var TradeOffer = class TradeOffer {
2439
+ deps;
2440
+ id;
2441
+ partner;
2442
+ token;
2443
+ message = "";
2444
+ state = 1;
2445
+ itemsToGive = [];
2446
+ itemsToReceive = [];
2447
+ isOurOffer = true;
2448
+ tradeID;
2449
+ confirmationMethod = 0;
2450
+ escrowEnds;
2451
+ settlementDate;
2452
+ delaySettlement = false;
2453
+ created;
2454
+ updated;
2455
+ expires;
2456
+ fromRealTimeTrade = false;
2457
+ glitched = false;
2458
+ countering;
2459
+ constructor(deps, init) {
2460
+ this.deps = deps;
2461
+ this.partner = init.partner;
2462
+ this.token = init.token;
2463
+ this.id = init.id;
2464
+ }
2465
+ static fromData(deps, raw, descriptions) {
2466
+ if (!raw.accountid_other) throw new SteamError(`Trade offer ${raw.tradeofferid} is missing a partner accountid`);
2467
+ const offer = new TradeOffer(deps, {
2468
+ partner: SteamID.fromIndividualAccountID(raw.accountid_other),
2469
+ id: raw.tradeofferid
2470
+ });
2471
+ offer.message = raw.message ?? "";
2472
+ offer.state = raw.trade_offer_state;
2473
+ offer.isOurOffer = raw.is_our_offer;
2474
+ offer.fromRealTimeTrade = raw.from_real_time_trade ?? false;
2475
+ const mapItem = (a) => descriptions ? buildItem(a, descriptions.get(`${a.appid}_${a.classid}_${a.instanceid}`), [], a.contextid) : toTradeItem(a);
2476
+ offer.itemsToGive = (raw.items_to_give ?? []).map(mapItem);
2477
+ offer.itemsToReceive = (raw.items_to_receive ?? []).map(mapItem);
2478
+ offer.confirmationMethod = raw.confirmation_method ?? 0;
2479
+ if (raw.tradeid) offer.tradeID = raw.tradeid;
2480
+ if (raw.escrow_end_date) offer.escrowEnds = /* @__PURE__ */ new Date(raw.escrow_end_date * 1e3);
2481
+ if (raw.settlement_date) offer.settlementDate = /* @__PURE__ */ new Date(raw.settlement_date * 1e3);
2482
+ offer.delaySettlement = raw.delay_settlement ?? false;
2483
+ if (raw.time_created) offer.created = /* @__PURE__ */ new Date(raw.time_created * 1e3);
2484
+ if (raw.time_updated) offer.updated = /* @__PURE__ */ new Date(raw.time_updated * 1e3);
2485
+ if (raw.expiration_time) offer.expires = /* @__PURE__ */ new Date(raw.expiration_time * 1e3);
2486
+ const allItems = [...offer.itemsToGive, ...offer.itemsToReceive];
2487
+ offer.glitched = !isTerminalState(offer.state) && (allItems.length === 0 || descriptions !== void 0 && allItems.some((i) => !i.name));
2488
+ return offer;
2489
+ }
2490
+ containsItem(item) {
2491
+ return [...this.itemsToGive, ...this.itemsToReceive].some((i) => i.appid === item.appid && i.contextid === item.contextid && i.assetid === item.assetid);
2492
+ }
2493
+ give(items) {
2494
+ this.itemsToGive.push(...items);
2495
+ return this;
2496
+ }
2497
+ receive(items) {
2498
+ this.itemsToReceive.push(...items);
2499
+ return this;
2500
+ }
2501
+ setMessage(message) {
2502
+ if (this.id) throw new SteamError("Cannot set a message on an already-sent offer");
2503
+ this.message = message.slice(0, 128);
2504
+ return this;
2505
+ }
2506
+ async send() {
2507
+ if (this.id) throw new SteamError("This offer has already been sent");
2508
+ if (this.itemsToGive.length + this.itemsToReceive.length === 0) throw new SteamError("Cannot send an empty trade offer");
2509
+ await this.deps.session.getAccessToken();
2510
+ const sessionid = await this.deps.http.getSessionId();
2511
+ await acknowledgeTradeProtection(this.deps.http).catch((err) => {
2512
+ if (err instanceof SteamSessionExpiredError) throw err;
2513
+ });
2514
+ const offerdata = {
2515
+ newversion: true,
2516
+ version: this.itemsToGive.length + this.itemsToReceive.length + 1,
2517
+ me: {
2518
+ assets: this.itemsToGive.map(toAsset),
2519
+ currency: [],
2520
+ ready: false
2521
+ },
2522
+ them: {
2523
+ assets: this.itemsToReceive.map(toAsset),
2524
+ currency: [],
2525
+ ready: false
2526
+ }
2527
+ };
2528
+ const createParams = {};
2529
+ if (this.token) createParams.trade_offer_access_token = this.token;
2530
+ const res = await this.deps.http.post(`${URLS.community}/tradeoffer/new/send`, {
2531
+ responseType: "json",
2532
+ referer: this.newOfferReferer(),
2533
+ form: {
2534
+ sessionid,
2535
+ serverid: 1,
2536
+ partner: this.partner.getSteamID64(),
2537
+ tradeoffermessage: this.message,
2538
+ json_tradeoffer: JSON.stringify(offerdata),
2539
+ captcha: "",
2540
+ trade_offer_create_params: JSON.stringify(createParams),
2541
+ ...this.countering ? { tradeofferid_countered: this.countering } : {}
2542
+ }
2543
+ });
2544
+ if (res.statusCode === 401) throw new SteamSessionExpiredError();
2545
+ const body = res.body;
2546
+ if (body?.strError) throw parseStrError(body.strError);
2547
+ if (res.statusCode !== 200) throw httpError(res);
2548
+ if (!body) throw new SteamError("Malformed JSON response");
2549
+ if (body.tradeofferid) {
2550
+ this.id = body.tradeofferid;
2551
+ this.state = 2;
2552
+ this.created = /* @__PURE__ */ new Date();
2553
+ this.updated = /* @__PURE__ */ new Date();
2554
+ this.expires = new Date(Date.now() + TWO_WEEKS_MS);
2555
+ }
2556
+ this.confirmationMethod = 0;
2557
+ if (body.needs_email_confirmation) {
2558
+ this.state = 9;
2559
+ this.confirmationMethod = 1;
2560
+ }
2561
+ if (body.needs_mobile_confirmation) {
2562
+ this.state = 9;
2563
+ this.confirmationMethod = 2;
2564
+ }
2565
+ if (this.state === 9) return "needs_confirmation";
2566
+ if (body.tradeofferid) return "sent";
2567
+ throw new SteamError("Unknown response sending trade offer");
2568
+ }
2569
+ async accept() {
2570
+ if (!this.id) throw new SteamError("Cannot accept an unsent offer");
2571
+ if (this.state !== 2) throw new SteamError(`Offer #${this.id} is not active, so it may not be accepted`);
2572
+ if (this.isOurOffer) throw new SteamError(`Cannot accept our own offer #${this.id}`);
2573
+ await this.deps.session.getAccessToken();
2574
+ const sessionid = await this.deps.http.getSessionId();
2575
+ const res = await this.deps.http.post(`${URLS.community}/tradeoffer/${this.id}/accept`, {
2576
+ responseType: "json",
2577
+ referer: `${URLS.community}/tradeoffer/${this.id}/`,
2578
+ form: {
2579
+ sessionid,
2580
+ serverid: 1,
2581
+ tradeofferid: this.id,
2582
+ partner: this.partner.getSteamID64(),
2583
+ captcha: ""
2584
+ }
2585
+ });
2586
+ if (res.statusCode === 403) throw new SteamSessionExpiredError();
2587
+ const body = res.body;
2588
+ if (body?.strError) throw parseStrError(body.strError);
2589
+ if (res.statusCode !== 200) throw httpError(res);
2590
+ if (!body) throw new SteamError("Malformed JSON response");
2591
+ if (body.tradeid) this.tradeID = body.tradeid;
2592
+ if (body.needs_mobile_confirmation || body.needs_email_confirmation) {
2593
+ this.confirmationMethod = body.needs_mobile_confirmation ? 2 : 1;
2594
+ return "needs_confirmation";
2595
+ }
2596
+ this.state = 3;
2597
+ try {
2598
+ const refreshed = await this.deps.trade.getTradeOffer(this.id);
2599
+ this.state = refreshed.state;
2600
+ this.escrowEnds = refreshed.escrowEnds;
2601
+ } catch {}
2602
+ return this.state === 11 ? "escrow" : "accepted";
2603
+ }
2604
+ async cancel() {
2605
+ if (!this.id) throw new SteamError("Cannot cancel or decline an unsent offer");
2606
+ if (this.state !== 2 && this.state !== 9) throw new SteamError(`Offer #${this.id} is not active, so it may not be cancelled or declined`);
2607
+ await this.deps.session.getAccessToken();
2608
+ const sessionid = await this.deps.http.getSessionId();
2609
+ const action = this.isOurOffer ? "cancel" : "decline";
2610
+ const res = await this.deps.http.post(`${URLS.community}/tradeoffer/${this.id}/${action}`, {
2611
+ responseType: "json",
2612
+ referer: this.offerReferer(),
2613
+ form: { sessionid }
2614
+ });
2615
+ if (res.statusCode === 401) throw new SteamSessionExpiredError();
2616
+ const body = res.body;
2617
+ if (body?.strError) throw parseStrError(body.strError);
2618
+ if (res.statusCode !== 200) throw httpError(res);
2619
+ if (!body) throw new SteamError("Malformed JSON response");
2620
+ if (body.tradeofferid !== this.id) throw new SteamError("Wrong response cancelling offer");
2621
+ this.state = this.isOurOffer ? 6 : 7;
2622
+ this.updated = /* @__PURE__ */ new Date();
2623
+ }
2624
+ decline() {
2625
+ return this.cancel();
2626
+ }
2627
+ async confirm() {
2628
+ if (!this.id) throw new ConfirmationError("Cannot confirm an unsent offer");
2629
+ if (!this.deps.confirmations.hasIdentitySecret) throw new ConfirmationError("identitySecret is required to confirm trade offers — construct SteamMobile with { identitySecret }");
2630
+ await this.deps.confirmations.acceptConfirmationForObject(this.id);
2631
+ }
2632
+ counter() {
2633
+ if (!this.id) throw new SteamError("Cannot counter an unsent offer");
2634
+ if (this.state !== 2) throw new SteamError(`Offer #${this.id} is not active, so it may not be countered`);
2635
+ const next = new TradeOffer(this.deps, {
2636
+ partner: this.partner,
2637
+ ...this.token ? { token: this.token } : {}
2638
+ });
2639
+ next.countering = this.id;
2640
+ next.isOurOffer = true;
2641
+ next.itemsToGive = this.itemsToGive.map((i) => ({ ...i }));
2642
+ next.itemsToReceive = this.itemsToReceive.map((i) => ({ ...i }));
2643
+ next.message = this.message;
2644
+ return next;
2645
+ }
2646
+ getTradeStatus() {
2647
+ if (!this.tradeID) throw new SteamError("No trade ID — getTradeStatus needs an accepted offer");
2648
+ return this.deps.trade.getTradeStatus({ tradeId: this.tradeID });
2649
+ }
2650
+ getPartnerInventory(appid, contextid, tradableOnly) {
2651
+ const target = this.partnerTarget();
2652
+ return this.deps.trade.getInventory(target, appid, contextid, tradableOnly !== void 0 ? { tradableOnly } : {});
2653
+ }
2654
+ async getUserDetails() {
2655
+ if (this.id && this.isOurOffer) throw new SteamError("Cannot get user details for an offer that we sent");
2656
+ if (this.id && this.state !== 2) throw new SteamError("Cannot get user details for an offer that is sent and not Active");
2657
+ await this.deps.session.getAccessToken();
2658
+ const url = this.id ? `${URLS.community}/tradeoffer/${this.id}/` : buildPartnerTradePageUrl(this.partner.accountid, this.token);
2659
+ const referer = `${URLS.community}/profiles/${this.partner.getSteamID64()}`;
2660
+ return fetchUserDetails(this.deps.http, url, referer, this.deps.session.steamID.accountid, this.partner.accountid);
2661
+ }
2662
+ partnerTarget() {
2663
+ const steamId = this.partner.getSteamID64();
2664
+ return this.token ? {
2665
+ steamId,
2666
+ token: this.token
2667
+ } : { steamId };
2668
+ }
2669
+ newOfferReferer() {
2670
+ const token = this.token ? `&token=${this.token}` : "";
2671
+ return `${URLS.community}/tradeoffer/${this.id ?? "new"}/?partner=${this.partner.accountid}${token}`;
2672
+ }
2673
+ offerReferer() {
2674
+ const token = this.token ? `&token=${this.token}` : "";
2675
+ return `${URLS.community}/tradeoffer/${this.id}/?partner=${this.partner.accountid}${token}`;
2676
+ }
2677
+ };
2678
+ function toAsset(item) {
2679
+ return {
2680
+ appid: item.appid,
2681
+ contextid: item.contextid,
2682
+ amount: item.amount && item.amount > 0 ? item.amount : 1,
2683
+ assetid: item.assetid
2684
+ };
2685
+ }
2686
+ function toTradeItem(raw) {
2687
+ return {
2688
+ appid: raw.appid,
2689
+ contextid: raw.contextid,
2690
+ assetid: raw.assetid,
2691
+ amount: Number(raw.amount) || 1
2692
+ };
2693
+ }
2694
+ //#endregion
2695
+ //#region src/trade/TradeNamespace.ts
2696
+ const FUTURE_CUTOFF_MS = 31536e6;
2697
+ var TradeNamespace = class extends EventEmitter {
2698
+ api;
2699
+ http;
2700
+ session;
2701
+ confirmations;
2702
+ poller;
2703
+ constructor(api, http, session, confirmations) {
2704
+ super();
2705
+ this.api = api;
2706
+ this.http = http;
2707
+ this.session = session;
2708
+ this.confirmations = confirmations;
2709
+ }
2710
+ offerDeps() {
2711
+ return {
2712
+ http: this.http,
2713
+ session: this.session,
2714
+ confirmations: this.confirmations,
2715
+ trade: this
2716
+ };
2717
+ }
2718
+ createOffer(target) {
2719
+ const { steamId, token } = resolveTarget(target);
2720
+ return new TradeOffer(this.offerDeps(), {
2721
+ partner: new SteamID(steamId),
2722
+ ...token ? { token } : {}
2723
+ });
2724
+ }
2725
+ async getTradeOffer(id) {
2726
+ const body = await this.api.call({
2727
+ httpMethod: "GET",
2728
+ iface: "IEconService",
2729
+ method: "GetTradeOffer",
2730
+ retryAfterMs: RETRY_AFTER.GetTradeOffer,
2731
+ input: {
2732
+ tradeofferid: id,
2733
+ get_descriptions: 1,
2734
+ ...LANG
2735
+ }
2736
+ });
2737
+ const raw = body.response?.offer;
2738
+ if (!raw) throw new SteamError(`Trade offer ${id} not found`);
2739
+ return TradeOffer.fromData(this.offerDeps(), raw, buildDescriptionMap(body.response?.descriptions));
2740
+ }
2741
+ async getTradeOffers(filter = 1, historicalCutoff) {
2742
+ const cutoff = historicalCutoff ?? new Date(Date.now() + FUTURE_CUTOFF_MS);
2743
+ const sentRaw = [];
2744
+ const receivedRaw = [];
2745
+ const descriptions = [];
2746
+ let cursor = 0;
2747
+ do {
2748
+ const response = (await this.api.call({
2749
+ httpMethod: "GET",
2750
+ iface: "IEconService",
2751
+ method: "GetTradeOffers",
2752
+ retryAfterMs: RETRY_AFTER.GetTradeOffers,
2753
+ input: {
2754
+ get_sent_offers: 1,
2755
+ get_received_offers: 1,
2756
+ get_descriptions: 1,
2757
+ active_only: filter === 1 ? 1 : 0,
2758
+ historical_only: filter === 2 ? 1 : 0,
2759
+ time_historical_cutoff: Math.floor(cutoff.getTime() / 1e3),
2760
+ cursor,
2761
+ ...LANG
2762
+ }
2763
+ })).response ?? {};
2764
+ sentRaw.push(...response.trade_offers_sent ?? []);
2765
+ receivedRaw.push(...response.trade_offers_received ?? []);
2766
+ descriptions.push(...response.descriptions ?? []);
2767
+ const next = response.next_cursor ?? 0;
2768
+ if (next !== 0 && next === cursor) break;
2769
+ cursor = next;
2770
+ if (cursor) this.emit("debug", `GetTradeOffers with cursor ${cursor}`);
2771
+ } while (cursor);
2772
+ const all = [...sentRaw, ...receivedRaw];
2773
+ if (all.length > 0 && (all.every(offerMalformed) || all.some(offerSuperMalformed))) throw new SteamError("Data temporarily unavailable");
2774
+ const descMap = buildDescriptionMap(descriptions);
2775
+ const build = (raw) => TradeOffer.fromData(this.offerDeps(), sanitizeRawOffer(raw), descMap);
2776
+ return {
2777
+ sent: sentRaw.map(build),
2778
+ received: receivedRaw.map(build)
2779
+ };
2780
+ }
2781
+ getTradeStatus(opts) {
2782
+ return getTradeStatus(this.api, opts.tradeId);
2783
+ }
2784
+ async getUserDetails(target) {
2785
+ await this.session.getAccessToken();
2786
+ const { steamId, token } = resolveTarget(target);
2787
+ const partnerAccountId = new SteamID(steamId).accountid;
2788
+ return fetchUserDetails(this.http, buildPartnerTradePageUrl(partnerAccountId, token), `${URLS.community}/profiles/${steamId}`, this.session.steamID.accountid, partnerAccountId);
2789
+ }
2790
+ async getEscrow(target) {
2791
+ const { steamId, token } = resolveTarget(target);
2792
+ const r = (await this.api.call({
2793
+ httpMethod: "GET",
2794
+ iface: "IEconService",
2795
+ method: "GetTradeHoldDurations",
2796
+ retryAfterMs: RETRY_AFTER.GetTradeHoldDurations,
2797
+ input: {
2798
+ steamid_target: steamId,
2799
+ ...token ? { trade_offer_access_token: token } : {}
2800
+ }
2801
+ })).response ?? {};
2802
+ const zero = { escrow_end_duration_seconds: 0 };
2803
+ return {
2804
+ me: r.my_escrow ?? zero,
2805
+ them: r.their_escrow ?? zero,
2806
+ both: r.both_escrow ?? zero
2807
+ };
2808
+ }
2809
+ getTradeHistory(opts) {
2810
+ return getTradeHistory(this.api, opts);
2811
+ }
2812
+ getTradeOffersSummary() {
2813
+ return getTradeOffersSummary(this.api);
2814
+ }
2815
+ async reconcile(ids) {
2816
+ const results = await Promise.allSettled(ids.map((id) => this.getTradeOffer(id)));
2817
+ const map = /* @__PURE__ */ new Map();
2818
+ results.forEach((res, i) => {
2819
+ const id = ids[i];
2820
+ if (id !== void 0 && res.status === "fulfilled") map.set(id, res.value);
2821
+ });
2822
+ return map;
2823
+ }
2824
+ async getOffersContainingItems(items, includeInactive = false) {
2825
+ const { sent, received } = await this.getTradeOffers(includeInactive ? 3 : 1);
2826
+ return [...sent, ...received].filter((offer) => items.every((item) => offer.containsItem(item)));
2827
+ }
2828
+ startPolling(options = {}) {
2829
+ this.poller?.stop();
2830
+ this.poller = new Poller(this, options);
2831
+ this.poller.start();
2832
+ }
2833
+ stopPolling() {
2834
+ this.poller?.stop();
2835
+ this.poller = void 0;
2836
+ }
2837
+ async pollOnce(options = {}) {
2838
+ const { forceFull, ...pollOptions } = options;
2839
+ if (!this.poller) this.poller = new Poller(this, pollOptions);
2840
+ return this.poller.pollOnce(forceFull ?? false);
2841
+ }
2842
+ get pollData() {
2843
+ return this.poller?.pollData;
2844
+ }
2845
+ async getInventory(target, appid, contextid = "2", options = {}) {
2846
+ const tradableOnly = options.tradableOnly ?? false;
2847
+ await this.session.getAccessToken();
2848
+ const { steamId, token } = resolveTarget(target);
2849
+ const sessionid = await this.http.getSessionId();
2850
+ const accountId = new SteamID(steamId).accountid;
2851
+ const referer = `${URLS.community}/tradeoffer/new/?partner=${accountId}${token ? `&token=${token}` : ""}`;
2852
+ return paginate(async (start) => {
2853
+ const res = await this.http.get(`${URLS.community}/tradeoffer/new/partnerinventory/`, {
2854
+ responseType: "json",
2855
+ searchParams: {
2856
+ sessionid,
2857
+ partner: steamId,
2858
+ appid,
2859
+ contextid,
2860
+ ...token ? { token } : {},
2861
+ ...start !== void 0 ? { start } : {},
2862
+ l: LANG.l
2863
+ },
2864
+ headers: { Referer: referer }
2865
+ });
2866
+ const body = res.body;
2867
+ if (res.statusCode === 500 && body?.error) throw parseStrError(body.error);
2868
+ if (res.statusCode !== 200) throw httpError(res, RETRY_AFTER.partnerInventory);
2869
+ if (!body?.success) throw await inventoryFailureError(this.http, steamId, token, body?.error ?? body?.Error);
2870
+ const next = body.more && typeof body.more_start === "number" && body.more_start > (start ?? 0) ? body.more_start : void 0;
2871
+ return {
2872
+ items: parsePartnerInventory(body, contextid, tradableOnly),
2873
+ next
2874
+ };
2875
+ });
2876
+ }
2877
+ };
2878
+ function itemMalformed(item) {
2879
+ return !item.appid || !item.contextid || !item.assetid;
2880
+ }
2881
+ function offerSuperMalformed(offer) {
2882
+ return !offer.accountid_other;
2883
+ }
2884
+ function offerMalformed(offer) {
2885
+ return offerSuperMalformed(offer) || (offer.items_to_give ?? []).length === 0 && (offer.items_to_receive ?? []).length === 0 || (offer.items_to_give ?? []).some(itemMalformed) || (offer.items_to_receive ?? []).some(itemMalformed);
2886
+ }
2887
+ function sanitizeRawOffer(offer) {
2888
+ return {
2889
+ ...offer,
2890
+ ...offer.items_to_give ? { items_to_give: offer.items_to_give.filter((i) => !itemMalformed(i)) } : {},
2891
+ ...offer.items_to_receive ? { items_to_receive: offer.items_to_receive.filter((i) => !itemMalformed(i)) } : {}
2892
+ };
2893
+ }
2894
+ //#endregion
2895
+ //#region src/SteamMobile.ts
2896
+ var SteamMobile = class extends EventEmitter {
2897
+ http;
2898
+ session;
2899
+ api;
2900
+ confirmations;
2901
+ trade;
2902
+ community;
2903
+ identitySecret;
2904
+ polling;
2905
+ proxy;
2906
+ profile;
2907
+ constructor(options) {
2908
+ super();
2909
+ const profile = resolveMobileProfile(options.mobileProfile);
2910
+ this.profile = profile;
2911
+ this.proxy = options.proxy;
2912
+ this.http = new HttpClient({
2913
+ ...options.proxy ? { proxy: options.proxy } : {},
2914
+ profile
2915
+ });
2916
+ this.session = new SessionManager(this.http, options.refreshToken);
2917
+ this.identitySecret = options.identitySecret;
2918
+ this.polling = options.polling;
2919
+ this.api = new WebApiClient(this.http, () => this.session.getAccessToken());
2920
+ this.confirmations = new ConfirmationManager(this.http, this.session.steamID, options.identitySecret, profile);
2921
+ this.trade = new TradeNamespace(this.api, this.http, this.session, this.confirmations);
2922
+ this.community = new CommunityNamespace(this.http, this.session, this.confirmations, this.api);
2923
+ this.session.on("refreshToken", (token) => this.emit("refreshToken", token));
2924
+ this.session.on("sessionExpired", (error) => this.emit("sessionExpired", error));
2925
+ this.session.on("debug", (message) => this.emit("debug", message));
2926
+ this.trade.on("debug", (message) => this.emit("debug", message));
2927
+ this.trade.on("newOffer", (o) => this.emit("newOffer", o));
2928
+ this.trade.on("sentOfferChanged", (o, s) => this.emit("sentOfferChanged", o, s));
2929
+ this.trade.on("receivedOfferChanged", (o, s) => this.emit("receivedOfferChanged", o, s));
2930
+ this.trade.on("unknownOfferSent", (o) => this.emit("unknownOfferSent", o));
2931
+ this.trade.on("sentOfferCanceled", (o, r) => this.emit("sentOfferCanceled", o, r));
2932
+ this.trade.on("offerUpdate", (u) => this.emit("offerUpdate", u));
2933
+ this.trade.on("pollData", (d) => this.emit("pollData", d));
2934
+ this.trade.on("pollSuccess", () => this.emit("pollSuccess"));
2935
+ this.trade.on("pollFailure", (e) => this.emit("pollFailure", e));
2936
+ }
2937
+ async login() {
2938
+ await this.session.getAccessToken();
2939
+ if (this.polling) this.trade.startPolling(this.polling === true ? {} : this.polling);
2940
+ return this;
2941
+ }
2942
+ async reauthenticate(credentials) {
2943
+ const result = await loginWithCredentials({
2944
+ ...credentials,
2945
+ ...this.proxy ? { proxy: this.proxy } : {},
2946
+ mobileProfile: this.profile
2947
+ });
2948
+ await this.session.setRefreshToken(result.refreshToken);
2949
+ }
2950
+ ensureApiKey(domain) {
2951
+ return this.community.ensureApiKey(domain);
2952
+ }
2953
+ openidLogin(options) {
2954
+ return this.community.openidLogin(options);
2955
+ }
2956
+ async request(method, url, opts) {
2957
+ await this.session.getAccessToken();
2958
+ return this.http.request(method, url, opts);
2959
+ }
2960
+ get(url, opts) {
2961
+ return this.request("GET", url, opts);
2962
+ }
2963
+ post(url, opts) {
2964
+ return this.request("POST", url, opts);
2965
+ }
2966
+ get steamID() {
2967
+ return this.session.steamID;
2968
+ }
2969
+ get accessToken() {
2970
+ return this.session.accessToken;
2971
+ }
2972
+ get refreshToken() {
2973
+ return this.session.refreshToken;
2974
+ }
2975
+ async shutdown() {
2976
+ this.trade.stopPolling();
2977
+ this.trade.removeAllListeners();
2978
+ this.removeAllListeners();
2979
+ }
2980
+ };
2981
+ //#endregion
2982
+ 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, getTradeHistory, getTradeOffersSummary, getTradeStatus, isTerminalAuthEResult, isTerminalState, isTransientEResult, loginWithCredentials, parseInventory, parseOpenidForm, parsePartnerInventory, resolveMobileProfile, resolveTarget, secondsUntilExpiry, steamOpenidLogin };
2983
+
2984
+ //# sourceMappingURL=index.mjs.map