@mtkruto/node 0.0.986 → 0.0.988

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