@mtkruto/node 0.0.985 → 0.0.987

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.
Files changed (45) hide show
  1. package/esm/client/0_utilities.d.ts +2 -0
  2. package/esm/client/0_utilities.js +22 -0
  3. package/esm/client/1_client_abstract.d.ts +15 -10
  4. package/esm/client/1_client_abstract.js +26 -27
  5. package/esm/client/2_client_plain.d.ts +9 -3
  6. package/esm/client/2_client_plain.js +9 -5
  7. package/esm/client/3_client.d.ts +30 -19
  8. package/esm/client/3_client.js +319 -291
  9. package/esm/connection/0_connection.d.ts +1 -0
  10. package/esm/connection/0_connection.js +8 -0
  11. package/esm/connection/1_connection_web_socket.d.ts +3 -1
  12. package/esm/connection/1_connection_web_socket.js +28 -9
  13. package/esm/constants.d.ts +1 -1
  14. package/esm/constants.js +1 -1
  15. package/esm/deps.js +1 -1
  16. package/esm/tl/0_tl_raw_reader.d.ts +2 -0
  17. package/esm/tl/0_tl_raw_reader.js +3 -1
  18. package/esm/tl/3_tl_reader.js +2 -2
  19. package/esm/transport/2_transport_provider.d.ts +7 -14
  20. package/esm/transport/2_transport_provider.js +9 -12
  21. package/esm/utilities/0_queue.d.ts +6 -0
  22. package/esm/utilities/0_queue.js +38 -0
  23. package/package.json +1 -1
  24. package/script/client/0_utilities.d.ts +2 -0
  25. package/script/client/0_utilities.js +25 -1
  26. package/script/client/1_client_abstract.d.ts +15 -10
  27. package/script/client/1_client_abstract.js +26 -27
  28. package/script/client/2_client_plain.d.ts +9 -3
  29. package/script/client/2_client_plain.js +9 -5
  30. package/script/client/3_client.d.ts +30 -19
  31. package/script/client/3_client.js +318 -290
  32. package/script/connection/0_connection.d.ts +1 -0
  33. package/script/connection/0_connection.js +8 -0
  34. package/script/connection/1_connection_web_socket.d.ts +3 -1
  35. package/script/connection/1_connection_web_socket.js +28 -9
  36. package/script/constants.d.ts +1 -1
  37. package/script/constants.js +1 -1
  38. package/script/deps.js +1 -1
  39. package/script/tl/0_tl_raw_reader.d.ts +2 -0
  40. package/script/tl/0_tl_raw_reader.js +5 -2
  41. package/script/tl/3_tl_reader.js +1 -1
  42. package/script/transport/2_transport_provider.d.ts +7 -14
  43. package/script/transport/2_transport_provider.js +9 -12
  44. package/script/utilities/0_queue.d.ts +6 -0
  45. package/script/utilities/0_queue.js +42 -0
@@ -52,12 +52,13 @@ const _2_client_plain_js_1 = require("./2_client_plain.js");
52
52
  const _1_misc_js_1 = require("../utilities/1_misc.js");
53
53
  const _0_utilities_js_1 = require("./0_utilities.js");
54
54
  const _1_user_js_1 = require("../types/1_user.js");
55
+ const _0_tl_raw_reader_js_1 = require("../tl/0_tl_raw_reader.js");
56
+ const _0_queue_js_1 = require("../utilities/0_queue.js");
55
57
  const d = (0, deps_js_1.debug)("Client");
56
58
  const dGap = (0, deps_js_1.debug)("Client/recoverUpdateGap");
57
59
  const dGapC = (0, deps_js_1.debug)("Client/recoverChannelUpdateGap");
58
60
  const dAuth = (0, deps_js_1.debug)("Client/authorize");
59
61
  const dRecv = (0, deps_js_1.debug)("Client/receiveLoop");
60
- const UPDATE_GAP = Symbol();
61
62
  exports.getEntity = Symbol();
62
63
  exports.getStickerSetName = Symbol();
63
64
  exports.handleMigrationError = Symbol();
@@ -76,8 +77,8 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
76
77
  * @param apiHash App's API hash from [my.telegram.org/apps](https://my.telegram.org/apps). Defaults to empty string (unset).
77
78
  * @param params Other parameters.
78
79
  */
