steamutils 1.4.63 → 1.4.64

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. package/SteamClient.js +612 -399
  2. package/package.json +1 -1
package/SteamClient.js CHANGED
@@ -90,11 +90,11 @@ export const SteamClientEvents = {
90
90
  accountLimitations: "accountLimitations",
91
91
  wallet: "wallet",
92
92
  licenses: "licenses",
93
+ logOff: "logOff",
93
94
  };
94
- const PersonasCache = [];
95
95
  let isSendingFriendMessages = false;
96
96
 
97
- function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestFreeLicense = true, isFakeGameScore = true, isPartyRegister = true, isAutoPlay = false, isInvisible = false, autoAcceptTradeRequest = false, autoReconnect = true, MAX_GAME_PLAY = 10, games = null }) {
97
+ function SteamClient({ username, cookie, clientJsToken, isAutoRequestFreeLicense = true, isFakeGameScore = true, isPartyRegister = true, isAutoPlay = false, isInvisible = false, autoAcceptTradeRequest = false, autoReconnect = true, MAX_GAME_PLAY = 10, games = null }) {
98
98
  const steamClient = new NodeSteamUser();
99
99
  let prime = null;
100
100
  let state = "Offline"; //InGame, Online, Invisible
@@ -104,7 +104,7 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
104
104
  let lastTimePartyRegister = 0;
105
105
  let lastTimePartySearch = 0;
106
106
  const ownedApps = [];
107
- let logOffEvent = null;
107
+ const logOffEvents = {};
108
108
  let _cleanNodeSteamUserTimeout = null;
109
109
  let _cleanNodeSteamLicensesTimeout = null;
110
110
  let _cleanNodeSteamFriendTimeout = null;
@@ -124,34 +124,115 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
124
124
  events[name] = [];
125
125
  }
126
126
 
127
- const gcCallback = {};
127
+ const gcEvents = {
128
+ requestJoinFriendData: [],
129
+ requestCoPlays: [],
130
+ partySearch: [],
131
+ playersProfile: [],
132
+ };
128
133
 
129
- function pushGCCallback(name, cb, timeout) {
130
- if (!gcCallback[name]) {
131
- gcCallback[name] = {};
134
+ function onGCEvent({ name, id, key, callback, timeoutMs }) {
135
+ if (typeof name !== "string") {
136
+ name = Object.keys(gcEvents).find((k) => gcEvents[k] === name);
132
137
  }
133
- let t = null;
134
- let id = uuidv4();
135
138
 
136
- function callback(...arg) {
137
- if (t) {
138
- clearTimeout(t);
139
- }
140
- delete gcCallback[name][id];
141
- cb?.apply(null, arg);
139
+ if (!gcEvents[name]) {
140
+ gcEvents[name] = [];
142
141
  }
142
+ if (!id) {
143
+ id = uuidv4();
144
+ }
145
+ const timeout = timeoutMs
146
+ ? setTimeout(function () {
147
+ callback?.();
148
+ clearGCCallback(id);
149
+ }, timeoutMs)
150
+ : null;
151
+
152
+ gcEvents[name].push({
153
+ id,
154
+ key,
155
+ timeout,
156
+ callback,
157
+ });
158
+ return id;
159
+ }
143
160
 
144
- if (timeout) {
145
- t = setTimeout(callback, timeout);
161
+ function clearGCCallback(name) {
162
+ if (!Array.isArray(name)) {
163
+ name = [name];
164
+ }
165
+ for (const nameE of name) {
166
+ switch (typeof nameE) {
167
+ case "string":
168
+ if (Array.isArray(gcEvents[nameE])) {
169
+ //name
170
+ for (const eventElement of gcEvents[nameE]) {
171
+ if (eventElement.timeout) {
172
+ clearTimeout(eventElement.timeout);
173
+ }
174
+ }
175
+ gcEvents[nameE] = [];
176
+ } else {
177
+ //id
178
+ for (const eventsKey in gcEvents) {
179
+ if (!Array.isArray(gcEvents[eventsKey])) {
180
+ continue;
181
+ }
182
+ const index = gcEvents[eventsKey].findIndex((e) => e.id === nameE);
183
+ if (index > -1) {
184
+ if (gcEvents[eventsKey].timeout) {
185
+ clearTimeout(gcEvents[eventsKey].timeout);
186
+ }
187
+ gcEvents[eventsKey].splice(index, 1);
188
+ }
189
+ }
190
+ }
191
+ break;
192
+ case "function":
193
+ //callback
194
+ for (const eventsKey in gcEvents) {
195
+ if (!Array.isArray(gcEvents[eventsKey])) {
196
+ continue;
197
+ }
198
+ const index = gcEvents[eventsKey].findIndex((e) => e.callback === nameE);
199
+ if (index > -1) {
200
+ if (gcEvents[eventsKey].timeout) {
201
+ clearTimeout(gcEvents[eventsKey].timeout);
202
+ }
203
+ gcEvents[eventsKey].splice(index, 1);
204
+ }
205
+ }
206
+ break;
207
+ }
146
208
  }
147
- gcCallback[name][id] = callback;
148
209
  }
149
210
 
150
- function callGCCallback(name, ...arg) {
151
- if (gcCallback[name]) {
152
- for (const id in gcCallback[name]) {
153
- gcCallback[name][id]?.(...arg);
211
+ async function callGCCallback({ name, data, key }) {
212
+ if (typeof name !== "string") {
213
+ name = Object.keys(gcEvents).find((k) => gcEvents[k] === name);
214
+ }
215
+
216
+ if (!Array.isArray(gcEvents[name])) {
217
+ return;
218
+ }
219
+
220
+ const events = _.remove(gcEvents[name], (item) => !key || item.key === key);
221
+ let _data = null;
222
+ let calculated = false;
223
+
224
+ for (const event of events) {
225
+ if (event.timeout) {
226
+ clearTimeout(event.timeout);
227
+ delete event.timeout;
154
228
  }
229
+
230
+ if (!calculated) {
231
+ calculated = true;
232
+ _data = typeof data === "function" ? await data() : data;
233
+ }
234
+
235
+ event.callback(_data);
155
236
  }
156
237
  }
157
238
 
@@ -170,28 +251,81 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
170
251
  _.remove(_events, (e) => !e || e?.once);
171
252
  }
172
253
 
173
- function onEvent(name, callback, once, timeout) {
254
+ function onEvent({ name, id, callback, once, timeout }) {
174
255
  if (!events[name]) {
175
256
  events[name] = [];
176
257
  }
177
- const t = timeout ? setTimeout(callback, timeout) : null;
258
+ if (!id) {
259
+ id = uuidv4();
260
+ }
261
+ const t = timeout
262
+ ? setTimeout(function () {
263
+ try {
264
+ callback?.();
265
+ } catch (e) {}
266
+ if (once) {
267
+ offEvent(id);
268
+ }
269
+ }, timeout)
270
+ : null;
178
271
  events[name].push({
272
+ id,
179
273
  once,
180
274
  callback,
181
275
  timeout: t,
182
276
  });
277
+ return id;
183
278
  }
184
279
 
280
+ // name, id, callback
185
281
  function offEvent(name) {
186
- if (Array.isArray(events[name])) {
187
- for (const eventElement of events[name]) {
188
- if (eventElement.timeout) {
189
- clearTimeout(eventElement.timeout);
190
- }
282
+ if (!Array.isArray(name)) {
283
+ name = [name];
284
+ }
285
+ for (const nameE of name) {
286
+ switch (typeof nameE) {
287
+ case "string":
288
+ if (Array.isArray(events[nameE])) {
289
+ //name
290
+ for (const eventElement of events[nameE]) {
291
+ if (eventElement.timeout) {
292
+ clearTimeout(eventElement.timeout);
293
+ }
294
+ }
295
+ events[nameE] = [];
296
+ } else {
297
+ //id
298
+ for (const eventsKey in events) {
299
+ if (!Array.isArray(events[eventsKey])) {
300
+ continue;
301
+ }
302
+ const index = events[eventsKey].findIndex((e) => e.id === nameE);
303
+ if (index > -1) {
304
+ if (events[eventsKey].timeout) {
305
+ clearTimeout(events[eventsKey].timeout);
306
+ }
307
+ events[eventsKey].splice(index, 1);
308
+ }
309
+ }
310
+ }
311
+ break;
312
+ case "function":
313
+ //callback
314
+ for (const eventsKey in events) {
315
+ if (!Array.isArray(events[eventsKey])) {
316
+ continue;
317
+ }
318
+ const index = events[eventsKey].findIndex((e) => e.callback === nameE);
319
+ if (index > -1) {
320
+ if (events[eventsKey].timeout) {
321
+ clearTimeout(events[eventsKey].timeout);
322
+ }
323
+ events[eventsKey].splice(index, 1);
324
+ }
325
+ }
326
+ break;
191
327
  }
192
328
  }
193
-
194
- events[name] = [];
195
329
  }
196
330
 
197
331
  function onAnyEvent(callback) {
@@ -289,6 +423,11 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
289
423
  console.log(`[${now}] [${getAccountInfoName()}]`, ...msg);
290
424
  }
291
425
 
426
+ function error(...msg) {
427
+ const now = moment().tz("Asia/Ho_Chi_Minh").format("DD/MM/YYYY HH:mm:ss");
428
+ console.error(`[${now}] [${getAccountInfoName()}]`, ...msg);
429
+ }
430
+
292
431
  async function getPersonas(steamIDs) {
293
432
  if (!steamIDs) return [];
294
433
  if (!Array.isArray(steamIDs)) {
@@ -355,14 +494,14 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
355
494
  return (
356
495
  getCookies() ||
357
496
  new Promise((resolve) => {
358
- onEvent(
359
- "webSession",
360
- function (webSession) {
497
+ onEvent({
498
+ name: "webSession",
499
+ callback: function (webSession) {
361
500
  resolve(webSession?.cookies);
362
501
  },
363
- true,
364
- 30000,
365
- );
502
+ once: true,
503
+ timeout: 30000,
504
+ });
366
505
  })
367
506
  );
368
507
  }
@@ -408,23 +547,33 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
408
547
  rank = parseInt(Object.keys(RANKS).find((k) => RANKS[k] === rank)) * 10;
409
548
  }
410
549
 
411
- const players = await new Promise((resolve) => {
412
- steamClient.sendToGC(
413
- AppID,
414
- ESteamProto.ECsgoGCMsg.k_EMsgGCCStrike15_v2_Party_Search,
415
- {},
416
- new SteamProto(SteamProtoType.CMsgGCCStrike15_v2_Party_Search).protoEncode({
417
- ver: CSGO_VER,
418
- apr: prime ? 1 : 0,
419
- ark: rank,
420
- grps: [],
421
- launcher: 0, // If we are using the China CSGO launcher or not*
422
- game_type: gameTypeMap[game_type],
423
- }),
424
- );
425
- lastTimePartySearch = Date.now();
426
- pushGCCallback("partySearch", resolve, timeout);
427
- });
550
+ const gcEventId = uuidv4();
551
+ const players = await ensureLogin(
552
+ new Promise((resolve) => {
553
+ lastTimePartySearch = Date.now();
554
+ onGCEvent({
555
+ name: gcEvents.partySearch,
556
+ id: gcEventId,
557
+ callback: resolve,
558
+ timeoutMs: timeout,
559
+ });
560
+ steamClient.sendToGC(
561
+ AppID,
562
+ ESteamProto.ECsgoGCMsg.k_EMsgGCCStrike15_v2_Party_Search,
563
+ {},
564
+ new SteamProto(SteamProtoType.CMsgGCCStrike15_v2_Party_Search).protoEncode({
565
+ ver: CSGO_VER,
566
+ apr: prime ? 1 : 0,
567
+ ark: rank,
568
+ grps: [],
569
+ launcher: 0, // If we are using the China CSGO launcher or not*
570
+ game_type: gameTypeMap[game_type],
571
+ }),
572
+ );
573
+ }),
574
+ );
575
+ clearGCCallback(gcEventId);
576
+
428
577
  if (Array.isArray(players) && players.length) {
429
578
  const personas = await getPersonas(players.map((p) => p.steamId));
430
579
  for (const player of players) {
@@ -434,8 +583,8 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
434
583
  player.avatar_hash = persona.avatar_hash;
435
584
  }
436
585
  }
586
+ return players;
437
587
  }
438
- return players;
439
588
  }
440
589
 
441
590
  async function createLobby() {
@@ -697,10 +846,20 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
697
846
  }
698
847
 
699
848
  async function requestCoPlays() {
700
- return new Promise((resolve) => {
701
- steamClient.sendToGC(AppID, ESteamProto.ECsgoGCMsg.k_EMsgGCCStrike15_v2_Account_RequestCoPlays, {}, Buffer.alloc(0));
702
- pushGCCallback("RequestCoPlays", resolve, 30000);
703
- });
849
+ const gcEventId = uuidv4();
850
+ const response = await ensureLogin(
851
+ new Promise((resolve) => {
852
+ onGCEvent({
853
+ name: gcEvents.requestCoPlays,
854
+ id: gcEventId,
855
+ callback: resolve,
856
+ timeoutMs: 30000,
857
+ });
858
+ steamClient.sendToGC(AppID, ESteamProto.ECsgoGCMsg.k_EMsgGCCStrike15_v2_Account_RequestCoPlays, {}, Buffer.alloc(0));
859
+ }),
860
+ );
861
+ clearGCCallback(gcEventId);
862
+ return response;
704
863
  }
705
864
 
706
865
  function bindEvent() {
@@ -712,62 +871,7 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
712
871
  callEvent(events.disconnected, { eresult, msg });
713
872
 
714
873
  if (["ServiceUnavailable", "NoConnection"].includes(msg) && autoReconnect && !isLogOff) {
715
- async function relogin(retry) {
716
- if (isLogOff) {
717
- console.error("Cannot relogin (logoff)");
718
- return false;
719
- } else if (retry <= 0) {
720
- console.error("Cannot relogin");
721
- return false;
722
- } else {
723
- const isSuccess = await login(true);
724
- if (isSuccess) {
725
- const loggedOnResponse = await new Promise((resolve) => {
726
- onEvent(SteamClientEvents.loggedOn, resolve, true, 180000);
727
- });
728
- if (loggedOnResponse) {
729
- console.log("Relogin success");
730
- return true;
731
- } else {
732
- const isLogOff = await new Promise((resolve, reject) => {
733
- logOffEvent = resolve;
734
- setTimeout(resolve, 120000);
735
- });
736
- logOffEvent = null;
737
- if (isLogOff === true) {
738
- return false;
739
- }
740
- return await relogin(retry - 1);
741
- }
742
- } else {
743
- const isLogOff = await new Promise((resolve, reject) => {
744
- logOffEvent = resolve;
745
- setTimeout(resolve, 120000);
746
- });
747
- logOffEvent = null;
748
- if (isLogOff === true) {
749
- return false;
750
- }
751
- return await relogin(retry - 1);
752
- }
753
- }
754
- }
755
-
756
- const isLogOff = await new Promise((resolve, reject) => {
757
- logOffEvent = resolve;
758
- setTimeout(resolve, 60000);
759
- });
760
- logOffEvent = null;
761
- if (isLogOff === true) {
762
- offAllEvent();
763
- doClearIntervals();
764
- } else {
765
- const isSuccess = await relogin(50);
766
- if (!isSuccess) {
767
- offAllEvent();
768
- doClearIntervals();
769
- }
770
- }
874
+ reLogin();
771
875
  } else {
772
876
  offAllEvent();
773
877
  doClearIntervals();
@@ -802,7 +906,7 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
802
906
  steamClient.webSession = webSession;
803
907
  callEvent(events.webSession, webSession);
804
908
  },
805
- async receivedFromGC(appid, msgType, payload) {
909
+ receivedFromGC: async function (appid, msgType, payload) {
806
910
  const key = getECsgoGCMsgKey(msgType);
807
911
  switch (msgType) {
808
912
  case ESteamProto.EMsg.k_EMsgClientChatInvite: {
@@ -830,9 +934,13 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
830
934
  break;
831
935
  }
832
936
  case ESteamProto.ECsgoGCMsg.k_EMsgGCCStrike15_v2_Account_RequestCoPlays: {
833
- const msg = new SteamProto(SteamProtoType.CMsgGCCStrike15_v2_Account_RequestCoPlays).protoDecode(payload);
834
- const personas = msg.players.map((p) => SteamID.fromIndividualAccountID(p.accountid));
835
- callGCCallback("RequestCoPlays", personas);
937
+ callGCCallback({
938
+ name: gcEvents.requestCoPlays,
939
+ data() {
940
+ const msg = new SteamProto(SteamProtoType.CMsgGCCStrike15_v2_Account_RequestCoPlays).protoDecode(payload);
941
+ return msg.players.map((p) => SteamID.fromIndividualAccountID(p.accountid));
942
+ },
943
+ });
836
944
  break;
837
945
  }
838
946
  case ESteamProto.EGCBaseClientMsg.k_EMsgGCClientWelcome: {
@@ -1127,79 +1235,83 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
1127
1235
  break;
1128
1236
  }
1129
1237
  case ESteamProto.ECsgoGCMsg.k_EMsgGCCStrike15_v2_Party_Search: {
1130
- const result = new SteamProto(SteamProtoType.CMsgGCCStrike15_v2_Party_SearchResults).protoDecode(payload);
1131
- const entries = _.uniqBy(result.entries, "id");
1132
- //{
1133
- // id: 144900402,
1134
- // grp: 0,
1135
- // game_type: 8,
1136
- // apr: 1,
1137
- // ark: 17,
1138
- // loc: 20041,
1139
- // accountid: 0
1140
- // }
1141
-
1142
- //{
1143
- // id: "76561199265943339",
1144
- // rich_presence: [],
1145
- // persona_state: null,
1146
- // game_played_app_id: null,
1147
- // game_server_ip: null,
1148
- // game_server_port: null,
1149
- // persona_state_flags: null,
1150
- // online_session_instances: null,
1151
- // persona_set_by_user: null,
1152
- // player_name: "杀人不见血",
1153
- // query_port: null,
1154
- // steamid_source: null,
1155
- // avatar_hash: "33994e26f1fe7e2093f8c7dee66c1ac91531050d",
1156
- // last_logoff: null,
1157
- // last_logon: null,
1158
- // last_seen_online: null,
1159
- // clan_rank: null,
1160
- // game_name: null,
1161
- // gameid: null,
1162
- // game_data_blob: null,
1163
- // clan_data: null,
1164
- // clan_tag: null,
1165
- // broadcast_id: null,
1166
- // game_lobby_id: null,
1167
- // watching_broadcast_accountid: null,
1168
- // watching_broadcast_appid: null,
1169
- // watching_broadcast_viewers: null,
1170
- // watching_broadcast_title: null,
1171
- // is_community_banned: null,
1172
- // player_name_pending_review: null,
1173
- // avatar_pending_review: null,
1174
- // avatar_url_icon: "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/33/33994e26f1fe7e2093f8c7dee66c1ac91531050d.jpg",
1175
- // avatar_url_medium: "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/33/33994e26f1fe7e2093f8c7dee66c1ac91531050d_medium.jpg",
1176
- // avatar_url_full: "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/33/33994e26f1fe7e2093f8c7dee66c1ac91531050d_full.jpg"
1177
- // }
1178
-
1179
- const players = [];
1180
- if (entries.length) {
1181
- const FriendCode = (await import("csgo-friendcode")).default;
1182
- for (const player of entries) {
1183
- try {
1184
- const prime = player.apr === 1 ? "PRIME" : "NON-PRIME";
1185
- const loc = LOCS[player.loc] || player.loc;
1186
- const steamId = SteamID.fromIndividualAccountID(player.id).getSteamID64();
1187
- const friendCode = FriendCode.encode(steamId);
1188
-
1189
- // if ((LOCS[player.loc] == 'VN' || !LOCS[player.loc])) {
1190
- players.push({
1191
- prime,
1192
- rank: RANKS[player.ark] !== undefined ? RANKS[player.ark] : player.ark,
1193
- loc,
1194
- steamId,
1195
- friendCode,
1196
- });
1197
- // }
1198
- } catch (e) {}
1199
- }
1200
- }
1201
-
1202
- callGCCallback("partySearch", players);
1238
+ callGCCallback({
1239
+ name: gcEvents.partySearch,
1240
+ async data() {
1241
+ const result = new SteamProto(SteamProtoType.CMsgGCCStrike15_v2_Party_SearchResults).protoDecode(payload);
1242
+ const entries = _.uniqBy(result.entries, "id");
1243
+ //{
1244
+ // id: 144900402,
1245
+ // grp: 0,
1246
+ // game_type: 8,
1247
+ // apr: 1,
1248
+ // ark: 17,
1249
+ // loc: 20041,
1250
+ // accountid: 0
1251
+ // }
1252
+
1253
+ //{
1254
+ // id: "76561199265943339",
1255
+ // rich_presence: [],
1256
+ // persona_state: null,
1257
+ // game_played_app_id: null,
1258
+ // game_server_ip: null,
1259
+ // game_server_port: null,
1260
+ // persona_state_flags: null,
1261
+ // online_session_instances: null,
1262
+ // persona_set_by_user: null,
1263
+ // player_name: "杀人不见血",
1264
+ // query_port: null,
1265
+ // steamid_source: null,
1266
+ // avatar_hash: "33994e26f1fe7e2093f8c7dee66c1ac91531050d",
1267
+ // last_logoff: null,
1268
+ // last_logon: null,
1269
+ // last_seen_online: null,
1270
+ // clan_rank: null,
1271
+ // game_name: null,
1272
+ // gameid: null,
1273
+ // game_data_blob: null,
1274
+ // clan_data: null,
1275
+ // clan_tag: null,
1276
+ // broadcast_id: null,
1277
+ // game_lobby_id: null,
1278
+ // watching_broadcast_accountid: null,
1279
+ // watching_broadcast_appid: null,
1280
+ // watching_broadcast_viewers: null,
1281
+ // watching_broadcast_title: null,
1282
+ // is_community_banned: null,
1283
+ // player_name_pending_review: null,
1284
+ // avatar_pending_review: null,
1285
+ // avatar_url_icon: "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/33/33994e26f1fe7e2093f8c7dee66c1ac91531050d.jpg",
1286
+ // avatar_url_medium: "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/33/33994e26f1fe7e2093f8c7dee66c1ac91531050d_medium.jpg",
1287
+ // avatar_url_full: "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/33/33994e26f1fe7e2093f8c7dee66c1ac91531050d_full.jpg"
1288
+ // }
1289
+
1290
+ const players = [];
1291
+ if (entries.length) {
1292
+ const FriendCode = (await import("csgo-friendcode")).default;
1293
+ for (const player of entries) {
1294
+ try {
1295
+ const prime = player.apr === 1 ? "PRIME" : "NON-PRIME";
1296
+ const loc = LOCS[player.loc] || player.loc;
1297
+ const steamId = SteamID.fromIndividualAccountID(player.id).getSteamID64();
1298
+ const friendCode = FriendCode.encode(steamId);
1299
+
1300
+ // if ((LOCS[player.loc] == 'VN' || !LOCS[player.loc])) {
1301
+ players.push({
1302
+ prime,
1303
+ rank: RANKS[player.ark] !== undefined ? RANKS[player.ark] : player.ark,
1304
+ loc,
1305
+ steamId,
1306
+ friendCode,
1307
+ });
1308
+ // }
1309
+ } catch (e) {}
1310
+ }
1311
+ }
1312
+ return players;
1313
+ },
1314
+ });
1203
1315
  break;
1204
1316
  }
1205
1317
  case ESteamProto.ECsgoGCMsg.k_EMsgGCCStrike15_v2_PlayersProfile: {
@@ -1270,7 +1382,11 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
1270
1382
  player.elo = ranking.rank_id;
1271
1383
  }
1272
1384
  }
1273
- callGCCallback(`PlayersProfile_${player.account_id}`, player);
1385
+ callGCCallback({
1386
+ name: gcEvents.playersProfile,
1387
+ data: player,
1388
+ key: player.account_id,
1389
+ });
1274
1390
  }
1275
1391
  callEvent(events.playersProfile, player);
1276
1392
  break;
@@ -1298,7 +1414,11 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
1298
1414
  res: null,
1299
1415
  errormsg: "Game is full",
1300
1416
  };
1301
- callGCCallback(`requestJoinFriendData_${data.account_id}`, data);
1417
+ callGCCallback({
1418
+ name: gcEvents.requestJoinFriendData,
1419
+ data,
1420
+ key: data.account_id,
1421
+ });
1302
1422
  break;
1303
1423
  }
1304
1424
  default:
@@ -1316,6 +1436,36 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
1316
1436
  }
1317
1437
  },
1318
1438
  async loggedOn(loggedOnResponse) {
1439
+ const example = {
1440
+ eresult: 1,
1441
+ legacy_out_of_game_heartbeat_seconds: 9,
1442
+ heartbeat_seconds: 9,
1443
+ deprecated_public_ip: 250140346,
1444
+ rtime32_server_time: 1727758337,
1445
+ account_flags: 2642053,
1446
+ cell_id: 71,
1447
+ email_domain: null,
1448
+ steam2_ticket: null,
1449
+ eresult_extended: null,
1450
+ webapi_authenticate_user_nonce: null,
1451
+ cell_id_ping_threshold: 2147483647,
1452
+ deprecated_use_pics: null,
1453
+ vanity_url: "",
1454
+ public_ip: {
1455
+ v4: 250140346,
1456
+ },
1457
+ client_supplied_steamid: "76561190347696",
1458
+ ip_country_code: "VN",
1459
+ parental_settings: null,
1460
+ parental_setting_signature: null,
1461
+ count_loginfailures_to_migrate: 0,
1462
+ count_disconnects_to_migrate: 0,
1463
+ ogs_data_report_time_window: null,
1464
+ client_instance_id: "4130450042302948",
1465
+ force_client_update_check: false,
1466
+ agreement_session_url: null,
1467
+ token_id: null,
1468
+ };
1319
1469
  callEvent(events.loggedOn, loggedOnResponse);
1320
1470
  updateInvisible();
1321
1471
  updateAutoRequestFreeLicense();
@@ -1663,20 +1813,31 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
1663
1813
  /*
1664
1814
  * usually take 400 -> 800 miliseconds
1665
1815
  * */
1666
- function getPlayersProfile(steamId) {
1816
+ async function getPlayersProfile(steamId) {
1667
1817
  const accountid = new SteamID(steamId).accountid;
1668
- steamClient.sendToGC(
1669
- 730,
1670
- ESteamProto.ECsgoGCMsg.k_EMsgGCCStrike15_v2_ClientRequestPlayersProfile,
1671
- {},
1672
- new SteamProto(SteamProtoType.CMsgGCCStrike15_v2_ClientRequestPlayersProfile).protoEncode({
1673
- account_id: accountid, // account_id: new SteamID('76561199184696945').accountid,
1674
- request_level: 32,
1818
+ const gcEventId = uuidv4();
1819
+ const result = await ensureLogin(
1820
+ new Promise((resolve) => {
1821
+ onGCEvent({
1822
+ name: gcEvents.playersProfile,
1823
+ key: accountid,
1824
+ id: gcEventId,
1825
+ callback: resolve,
1826
+ timeoutMs: 5000,
1827
+ });
1828
+ steamClient.sendToGC(
1829
+ 730,
1830
+ ESteamProto.ECsgoGCMsg.k_EMsgGCCStrike15_v2_ClientRequestPlayersProfile,
1831
+ {},
1832
+ new SteamProto(SteamProtoType.CMsgGCCStrike15_v2_ClientRequestPlayersProfile).protoEncode({
1833
+ account_id: accountid, // account_id: new SteamID('76561199184696945').accountid,
1834
+ request_level: 32,
1835
+ }),
1836
+ );
1675
1837
  }),
1676
1838
  );
1677
- return new Promise((resolve) => {
1678
- pushGCCallback(`PlayersProfile_${accountid}`, resolve, 2000);
1679
- });
1839
+ clearGCCallback(gcEventId);
1840
+ return result;
1680
1841
  }
1681
1842
 
1682
1843
  async function checkPlayerPrimeStatus(steamId) {
@@ -1783,23 +1944,113 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
1783
1944
  }
1784
1945
  }
1785
1946
 
1786
- async function login(reconnect = false) {
1787
- function logOn(clientJsToken) {
1788
- try {
1789
- steamClient.logOn({
1790
- ...clientJsToken,
1791
- steamId: new SteamID(clientJsToken.steamid),
1792
- steamID: new SteamID(clientJsToken.steamid),
1793
- accountName: clientJsToken.account_name,
1794
- webLogonToken: clientJsToken.token,
1795
- });
1796
- return true;
1797
- } catch (e) {
1798
- console.error(e);
1799
- return false;
1800
- }
1947
+ /**
1948
+ * Logs on to the Steam client using the provided token.
1949
+ *
1950
+ * @async
1951
+ * @function logOn
1952
+ *
1953
+ * @param {Object} [token] - The token object containing credentials and other information. Defaults to a pre-filled sample token.
1954
+ * @param {boolean} [token.logged_in=true] - Indicates whether the user is logged in.
1955
+ * @param {string} [token.steamid="76561199027696"] - The user's SteamID in string format.
1956
+ * @param {number} [token.accountid=10671968] - The user's account ID.
1957
+ * @param {string} [token.account_name="helloword"] - The user's account name.
1958
+ * @param {string} [token.accountName="helloword"] - The user's account name (duplicate).
1959
+ * @param {boolean} [token.is_support=false] - Whether the user is a support member.
1960
+ * @param {boolean} [token.is_limited=true] - Indicates if the user's account is limited.
1961
+ * @param {boolean} [token.is_partner_member=false] - Whether the user is a partner member.
1962
+ * @param {string} [token.country_code="CN"] - The user's country code.
1963
+ * @param {string} [token.token="AAAAAAAAAAAAAAA"] - The web logon token.
1964
+ * @param {string} [token.webLogonToken="AAAAAAAAAAAAAAA"] - The web logon token (duplicate).
1965
+ * @param {string} [token.token_use_id="e6a33a62c1d403b71761301c"] - The token use ID.
1966
+ * @param {Object} [token.steamId] - SteamID details.
1967
+ * @param {Object} [token.steamID] - Duplicate SteamID object (legacy support).
1968
+ *
1969
+ * @returns {Promise<boolean>} Resolves to `true` if login is successful, `false` otherwise.
1970
+ *
1971
+ * @throws Will log an error to the console if the login fails.
1972
+ */
1973
+ async function logOn(token) {
1974
+ try {
1975
+ steamClient.logOn({
1976
+ ...token,
1977
+ steamId: new SteamID(token.steamid),
1978
+ steamID: new SteamID(token.steamid),
1979
+ accountName: token.account_name,
1980
+ webLogonToken: token.token,
1981
+ });
1982
+ let timeout = null;
1983
+ const eventIds = [];
1984
+ const loggedOnResponse = await Promise.race([
1985
+ new Promise((resolve) => {
1986
+ timeout = setTimeout(function () {
1987
+ resolve();
1988
+ }, 180000);
1989
+ }),
1990
+ new Promise((resolve) => {
1991
+ eventIds.push(
1992
+ onEvent({
1993
+ name: SteamClientEvents.loggedOn,
1994
+ callback: resolve,
1995
+ once: true,
1996
+ }),
1997
+ onEvent({
1998
+ name: SteamClientEvents.logOff,
1999
+ callback: function () {
2000
+ resolve();
2001
+ },
2002
+ once: true,
2003
+ }),
2004
+ onEvent({
2005
+ name: SteamClientEvents.error,
2006
+ callback: function () {
2007
+ resolve();
2008
+ },
2009
+ once: true,
2010
+ }),
2011
+ );
2012
+ }),
2013
+ ]);
2014
+ clearTimeout(timeout);
2015
+ offEvent(eventIds);
2016
+ return loggedOnResponse?.eresult === 1;
2017
+ } catch (e) {
2018
+ console.error("logOn", e);
2019
+ return false;
2020
+ }
2021
+ }
2022
+
2023
+ async function reLogin(retry = 50) {
2024
+ if (isLogOff || retry <= 0) {
2025
+ offAllEvent();
2026
+ doClearIntervals();
2027
+
2028
+ error(`Cannot relogin (${isLogOff ? "logoff" : "retry limit reached"})`);
2029
+ return false;
2030
+ }
2031
+
2032
+ const isSuccess = await login(true);
2033
+ if (isSuccess) {
2034
+ log("Relogin success");
2035
+ return true;
2036
+ }
2037
+
2038
+ if (isLogOff) {
2039
+ offAllEvent();
2040
+ doClearIntervals();
2041
+ error(`Cannot relogin (${isLogOff ? "logoff" : "retry limit reached"})`);
2042
+ return false;
2043
+ }
2044
+
2045
+ if (retry > 1) {
2046
+ await sleep(60000);
2047
+ return await reLogin(retry - 1);
2048
+ } else {
2049
+ return false;
1801
2050
  }
2051
+ }
1802
2052
 
2053
+ async function login(reconnect = false) {
1803
2054
  if (clientJsToken?.logged_in === true) {
1804
2055
  log(`[${clientJsToken.steamid}] ${reconnect ? "reconnect with clientJsToken" : "login with clientJsToken"}`);
1805
2056
  setTimeout(function () {
@@ -1814,27 +2065,33 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
1814
2065
  return logOn(_clientJsToken);
1815
2066
  } else {
1816
2067
  log(`Account not logged in ${clientJsToken?.account_name || steamUser.getSteamIdUser() || ""}`);
1817
- console.log(clientJsToken);
1818
- return false;
1819
2068
  }
1820
- } else if (username && password) {
1821
- log(reconnect ? `reconnect with username ${username}` : `login with username ${username}`);
1822
- steamClient.logOn({
1823
- accountName: username,
1824
- password: password,
1825
- rememberPassword: true,
1826
- machineName: "Natri",
1827
- });
1828
- return true;
1829
2069
  } else {
1830
2070
  log(`Account not logged in ${clientJsToken?.account_name || ""}`);
1831
2071
  return false;
1832
2072
  }
1833
2073
  }
1834
2074
 
1835
- function logOff() {
2075
+ function clearLogOffEvent(id) {
2076
+ if (typeof id === "string") {
2077
+ delete logOffEvents[id];
2078
+ return id;
2079
+ } else if (typeof id === "function") {
2080
+ for (const idKey in logOffEvents) {
2081
+ if (logOffEvents[idKey] === id) {
2082
+ delete logOffEvents[idKey];
2083
+ return idKey;
2084
+ }
2085
+ }
2086
+ }
2087
+ }
2088
+
2089
+ async function logOff() {
1836
2090
  isLogOff = true;
1837
- logOffEvent?.(true);
2091
+
2092
+ callEvent(events.logOff);
2093
+ events.logOff = [];
2094
+
1838
2095
  if (_cleanNodeSteamUserTimeout) {
1839
2096
  clearTimeout(_cleanNodeSteamUserTimeout);
1840
2097
  }
@@ -1849,25 +2106,41 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
1849
2106
  steamClient.licenses = [];
1850
2107
 
1851
2108
  steamClient.logOff();
1852
- }
2109
+ await sleep(5000);
2110
+ delete steamClient._logOnDetails;
2111
+ delete steamClient.logOnResult;
2112
+ delete steamClient._httpClient;
2113
+ delete steamClient._lastChosenCM;
2114
+ try {
2115
+ steamClient._connection.end();
2116
+ } catch (e) {}
2117
+ delete steamClient._connection;
2118
+ delete steamClient.accountInfo;
2119
+ delete steamClient.emailInfo;
2120
+ delete steamClient.limitations;
2121
+ delete steamClient.vac;
2122
+ delete steamClient.wallet;
2123
+ delete steamClient.licenses;
2124
+ delete steamClient.gifts;
2125
+ delete steamClient.myGroups;
2126
+ delete steamClient.playingState;
1853
2127
 
1854
- function onCookie(callback) {
1855
- if (getCookies()) {
1856
- callback(getCookies());
1857
- } else {
1858
- onEvent(
1859
- "webSession",
1860
- function (webSession) {
1861
- callback(webSession?.cookies?.join?.(";"));
1862
- },
1863
- true,
1864
- );
2128
+ delete steamClient._heartbeatInterval;
2129
+ delete steamClient.chat;
2130
+ delete steamClient._httpClient;
2131
+ delete steamClient._events;
2132
+ delete steamClient.picsCache;
2133
+ delete steamClient.options;
2134
+
2135
+ for (const key in steamClient) {
2136
+ delete steamClient[key];
1865
2137
  }
1866
2138
  }
1867
2139
 
1868
2140
  async function init() {
1869
2141
  bindEvent();
1870
- if (await login()) {
2142
+ const logged = await login();
2143
+ if (logged) {
1871
2144
  steamClient._handlerManager.add(ESteamProto.EMsg.k_EMsgClientRequestedClientStats, function (payload) {
1872
2145
  const result = new SteamProto(SteamProtoType.CMsgClientRequestedClientStats).protoDecode(payload.toBuffer());
1873
2146
  // console.log("CMsgClientRequestedClientStats", result);
@@ -1998,40 +2271,54 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
1998
2271
  isInvisible = false;
1999
2272
  isAutoPlay = true;
2000
2273
  isPartyRegister = false;
2001
- const online = await new Promise((resolve) => {
2002
- const timeout = setTimeout(function () {
2003
- resolve(false);
2004
- }, 60000);
2005
2274
 
2006
- onEvent(
2007
- SteamClientEvents.csgoOnline,
2008
- function () {
2009
- clearTimeout(timeout);
2010
- resolve(true);
2011
- },
2012
- true,
2013
- );
2014
- onEvent(
2015
- SteamClientEvents.error,
2016
- function () {
2017
- clearTimeout(timeout);
2018
- resolve(false);
2019
- },
2020
- true,
2021
- );
2022
- onEvent(
2023
- SteamClientEvents.playingState,
2024
- function ({ playing_blocked, playing_app }) {
2025
- if (playing_blocked) {
2026
- clearTimeout(timeout);
2027
- resolve(false);
2028
- }
2029
- },
2030
- true,
2031
- );
2032
- init();
2033
- });
2034
- offAllEvent();
2275
+ let timeout = null;
2276
+ const eventIds = [];
2277
+
2278
+ const online = await Promise.race([
2279
+ new Promise((resolve) => {
2280
+ setTimeout(function () {
2281
+ resolve();
2282
+ }, 60000);
2283
+ }),
2284
+ new Promise((resolve) => {
2285
+ eventIds.push(
2286
+ onEvent({
2287
+ name: SteamClientEvents.logOff,
2288
+ callback: function () {
2289
+ resolve(false);
2290
+ },
2291
+ once: true,
2292
+ }),
2293
+ onEvent({
2294
+ name: SteamClientEvents.csgoOnline,
2295
+ callback: function () {
2296
+ resolve(true);
2297
+ },
2298
+ once: true,
2299
+ }),
2300
+ onEvent({
2301
+ name: SteamClientEvents.error,
2302
+ callback: function () {
2303
+ resolve(false);
2304
+ },
2305
+ once: true,
2306
+ }),
2307
+ onEvent({
2308
+ name: SteamClientEvents.playingState,
2309
+ callback: function ({ playing_blocked }) {
2310
+ if (playing_blocked) {
2311
+ resolve(false);
2312
+ }
2313
+ },
2314
+ }),
2315
+ );
2316
+ init();
2317
+ }),
2318
+ ]);
2319
+
2320
+ clearTimeout(timeout);
2321
+ offEvent(eventIds);
2035
2322
  return online;
2036
2323
  }
2037
2324
 
@@ -2248,7 +2535,7 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
2248
2535
  return steamClient._playingAppIds || [];
2249
2536
  }
2250
2537
 
2251
- function requestJoinFriendData(token, version) {
2538
+ async function requestJoinFriendData(token, version) {
2252
2539
  if (!steamClient.steamID) {
2253
2540
  return;
2254
2541
  }
@@ -2263,20 +2550,31 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
2263
2550
  let accountID = conBuf.readInt32BE(4);
2264
2551
  let joinIpp = conBuf.readInt32BE(8);
2265
2552
 
2266
- return new Promise((resolve) => {
2267
- pushGCCallback(`requestJoinFriendData_${accountID}`, resolve, 30000);
2268
- steamClient.sendToGC(
2269
- AppID,
2270
- ESteamProto.ECsgoGCMsg.k_EMsgGCCStrike15_v2_ClientRequestJoinFriendData,
2271
- {},
2272
- new SteamProto(SteamProtoType.CMsgGCCStrike15_v2_ClientRequestJoinFriendData).protoEncode({
2273
- version: version || CSGO_VER,
2274
- account_id: accountID,
2275
- join_token: joinToken,
2276
- join_ipp: joinIpp,
2277
- }),
2278
- );
2279
- });
2553
+ const gcEventId = uuidv4();
2554
+ const response = await ensureLogin(
2555
+ new Promise((resolve) => {
2556
+ onGCEvent({
2557
+ name: gcEvents.requestJoinFriendData,
2558
+ id: gcEventId,
2559
+ key: accountID,
2560
+ callback: resolve,
2561
+ timeoutMs: 30000,
2562
+ });
2563
+ steamClient.sendToGC(
2564
+ AppID,
2565
+ ESteamProto.ECsgoGCMsg.k_EMsgGCCStrike15_v2_ClientRequestJoinFriendData,
2566
+ {},
2567
+ new SteamProto(SteamProtoType.CMsgGCCStrike15_v2_ClientRequestJoinFriendData).protoEncode({
2568
+ version: version || CSGO_VER,
2569
+ account_id: accountID,
2570
+ join_token: joinToken,
2571
+ join_ipp: joinIpp,
2572
+ }),
2573
+ );
2574
+ }),
2575
+ );
2576
+ clearGCCallback(gcEventId);
2577
+ return response;
2280
2578
  }
2281
2579
 
2282
2580
  function redeemFreeReward(itemIds, generation_time) {
@@ -2345,6 +2643,25 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
2345
2643
  );
2346
2644
  }
2347
2645
 
2646
+ async function ensureLogin(promise) {
2647
+ const logOffEventId = uuidv4();
2648
+ const result = await Promise.race([
2649
+ new Promise((resolve) => {
2650
+ onEvent({
2651
+ name: SteamClientEvents.logOff,
2652
+ id: logOffEventId,
2653
+ callback: function () {
2654
+ resolve(null);
2655
+ },
2656
+ once: true,
2657
+ });
2658
+ }),
2659
+ promise,
2660
+ ]);
2661
+ offEvent(logOffEventId);
2662
+ return result;
2663
+ }
2664
+
2348
2665
  return {
2349
2666
  init,
2350
2667
  partySearch,
@@ -2443,6 +2760,7 @@ function SteamClient({ username, password, cookie, clientJsToken, isAutoRequestF
2443
2760
  return state;
2444
2761
  },
2445
2762
  log,
2763
+ error,
2446
2764
  isPrime() {
2447
2765
  return prime === true;
2448
2766
  },
@@ -2563,111 +2881,6 @@ export function increaseCSGO_VER() {
2563
2881
  return ++CSGO_VER;
2564
2882
  }
2565
2883
 
2566
- SteamClient.isAccountPlayable = async function isAccountPlayable({ cookie, clientJsToken, timeoutMs, onPlayable, onNotPlayable, keepLoginWhenPlayable, ...rest }) {
2567
- if (!clientJsToken && cookie) {
2568
- clientJsToken = await new SteamUser(typeof cookie === "function" ? await cookie() : cookie).getClientJsToken();
2569
- }
2570
- if (clientJsToken?.logged_in !== true) {
2571
- if (typeof onNotPlayable === "function") {
2572
- await onNotPlayable(null);
2573
- }
2574
- return { invalidClientJsToken: true };
2575
- }
2576
- return await new Promise((resolve) => {
2577
- const timeouts = [
2578
- setTimeout(() => {
2579
- doResolve({ timedOut: true });
2580
- }, timeoutMs || 30000),
2581
- ];
2582
-
2583
- const steamClient = new SteamClient({
2584
- isFakeGameScore: false,
2585
- isAutoPlay: true,
2586
- isPartyRegister: false,
2587
- isInvisible: true,
2588
- MAX_GAME_PLAY: 10,
2589
- games: 730,
2590
- clientJsToken,
2591
- ...rest,
2592
- });
2593
-
2594
- steamClient.onEvent(
2595
- SteamClientEvents.error,
2596
- ({ eresult, msg, error }) => {
2597
- doResolve({ eresult, msg, error });
2598
- },
2599
- true,
2600
- );
2601
-
2602
- steamClient.onEvent(
2603
- SteamClientEvents.csgoOnline,
2604
- (ClientWelcome) => {
2605
- doResolve({ playable: true });
2606
- },
2607
- true,
2608
- );
2609
-
2610
- steamClient.onEvent(
2611
- SteamClientEvents.csgoClientHello,
2612
- (ClientHello) => {
2613
- doResolve({ playable: true });
2614
- },
2615
- true,
2616
- );
2617
-
2618
- steamClient.onEvent(SteamClientEvents.playingState, ({ playing_blocked, playing_app }) => {
2619
- if (playing_blocked) {
2620
- doResolve({ playable: false });
2621
- } else {
2622
- timeouts.push(
2623
- setTimeout(function () {
2624
- const isBlocked = steamClient.isPlayingBlocked();
2625
- doResolve({ playable: !isBlocked });
2626
- }, 5000),
2627
- );
2628
- }
2629
- });
2630
-
2631
- // steamClient.onEvent("fatalError", () => {
2632
- // doResolve();
2633
- // });
2634
-
2635
- steamClient.init();
2636
-
2637
- async function doResolve(data) {
2638
- const playable = data?.playable === true;
2639
- timeouts.forEach((timeout) => clearTimeout(timeout));
2640
-
2641
- if (!playable || !keepLoginWhenPlayable) {
2642
- steamClient.doClearIntervals();
2643
- steamClient.offAllEvent();
2644
- }
2645
-
2646
- if (playable) {
2647
- if (typeof onPlayable === "function") {
2648
- try {
2649
- await onPlayable(steamClient);
2650
- } catch (e) {}
2651
- }
2652
- } else {
2653
- if (typeof onNotPlayable === "function") {
2654
- try {
2655
- await onNotPlayable(steamClient);
2656
- } catch (e) {}
2657
- }
2658
- }
2659
-
2660
- if (!playable || !keepLoginWhenPlayable) {
2661
- steamClient.logOff();
2662
- return resolve(data);
2663
- } else {
2664
- steamClient.offAllEvent();
2665
- return resolve({ ...data, steamClient });
2666
- }
2667
- }
2668
- });
2669
- };
2670
-
2671
2884
  class XpBonuxFlagsHelper {
2672
2885
  static kCSXpBonusFlags_EarnedXpThisPeriod = 1 << 0;
2673
2886
  static kCSXpBonusFlags_FirstReward = 1 << 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "steamutils",
3
- "version": "1.4.63",
3
+ "version": "1.4.64",
4
4
  "main": "index.js",
5
5
  "dependencies": {
6
6
  "alpha-common-utils": "^1.0.6",