79
- constructor(storage = new _1_storage_memory_js_1.StorageMemory(), apiId = 0, apiHash = "", params, cdn = false) {
80
- super(params?.transportProvider, cdn);
80
+ constructor(storage = new _1_storage_memory_js_1.StorageMemory(), apiId = 0, apiHash = "", params) {
81
+ super(params);
81
82
  Object.defineProperty(this, "storage", {
82
83
  enumerable: true,
83
84
  configurable: true,
@@ -132,12 +133,6 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
132
133
  writable: true,
133
134
  value: void 0
134
135
  });
135
- Object.defineProperty(this, "updateHandler", {
136
- enumerable: true,
137
- configurable: true,
138
- writable: true,
139
- value: null
140
- });
141
136
  Object.defineProperty(this, "parseMode", {
142
137
  enumerable: true,
143
138
  configurable: true,
@@ -192,12 +187,26 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
192
187
  writable: true,
193
188
  value: void 0
194
189
  });
190
+ Object.defineProperty(this, "stateChangeHandler", {
191
+ enumerable: true,
192
+ configurable: true,
193
+ writable: true,
194
+ value: ((connected) => {
195
+ this.propagateConnectionState(connected ? "ready" : "not-connected");
196
+ }).bind(this)
197
+ });
195
198
  Object.defineProperty(this, "storageInited", {
196
199
  enumerable: true,
197
200
  configurable: true,
198
201
  writable: true,
199
202
  value: false
200
203
  });
204
+ Object.defineProperty(this, "connectMutex", {
205
+ enumerable: true,
206
+ configurable: true,
207
+ writable: true,
208
+ value: new deps_js_1.Mutex()
209
+ });
201
210
  Object.defineProperty(this, "connectionInited", {
202
211
  enumerable: true,
203
212
  configurable: true,
@@ -216,23 +225,25 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
216
225
  writable: true,
217
226
  value: 0n
218
227
  });
219
- Object.defineProperty(this, "updateApplicationMutex", {
228
+ Object.defineProperty(this, "handleUpdateQueue", {
220
229
  enumerable: true,
221
230
  configurable: true,
222
231
  writable: true,
223
- value: new deps_js_1.Mutex()
232
+ value: new _0_queue_js_1.Queue()
224
233
  });
225
- Object.defineProperty(this, "updateProcessLock", {
234
+ Object.defineProperty(this, "processUpdatesQueue", {
226
235
  enumerable: true,
227
236
  configurable: true,
228
237
  writable: true,
229
- value: new deps_js_1.Mutex()
238
+ value: new _0_queue_js_1.Queue()
230
239
  });
231
- Object.defineProperty(this, "updateGapRecoveryMutex", {
240
+ Object.defineProperty(this, "handler", {
232
241
  enumerable: true,
233
242
  configurable: true,
234
243
  writable: true,
235
- value: new deps_js_1.Mutex()
244
+ value: (_upd, next) => {
245
+ next();
246
+ }
236
247
  });
237
248
  this.parseMode = params?.parseMode ?? ParseMode.None;
238
249
  this.appVersion = params?.appVersion ?? constants_js_1.APP_VERSION;
@@ -244,6 +255,9 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
244
255
  this.publicKeys = params?.publicKeys;
245
256
  this.autoStart = params?.autoStart ?? true;
246
257
  }
258
+ propagateConnectionState(connectionState) {
259
+ return this.handler({ connectionState }, resolve);
260
+ }
247
261
  /**
248
262
  * Sets the DC and resets the auth key stored in the session provider
249
263
  * if the stored DC was not the same as the `dc` parameter.
@@ -273,38 +287,47 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
273
287
  * Before establishing the connection, the session is saved.
274
288
  */
275
289
  async connect() {
276
- if (!this.storageInited) {
277
- await this.storage.init();
278
- this.storageInited = true;
279
- }
280
- const authKey = await this.storage.getAuthKey();
281
- if (authKey == null) {
282
- const plain = new _2_client_plain_js_1.ClientPlain(this.transportProvider, this.publicKeys);
290
+ const release = await this.connectMutex.acquire();
291
+ try {
292
+ if (this.connected) {
293
+ return;
294
+ }
295
+ if (!this.storageInited) {
296
+ await this.storage.init();
297
+ this.storageInited = true;
298
+ }
299
+ const authKey = await this.storage.getAuthKey();
300
+ if (authKey == null) {
301
+ const plain = new _2_client_plain_js_1.ClientPlain({ initialDc: this.initialDc, transportProvider: this.transportProvider, cdn: this.cdn, publicKeys: this.publicKeys });
302
+ const dc = await this.storage.getDc();
303
+ if (dc != null) {
304
+ plain.setDc(dc);
305
+ }
306
+ await plain.connect();
307
+ const { authKey, salt } = await plain.createAuthKey();
308
+ await plain.disconnect();
309
+ await this.storage.setAuthKey(authKey);
310
+ await this.setAuth(authKey);
311
+ this.state.salt = salt;
312
+ }
313
+ else {
314
+ await this.setAuth(authKey);
315
+ }
283
316
  const dc = await this.storage.getDc();
284
317
  if (dc != null) {
285
- plain.setDc(dc);
318
+ await this.setDc(dc);
286
319
  }
287
- await plain.connect();
288
- const { authKey, salt } = await plain.createAuthKey();
289
- await plain.disconnect();
290
- await this.storage.setAuthKey(authKey);
291
- await this.setAuth(authKey);
292
- this.state.salt = salt;
293
- }
294
- else {
295
- await this.setAuth(authKey);
296
- }
297
- const dc = await this.storage.getDc();
298
- if (dc != null) {
299
- await this.setDc(dc);
320
+ await super.connect();
321
+ if (dc == null) {
322
+ await this.storage.setDc(this.initialDc);
323
+ }
324
+ d("encrypted client connected");
325
+ (0, _1_misc_js_1.drop)(this.receiveLoop());
326
+ (0, _1_misc_js_1.drop)(this.pingLoop());
300
327
  }
301
- await super.connect();
302
- if (dc == null) {
303
- await this.storage.setDc(this.transportProvider.initialDc);
328
+ finally {
329
+ release();
304
330
  }
305
- d("encrypted client connected");
306
- (0, _1_misc_js_1.drop)(this.receiveLoop());
307
- (0, _1_misc_js_1.drop)(this.pingLoop());
308
331
  }
309
332
  async fetchState(source) {
310
333
  const state = await this.invoke(new functions.UpdatesGetState());
@@ -378,7 +401,7 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
378
401
  params = (0, _1_misc_js_1.mustPrompt)("Bot token:");
379
402
  }
380
403
  else {
381
- params = { phone: () => (0, _1_misc_js_1.mustPrompt)("Phone number:"), code: () => (0, _1_misc_js_1.mustPrompt)("Verification code:"), password: () => (0, _1_misc_js_1.mustPrompt)(`Password:`) };
404
+ params = { phone: () => (0, _1_misc_js_1.mustPrompt)("Phone number:"), code: () => (0, _1_misc_js_1.mustPrompt)("Verification code:"), password: () => (0, _1_misc_js_1.mustPrompt)("Password:") };
382
405
  }
383
406
  }
384
407
  dAuth("authorizing with %s", typeof params === "string" ? "bot token" : params instanceof types.AuthExportedAuthorization ? "exported authorization" : "AuthorizeUserParams");
@@ -538,7 +561,7 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
538
561
  await this.authorize(params);
539
562
  }
540
563
  async receiveLoop() {
541
- if (!this.auth) {
564
+ if (!this.auth || !this.transport) {
542
565
  throw new Error("Not connected");
543
566
  }
544
567
  while (this.connected) {
@@ -547,7 +570,7 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
547
570
  await this.send(new types.MsgsAck({ msgIds: [...this.toAcknowledge] }));
548
571
  this.toAcknowledge.clear();
549
572
  }
550
- const buffer = await this.transport.receive();
573
+ const buffer = await this.transport.transport.receive();
551
574
  let decrypted;
552
575
  try {
553
576
  decrypted = await ((0, _0_message_js_1.decryptMessage)(buffer, this.auth.key, this.auth.id, this.sessionId));
@@ -564,8 +587,8 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
564
587
  body = new _3_tl_reader_js_1.TLReader((0, deps_js_1.gunzip)(body.packedData)).readObject();
565
588
  }
566
589
  dRecv("received %s", body.constructor.name);
567
- if (body instanceof types.Updates || body instanceof types.TypeUpdate) {
568
- (0, _1_misc_js_1.drop)(this.processUpdates(body));
590
+ if (body instanceof types.TypeUpdates || body instanceof types.TypeUpdate) {
591
+ this.processUpdatesQueue.add(() => this.processUpdates(body));
569
592
  }
570
593
  else if (message.body instanceof _5_rpc_result_js_1.RPCResult) {
571
594
  let result = message.body.result;
@@ -592,7 +615,10 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
592
615
  }
593
616
  };
594
617
  if (result instanceof types.TypeUpdates || result instanceof types.TypeUpdate) {
595
- this.processUpdates(result).then(resolvePromise).catch();
618
+ this.processUpdatesQueue.add(async () => {
619
+ await this.processUpdates(result);
620
+ resolvePromise();
621
+ });
596
622
  }
597
623
  else {
598
624
  await this.processResult(result);
@@ -622,6 +648,10 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
622
648
  if (!this.connected) {
623
649
  break;
624
650
  }
651
+ else if (err instanceof _0_tl_raw_reader_js_1.TLError) {
652
+ dRecv("failed to deserialize: %o", err);
653
+ (0, _1_misc_js_1.drop)(this.recoverUpdateGap("deserialize"));
654
+ }
625
655
  else {
626
656
  throw err;
627
657
  }
@@ -640,7 +670,7 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
640
670
  }
641
671
  }
642
672
  async invoke(function_, noWait) {
643
- if (!this.auth) {
673
+ if (!this.auth || !this.transport) {
644
674
  if (this.autoStart && !this.autoStarted) {
645
675
  await this.start();
646
676
  }
@@ -648,7 +678,7 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
648
678
  throw new Error("Not connected");
649
679
  }
650
680
  }
651
- if (!this.auth) {
681
+ if (!this.auth || !this.transport) {
652
682
  (0, _0_control_js_1.UNREACHABLE)();
653
683
  }
654
684
  let seqNo = this.state.seqNo * 2;
@@ -658,7 +688,7 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
658
688
  }
659
689
  const messageId = this.lastMsgId = (0, _0_message_js_1.getMessageId)(this.lastMsgId);
660
690
  const message = new _6_message_js_1.Message(messageId, seqNo, function_);
661
- await this.transport.send(await (0, _0_message_js_1.encryptMessage)(message, this.auth.key, this.auth.id, this.state.salt, this.sessionId));
691
+ await this.transport.transport.send(await (0, _0_message_js_1.encryptMessage)(message, this.auth.key, this.auth.id, this.state.salt, this.sessionId));
662
692
  d("invoked %s", function_.constructor.name);
663
693
  if (noWait) {
664
694
  return;
@@ -710,222 +740,160 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
710
740
  }
711
741
  }
712
742
  }
713
- async applyUpdateNoGap(update, usePts = true) {
714
- const release = await this.updateApplicationMutex.acquire();
715
- try {
716
- if ((update instanceof types.UpdateNewMessage) ||
717
- (update instanceof types.UpdateDeleteMessages) ||
718
- (update instanceof types.UpdateReadHistoryInbox) ||
719
- (update instanceof types.UpdateReadHistoryOutbox) ||
720
- (update instanceof types.UpdateWebPage) ||
721
- (update instanceof types.UpdateReadMessagesContents) ||
722
- (update instanceof types.UpdateEditMessage) ||
723
- (update instanceof types.UpdateFolderPeers) ||
724
- (update instanceof types.UpdatePinnedMessages) ||
725
- (update instanceof types.UpdatePinnedChannelMessages) ||
726
- (update instanceof types.UpdateShortMessage) ||
727
- (update instanceof types.UpdateShortChatMessage) ||
728
- (update instanceof types.UpdateShortSentMessage)) {
729
- if (update.pts != 0 && update.ptsCount != 0) {
730
- const localState = await this.getLocalState();
731
- if (localState.pts + update.ptsCount > update.pts) {
732
- // the update is already applied
733
- return;
734
- }
735
- else if (localState.pts + update.ptsCount < update.pts) {
736
- // there is an update gap that needs to be filled
737
- throw UPDATE_GAP;
738
- }
739
- localState.pts = update.pts;
740
- d("applied update with pts %d", update.pts);
741
- await this.storage.setState(localState);
742
- }
743
- }
744
- else if (usePts &&
745
- ((update instanceof types.UpdateNewChannelMessage) ||
746
- (update instanceof types.UpdateDeleteChannelMessages) ||
747
- (update instanceof types.UpdateEditChannelMessage) ||
748
- (update instanceof types.UpdateChannelWebPage))) {
749
- const channelId = update instanceof types.UpdateNewChannelMessage || update instanceof types.UpdateEditChannelMessage ? update.message.peerId[_1_tl_object_js_1.as](types.PeerChannel).channelId : update.channelId;
750
- let localPts = await this.storage.getChannelPts(channelId);
751
- if (!localPts) {
752
- localPts = update.pts - update.ptsCount;
753
- }
754
- if (localPts + update.ptsCount > update.pts) {
755
- // already applied
756
- return;
757
- }
758
- else if (localPts + update.ptsCount < update.pts) {
759
- // should call channelGetDifference
760
- throw UPDATE_GAP;
761
- }
762
- d("applied update with pts %d", update.pts);
763
- await this.storage.setChannelPts(channelId, update.pts);
743
+ async checkGap(pts, ptsCount, assertNoGap) {
744
+ const localState = await this.getLocalState();
745
+ if (localState.pts + ptsCount < pts) {
746
+ if (assertNoGap) {
747
+ (0, _0_control_js_1.UNREACHABLE)();
764
748
  }
765
- if (update instanceof types.UpdateNewMessage || update instanceof types.UpdateNewMessage || update instanceof types.UpdateNewChannelMessage || update instanceof types.UpdateNewChannelMessage) {
766
- if (update.message instanceof types.Message || update.message instanceof types.MessageService) {
767
- await this.storage.setMessage((0, _0_utilities_js_1.peerToChatId)(update.message.peerId), update.message.id, update.message);
768
- }
749
+ else {
750
+ await this.recoverUpdateGap("processUpdates");
769
751
  }
770
- else if (update instanceof types.UpdateDeleteChannelMessages) {
771
- for (const message of update.messages) {
772
- await this.storage.setMessage((0, _0_utilities_js_1.getChannelChatId)(update.channelId), message, null);
773
- }
752
+ }
753
+ }
754
+ async checkChannelGap(channelId, pts, ptsCount, assertNoGap) {
755
+ let localPts = await this.storage.getChannelPts(channelId);
756
+ if (!localPts) {
757
+ localPts = pts - ptsCount;
758
+ }
759
+ if (localPts + ptsCount < pts) {
760
+ if (assertNoGap) {
761
+ (0, _0_control_js_1.UNREACHABLE)();
774
762
  }
775
- else if (update instanceof types.UpdateDeleteMessages) {
776
- for (const message of update.messages) {
777
- const chatId = await this.storage.getMessageChat(message);
778
- if (chatId) {
779
- await this.storage.setMessage(chatId, message, null);
780
- }
781
- }
763
+ else {
764
+ await this.recoverChannelUpdateGap(channelId, "processUpdates");
782
765
  }
783
- // apply update (call listeners)
784
- this.updateHandler?.(this, update);
785
- }
786
- finally {
787
- release();
788
766
  }
789
767
  }
790
- async applyUpdate(update) {
791
- if (update instanceof types.TypeUpdates &&
792
- !(update instanceof types.UpdateShortMessage) &&
793
- !(update instanceof types.UpdateShortChatMessage) &&
794
- !(update instanceof types.UpdateShortSentMessage)) {
795
- // other constructors inheriting Updates are not applicable
796
- (0, _0_control_js_1.UNREACHABLE)();
768
+ async processUpdates(updates_, assertNoGap = false) {
769
+ /// First, individual updates (Update[1]) and updateShort* are extracted from Updates.[2]
770
+ ///
771
+ /// If an updatesTooLong[3] was received, an update gap recovery is initiated and no further action will be taken.
772
+ ///
773
+ /// [1]: https://core.telegram.org/type/Update
774
+ /// [2]: https://core.telegram.org/type/Updates
775
+ /// [3]: https://core.telegram.org/constructor/updatesTooLong
776
+ let updates;
777
+ if (updates_ instanceof types.UpdatesCombined || updates_ instanceof types.Updates) {
778
+ updates = updates_.updates;
779
+ }
780
+ else if (updates_ instanceof types.UpdateShort) {
781
+ updates = [updates_.update];
782
+ }
783
+ else if (updates_ instanceof types.UpdateShortMessage ||
784
+ updates_ instanceof types.UpdateShortChatMessage ||
785
+ updates_ instanceof types.UpdateShortSentMessage) {
786
+ updates = [updates_];
787
+ }
788
+ else if (updates_ instanceof types.UpdatesTooLong) {
789
+ await this.recoverUpdateGap("updatesTooLong");
790
+ return;
797
791
  }
798
- if (update instanceof types.TypeUpdate && update instanceof types.UpdateChannelTooLong) {
799
- // updateChannelTooLong is not applicable
800
- (0, _0_control_js_1.UNREACHABLE)();
792
+ else if (updates_ instanceof types.TypeUpdate) {
793
+ updates = [updates_];
801
794
  }
802
- // can't apply updates when filling gap
803
- const release = await this.updateGapRecoveryMutex.acquire();
804
- try {
805
- await this.applyUpdateNoGap(update);
806
- release();
795
+ else {
796
+ (0, _0_control_js_1.UNREACHABLE)();
807
797
  }
808
- catch (err) {
809
- release();
810
- if (err == UPDATE_GAP) {
811
- if ((update instanceof types.UpdateNewChannelMessage) ||
812
- (update instanceof types.UpdateDeleteChannelMessages) ||
813
- (update instanceof types.UpdateEditChannelMessage) ||
814
- (update instanceof types.UpdateChannelWebPage)) {
815
- const channelId = update instanceof types.UpdateNewChannelMessage || update instanceof types.UpdateEditChannelMessage ? update.message.peerId[_1_tl_object_js_1.as](types.PeerChannel).channelId : update.channelId;
816
- await this.recoverChannelUpdateGap(channelId, "applyUpdate");
798
+ /// Then, we go through each Update and updateShort*, and see if they are order-sensitive.
799
+ /// If they were, we check the local state to see if it is OK to process them right away.
800
+ ///
801
+ /// If we there was a gap, a recovery process will be initiated and the processing will be postponed.
802
+ let localState = null;
803
+ let originalPts = null;
804
+ const channelPtsMap = new Map();
805
+ for (const update of updates) {
806
+ if ((0, _0_utilities_js_1.hasPts)(update)) {
807
+ if (update.pts == 0) {
808
+ continue;
817
809
  }
818
- else if ((update instanceof types.UpdateNewMessage) ||
819
- (update instanceof types.UpdateDeleteMessages) ||
820
- (update instanceof types.UpdateReadHistoryInbox) ||
821
- (update instanceof types.UpdateReadHistoryOutbox) ||
822
- (update instanceof types.UpdateWebPage) ||
823
- (update instanceof types.UpdateReadMessagesContents) ||
824
- (update instanceof types.UpdateEditMessage) ||
825
- (update instanceof types.UpdateFolderPeers) ||
826
- (update instanceof types.UpdatePinnedMessages) ||
827
- (update instanceof types.UpdatePinnedChannelMessages) ||
828
- (update instanceof types.UpdateShortMessage) ||
829
- (update instanceof types.UpdateShortChatMessage) ||
830
- (update instanceof types.UpdateShortSentMessage)) {
831
- await this.recoverUpdateGap("applyUpdate");
810
+ await this.checkGap(update.pts, update.ptsCount, assertNoGap);
811
+ localState ??= await this.getLocalState();
812
+ originalPts ??= localState.pts;
813
+ if (localState.pts + update.ptsCount > update.pts) {
814
+ updates = updates.filter((v) => v != update);
832
815
  }
833
816
  else {
834
- // can't detect update gap from other types of updates
835
- (0, _0_control_js_1.UNREACHABLE)();
817
+ localState.pts = update.pts;
836
818
  }
837
- // just for integrity
838
- const release = await this.updateGapRecoveryMutex.acquire();
839
- try {
840
- await this.applyUpdateNoGap(update);
819
+ }
820
+ else if ((0, _0_utilities_js_1.hasChannelPts)(update)) {
821
+ if (update.pts == 0) {
822
+ continue;
841
823
  }
842
- catch (err) {
843
- if (err == UPDATE_GAP) {
844
- // the gap must have been filled until now
845
- (0, _0_control_js_1.UNREACHABLE)();
846
- }
847
- else {
848
- throw err;
849
- }
824
+ const ptsCount = "ptsCount" in update ? update.ptsCount : 1;
825
+ const channelId = update instanceof types.UpdateNewChannelMessage || update instanceof types.UpdateEditChannelMessage ? update.message.peerId[_1_tl_object_js_1.as](types.PeerChannel).channelId : update.channelId;
826
+ await this.checkChannelGap(channelId, update.pts, ptsCount, assertNoGap);
827
+ let currentPts = channelPtsMap.get(channelId);
828
+ if (currentPts === undefined) {
829
+ currentPts = await this.storage.getChannelPts(channelId);
830
+ }
831
+ currentPts ??= update.pts;
832
+ if (currentPts + ptsCount > update.pts) {
833
+ updates = updates.filter((v) => v != update);
850
834
  }
851
- finally {
852
- release();
835
+ else {
836
+ channelPtsMap.set(channelId, update.pts);
853
837
  }
854
838
  }
855
- else {
856
- throw err;
839
+ }
840
+ if (!assertNoGap) {
841
+ if (localState != null && originalPts != null && localState.pts != originalPts) {
842
+ await this.storage.setState(localState);
843
+ }
844
+ for (const [channelId, pts] of channelPtsMap.entries()) {
845
+ await this.storage.setChannelPts(channelId, pts);
857
846
  }
858
847
  }
859
- }
860
- async processUpdates(updates, release) {
861
- release ??= await this.updateProcessLock.acquire();
862
- try {
863
- if (updates instanceof types.TypeUpdates) {
864
- if (updates instanceof types.Updates) {
865
- await this.processChats(updates.chats);
866
- await this.processUsers(updates.users);
867
- await this.setUpdateStateDate(updates.date);
868
- for (const update of updates.updates) {
869
- await this.processUpdates(update, release);
870
- }
871
- }
872
- else if (updates instanceof types.UpdateShortMessage ||
873
- updates instanceof types.UpdateShortChatMessage ||
874
- updates instanceof types.UpdateShortSentMessage) {
875
- await this.setUpdateStateDate(updates.date);
876
- await this.applyUpdate(updates);
877
- }
878
- else if (updates instanceof types.UpdatesTooLong) {
879
- await this.recoverUpdateGap("updatesTooLong");
880
- }
881
- else if (updates instanceof types.UpdatesCombined) {
882
- await this.setUpdateStateDate(updates.date);
883
- await this.processChats(updates.chats);
884
- await this.processUsers(updates.users);
885
- for (const update of updates.updates) {
886
- await this.processUpdates(update, release);
887
- }
888
- }
848
+ /// We process the updates when we are sure there is no gap.
849
+ if (updates_ instanceof types.Updates || updates_ instanceof types.UpdatesCombined) {
850
+ await this.processChats(updates_.chats);
851
+ await this.processUsers(updates_.users);
852
+ await this.setUpdateStateDate(updates_.date);
853
+ }
854
+ else if (updates_ instanceof types.UpdateShort) {
855
+ await this.setUpdateStateDate(updates_.date);
856
+ }
857
+ const updatesToHandle = new Array();
858
+ for (const update of updates) {
859
+ if (update instanceof types.UpdateShortMessage ||
860
+ update instanceof types.UpdateShortChatMessage ||
861
+ update instanceof types.UpdateShortSentMessage) {
862
+ await this.setUpdateStateDate(update.date);
889
863
  }
890
- else if (updates instanceof types.TypeUpdate && updates instanceof types.UpdateChannelTooLong) {
891
- if (updates.pts != undefined) {
892
- await this.storage.setChannelPts(updates.channelId, updates.pts);
864
+ else if (update instanceof types.UpdateChannelTooLong) {
865
+ if (update.pts != undefined) {
866
+ await this.storage.setChannelPts(update.channelId, update.pts);
893
867
  }
894
- await this.recoverChannelUpdateGap(updates.channelId, "updateChannelTooLong");
868
+ await this.recoverChannelUpdateGap(update.channelId, "updateChannelTooLong");
895
869
  }
896
- else {
897
- if (updates instanceof types.UpdateUserName) {
898
- await this.storage.updateUsernames("user", updates.userId, updates.usernames.map((v) => v[_1_tl_object_js_1.as](types.Username)).map((v) => v.username));
870
+ else if (update instanceof types.UpdateUserName) {
871
+ await this.storage.updateUsernames("user", update.userId, update.usernames.map((v) => v[_1_tl_object_js_1.as](types.Username)).map((v) => v.username));
872
+ }
873
+ else if (update instanceof types.UpdatePtsChanged) {
874
+ await this.fetchState("updatePtsChanged");
875
+ if (this.updateState) {
876
+ await this.storage.setState(this.updateState);
899
877
  }
900
- else if (updates instanceof types.UpdatePtsChanged) {
901
- await this.fetchState("updatePtsChanged");
902
- if (this.updateState) {
903
- await this.storage.setState(this.updateState);
904
- }
905
- else {
906
- (0, _0_control_js_1.UNREACHABLE)();
907
- }
878
+ else {
879
+ (0, _0_control_js_1.UNREACHABLE)();
908
880
  }
909
- await this.applyUpdate(updates);
881
+ }
882
+ /// If there were any Update, they will be passed to the update handling queue.
883
+ if (update instanceof types.TypeUpdate) {
884
+ updatesToHandle.push(update);
910
885
  }
911
886
  }
912
- catch (err) {
913
- d("error processing updates: %O", err);
914
- }
915
- finally {
916
- release();
917
- }
887
+ this.handleUpdateQueue.add(async () => {
888
+ for (const update of updatesToHandle) {
889
+ await this.handleUpdate(update);
890
+ }
891
+ });
918
892
  }
919
893
  async setUpdateStateDate(date) {
920
- const release = await this.updateApplicationMutex.acquire();
921
- try {
922
- const localState = await this.getLocalState();
923
- localState.date = date;
924
- await this.storage.setState(localState);
925
- }
926
- finally {
927
- release();
928
- }
894
+ const localState = await this.getLocalState();
895
+ localState.date = date;
896
+ await this.storage.setState(localState);
929
897
  }
930
898
  async getLocalState() {
931
899
  let localState = await this.storage.getState();
@@ -949,7 +917,7 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
949
917
  }
950
918
  async recoverUpdateGap(source) {
951
919
  dGap("recovering from update gap [%s]", source);
952
- const release = await this.updateGapRecoveryMutex.acquire();
920
+ await this.propagateConnectionState("updating");
953
921
  try {
954
922
  let state = await this.getLocalState();
955
923
  while (true) {
@@ -958,10 +926,10 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
958
926
  await this.processChats(difference.chats);
959
927
  await this.processUsers(difference.users);
960
928
  for (const message of difference.newMessages) {
961
- await this.applyUpdateNoGap(new types.UpdateNewMessage({ message, pts: 0, ptsCount: 0 }));
929
+ await this.processUpdates(new types.UpdateNewMessage({ message, pts: 0, ptsCount: 0 }), true);
962
930
  }
963
931
  for (const update of difference.otherUpdates) {
964
- await this.applyUpdateNoGap(update);
932
+ await this.processUpdates(update, true);
965
933
  }
966
934
  if (difference instanceof types.UpdatesDifference) {
967
935
  await this.storage.setState(difference.state[_1_tl_object_js_1.as](types.UpdatesState));
@@ -976,6 +944,7 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
976
944
  }
977
945
  }
978
946
  else if (difference instanceof types.UpdatesDifferenceTooLong) {
947
+ // TODO: we actually do now
979
948
  // stored messages should be invalidated in case we store messages in the future
980
949
  state.pts = difference.pts;
981
950
  dGap("received differenceTooLong");
@@ -991,61 +960,55 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
991
960
  }
992
961
  }
993
962
  finally {
994
- release();
963
+ this.stateChangeHandler(this.connected);
995
964
  }
996
965
  }
997
966
  async recoverChannelUpdateGap(channelId, source) {
998
967
  dGapC("recovering channel update gap [%o, %s]", channelId, source);
999
- const release = await this.updateGapRecoveryMutex.acquire();
1000
- try {
1001
- const pts_ = await this.storage.getChannelPts(channelId);
1002
- let pts = pts_ == null ? 1 : pts_;
1003
- while (true) {
1004
- const { accessHash } = await this.getInputPeer(constants_js_1.ZERO_CHANNEL_ID + -Number(channelId)).then((v) => v[_1_tl_object_js_1.as](types.InputPeerChannel));
1005
- const difference = await this.invoke(new functions.UpdatesGetChannelDifference({
1006
- pts,
1007
- channel: new types.InputChannel({ channelId, accessHash: accessHash }),
1008
- filter: new types.ChannelMessagesFilterEmpty(),
1009
- limit: await this.storage.getAccountType() == "user" ? constants_js_1.CHANNEL_DIFFERENCE_LIMIT_USER : constants_js_1.CHANNEL_DIFFERENCE_LIMIT_BOT,
1010
- }));
1011
- if (difference instanceof types.UpdatesChannelDifference) {
1012
- await this.processChats(difference.chats);
1013
- await this.processUsers(difference.users);
1014
- for (const message of difference.newMessages) {
1015
- await this.applyUpdateNoGap(new types.UpdateNewChannelMessage({ message, pts: 0, ptsCount: 0 }), false);
1016
- }
1017
- for (const update of difference.otherUpdates) {
1018
- await this.applyUpdateNoGap(update, false);
1019
- }
1020
- await this.storage.setChannelPts(channelId, difference.pts);
1021
- dGapC("recovered from update gap [%o, %s]", channelId, source);
1022
- break;
968
+ const pts_ = await this.storage.getChannelPts(channelId);
969
+ let pts = pts_ == null ? 1 : pts_;
970
+ while (true) {
971
+ const { accessHash } = await this.getInputPeer(constants_js_1.ZERO_CHANNEL_ID + -Number(channelId)).then((v) => v[_1_tl_object_js_1.as](types.InputPeerChannel));
972
+ const difference = await this.invoke(new functions.UpdatesGetChannelDifference({
973
+ pts,
974
+ channel: new types.InputChannel({ channelId, accessHash: accessHash }),
975
+ filter: new types.ChannelMessagesFilterEmpty(),
976
+ limit: await this.storage.getAccountType() == "user" ? constants_js_1.CHANNEL_DIFFERENCE_LIMIT_USER : constants_js_1.CHANNEL_DIFFERENCE_LIMIT_BOT,
977
+ }));
978
+ if (difference instanceof types.UpdatesChannelDifference) {
979
+ await this.processChats(difference.chats);
980
+ await this.processUsers(difference.users);
981
+ for (const message of difference.newMessages) {
982
+ await this.processUpdates(new types.UpdateNewChannelMessage({ message, pts: 0, ptsCount: 0 }), true);
1023
983
  }
1024
- else if (difference instanceof types.UpdatesChannelDifferenceTooLong) {
1025
- // invalidate messages
1026
- dGapC("received channelDifferenceTooLong");
1027
- await this.processChats(difference.chats);
1028
- await this.processUsers(difference.users);
1029
- for (const message of difference.messages) {
1030
- await this.applyUpdateNoGap(new types.UpdateNewChannelMessage({ message, pts: 0, ptsCount: 0 }), false);
1031
- }
1032
- const pts_ = difference.dialog[_1_tl_object_js_1.as](types.Dialog).pts;
1033
- if (pts_ != undefined) {
1034
- pts = pts_;
1035
- }
1036
- else {
1037
- (0, _0_control_js_1.UNREACHABLE)();
1038
- }
1039
- dGapC("processed channelDifferenceTooLong");
984
+ for (const update of difference.otherUpdates) {
985
+ await this.processUpdates(update, true);
1040
986
  }
1041
- else if (difference instanceof types.UpdatesChannelDifferenceEmpty) {
1042
- dGapC("there was no update gap");
1043
- break;
987
+ await this.storage.setChannelPts(channelId, difference.pts);
988
+ dGapC("recovered from update gap [%o, %s]", channelId, source);
989
+ break;
990
+ }
991
+ else if (difference instanceof types.UpdatesChannelDifferenceTooLong) {
992
+ // invalidate messages
993
+ dGapC("received channelDifferenceTooLong");
994
+ await this.processChats(difference.chats);
995
+ await this.processUsers(difference.users);
996
+ for (const message of difference.messages) {
997
+ await this.processUpdates(new types.UpdateNewChannelMessage({ message, pts: 0, ptsCount: 0 }), true);
998
+ }
999
+ const pts_ = difference.dialog[_1_tl_object_js_1.as](types.Dialog).pts;
1000
+ if (pts_ != undefined) {
1001
+ pts = pts_;
1044
1002
  }
1003
+ else {
1004
+ (0, _0_control_js_1.UNREACHABLE)();
1005
+ }
1006
+ dGapC("processed channelDifferenceTooLong");
1007
+ }
1008
+ else if (difference instanceof types.UpdatesChannelDifferenceEmpty) {
1009
+ dGapC("there was no update gap");
1010
+ break;
1045
1011
  }
1046
- }
1047
- finally {
1048
- release();
1049
1012
  }
1050
1013
  }
1051
1014
  async getInputPeer(id) {
@@ -1362,7 +1325,8 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
1362
1325
  langPack: this.langPack,
1363
1326
  systemLangCode: this.systemLangCode,
1364
1327
  systemVersion: this.systemVersion,
1365
- }, true);
1328
+ cdn: true,
1329
+ });
1366
1330
  let dc = String(dcId);
1367
1331
  if (this.dcId < 0) {
1368
1332
  dc += "-test";
@@ -1473,5 +1437,69 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
1473
1437
  }
1474
1438
  return (0, _1_user_js_1.constructUser)(users[0][_1_tl_object_js_1.as](types.User));
1475
1439
  }
1440
+ async handleUpdate(update) {
1441
+ if (update instanceof types.UpdateNewMessage || update instanceof types.UpdateNewMessage || update instanceof types.UpdateNewChannelMessage || update instanceof types.UpdateNewChannelMessage) {
1442
+ if (update.message instanceof types.Message || update.message instanceof types.MessageService) {
1443
+ await this.storage.setMessage((0, _0_utilities_js_1.peerToChatId)(update.message.peerId), update.message.id, update.message);
1444
+ }
1445
+ }
1446
+ else if (update instanceof types.UpdateDeleteChannelMessages) {
1447
+ for (const message of update.messages) {
1448
+ await this.storage.setMessage((0, _0_utilities_js_1.getChannelChatId)(update.channelId), message, null);
1449
+ }
1450
+ }
1451
+ else if (update instanceof types.UpdateDeleteMessages) {
1452
+ for (const message of update.messages) {
1453
+ const chatId = await this.storage.getMessageChat(message);
1454
+ if (chatId) {
1455
+ await this.storage.setMessage(chatId, message, null);
1456
+ }
1457
+ }
1458
+ }
1459
+ if (update instanceof types.UpdateNewMessage ||
1460
+ update instanceof types.UpdateNewChannelMessage ||
1461
+ update instanceof types.UpdateEditMessage ||
1462
+ update instanceof types.UpdateEditChannelMessage) {
1463
+ const key = update instanceof types.UpdateNewMessage || update instanceof types.UpdateNewChannelMessage ? "message" : "editedMessage";
1464
+ const message = await (0, _3_message_js_1.constructMessage)(update.message, this[exports.getEntity].bind(this), this.getMessage.bind(this), this[exports.getStickerSetName].bind(this));
1465
+ await this.handler({ [key]: message }, resolve);
1466
+ }
1467
+ }
1468
+ use(middleware) {
1469
+ const handler = this.handler;
1470
+ this.handler = async (upd, next) => {
1471
+ let called = false;
1472
+ await middleware(upd, async () => {
1473
+ if (called)
1474
+ return;
1475
+ called = true;
1476
+ await handler(upd, next);
1477
+ });
1478
+ };
1479
+ }
1480
+ on(filter, handler) {
1481
+ const type = typeof filter === "string" ? filter : filter[0];
1482
+ const keys = Array.isArray(filter) ? filter.slice(1) : [];
1483
+ this.use((update, next) => {
1484
+ if (type in update) {
1485
+ if (keys.length > 0) {
1486
+ for (const key of keys) {
1487
+ // deno-lint-ignore ban-ts-comment
1488
+ // @ts-ignore
1489
+ if (!(key in update[type])) {
1490
+ return next();
1491
+ }
1492
+ }
1493
+ }
1494
+ // deno-lint-ignore ban-ts-comment
1495
+ // @ts-ignore
1496
+ return handler(update, next);
1497
+ }
1498
+ else {
1499
+ return next();
1500
+ }
1501
+ });
1502
+ }
1476
1503
  }
1477
1504
  exports.Client = Client;
1505
+ const resolve = () => Promise.resolve();