@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.
- package/esm/client/0_utilities.d.ts +2 -0
- package/esm/client/0_utilities.js +22 -0
- package/esm/client/1_client_abstract.d.ts +15 -10
- package/esm/client/1_client_abstract.js +26 -27
- package/esm/client/2_client_plain.d.ts +9 -3
- package/esm/client/2_client_plain.js +9 -5
- package/esm/client/3_client.d.ts +28 -19
- package/esm/client/3_client.js +314 -291
- package/esm/connection/0_connection.d.ts +1 -0
- package/esm/connection/0_connection.js +8 -0
- package/esm/connection/1_connection_web_socket.d.ts +3 -1
- package/esm/connection/1_connection_web_socket.js +28 -9
- package/esm/constants.d.ts +1 -1
- package/esm/constants.js +1 -1
- package/esm/deps.js +1 -1
- package/esm/transport/2_transport_provider.d.ts +7 -14
- package/esm/transport/2_transport_provider.js +9 -12
- package/esm/utilities/0_queue.d.ts +6 -0
- package/esm/utilities/0_queue.js +38 -0
- package/package.json +1 -1
- package/script/client/0_utilities.d.ts +2 -0
- package/script/client/0_utilities.js +25 -1
- package/script/client/1_client_abstract.d.ts +15 -10
- package/script/client/1_client_abstract.js +26 -27
- package/script/client/2_client_plain.d.ts +9 -3
- package/script/client/2_client_plain.js +9 -5
- package/script/client/3_client.d.ts +28 -19
- package/script/client/3_client.js +313 -290
- package/script/connection/0_connection.d.ts +1 -0
- package/script/connection/0_connection.js +8 -0
- package/script/connection/1_connection_web_socket.d.ts +3 -1
- package/script/connection/1_connection_web_socket.js +28 -9
- package/script/constants.d.ts +1 -1
- package/script/constants.js +1 -1
- package/script/deps.js +1 -1
- package/script/transport/2_transport_provider.d.ts +7 -14
- package/script/transport/2_transport_provider.js +9 -12
- package/script/utilities/0_queue.d.ts +6 -0
- package/script/utilities/0_queue.js +42 -0
package/esm/client/3_client.js
CHANGED
|
@@ -24,15 +24,15 @@ import { checkPassword } from "./0_password.js";
|
|
|
24
24
|
import { ClientAbstract } from "./1_client_abstract.js";
|
|
25
25
|
import { ClientPlain } from "./2_client_plain.js";
|
|
26
26
|
import { drop, mustPrompt, mustPromptOneOf } from "../utilities/1_misc.js";
|
|
27
|
-
import { getChannelChatId, peerToChatId } from "./0_utilities.js";
|
|
27
|
+
import { getChannelChatId, hasChannelPts, hasPts, peerToChatId } from "./0_utilities.js";
|
|
28
28
|
import { constructUser } from "../types/1_user.js";
|
|
29
29
|
import { TLError } from "../tl/0_tl_raw_reader.js";
|
|
30
|
+
import { Queue } from "../utilities/0_queue.js";
|
|
30
31
|
const d = debug("Client");
|
|
31
32
|
const dGap = debug("Client/recoverUpdateGap");
|
|
32
33
|
const dGapC = debug("Client/recoverChannelUpdateGap");
|
|
33
34
|
const dAuth = debug("Client/authorize");
|
|
34
35
|
const dRecv = debug("Client/receiveLoop");
|
|
35
|
-
const UPDATE_GAP = Symbol();
|
|
36
36
|
export const getEntity = Symbol();
|
|
37
37
|
export const getStickerSetName = Symbol();
|
|
38
38
|
export const handleMigrationError = Symbol();
|
|
@@ -51,8 +51,8 @@ export class Client extends ClientAbstract {
|
|
|
51
51
|
* @param apiHash App's API hash from [my.telegram.org/apps](https://my.telegram.org/apps). Defaults to empty string (unset).
|
|
52
52
|
* @param params Other parameters.
|
|
53
53
|
*/
|
|
54
|
-
constructor(storage = new StorageMemory(), apiId = 0, apiHash = "", params
|
|
55
|
-
super(params
|
|
54
|
+
constructor(storage = new StorageMemory(), apiId = 0, apiHash = "", params) {
|
|
55
|
+
super(params);
|
|
56
56
|
Object.defineProperty(this, "storage", {
|
|
57
57
|
enumerable: true,
|
|
58
58
|
configurable: true,
|
|
@@ -107,12 +107,6 @@ export class Client extends ClientAbstract {
|
|
|
107
107
|
writable: true,
|
|
108
108
|
value: void 0
|
|
109
109
|
});
|
|
110
|
-
Object.defineProperty(this, "updateHandler", {
|
|
111
|
-
enumerable: true,
|
|
112
|
-
configurable: true,
|
|
113
|
-
writable: true,
|
|
114
|
-
value: null
|
|
115
|
-
});
|
|
116
110
|
Object.defineProperty(this, "parseMode", {
|
|
117
111
|
enumerable: true,
|
|
118
112
|
configurable: true,
|
|
@@ -167,12 +161,26 @@ export class Client extends ClientAbstract {
|
|
|
167
161
|
writable: true,
|
|
168
162
|
value: void 0
|
|
169
163
|
});
|
|
164
|
+
Object.defineProperty(this, "stateChangeHandler", {
|
|
165
|
+
enumerable: true,
|
|
166
|
+
configurable: true,
|
|
167
|
+
writable: true,
|
|
168
|
+
value: ((connected) => {
|
|
169
|
+
this.propagateConnectionState(connected ? "ready" : "not-connected");
|
|
170
|
+
}).bind(this)
|
|
171
|
+
});
|
|
170
172
|
Object.defineProperty(this, "storageInited", {
|
|
171
173
|
enumerable: true,
|
|
172
174
|
configurable: true,
|
|
173
175
|
writable: true,
|
|
174
176
|
value: false
|
|
175
177
|
});
|
|
178
|
+
Object.defineProperty(this, "connectMutex", {
|
|
179
|
+
enumerable: true,
|
|
180
|
+
configurable: true,
|
|
181
|
+
writable: true,
|
|
182
|
+
value: new Mutex()
|
|
183
|
+
});
|
|
176
184
|
Object.defineProperty(this, "connectionInited", {
|
|
177
185
|
enumerable: true,
|
|
178
186
|
configurable: true,
|
|
@@ -191,23 +199,25 @@ export class Client extends ClientAbstract {
|
|
|
191
199
|
writable: true,
|
|
192
200
|
value: 0n
|
|
193
201
|
});
|
|
194
|
-
Object.defineProperty(this, "
|
|
202
|
+
Object.defineProperty(this, "handleUpdateQueue", {
|
|
195
203
|
enumerable: true,
|
|
196
204
|
configurable: true,
|
|
197
205
|
writable: true,
|
|
198
|
-
value: new
|
|
206
|
+
value: new Queue()
|
|
199
207
|
});
|
|
200
|
-
Object.defineProperty(this, "
|
|
208
|
+
Object.defineProperty(this, "processUpdatesQueue", {
|
|
201
209
|
enumerable: true,
|
|
202
210
|
configurable: true,
|
|
203
211
|
writable: true,
|
|
204
|
-
value: new
|
|
212
|
+
value: new Queue()
|
|
205
213
|
});
|
|
206
|
-
Object.defineProperty(this, "
|
|
214
|
+
Object.defineProperty(this, "handler", {
|
|
207
215
|
enumerable: true,
|
|
208
216
|
configurable: true,
|
|
209
217
|
writable: true,
|
|
210
|
-
value:
|
|
218
|
+
value: (_upd, next) => {
|
|
219
|
+
next();
|
|
220
|
+
}
|
|
211
221
|
});
|
|
212
222
|
this.parseMode = params?.parseMode ?? ParseMode.None;
|
|
213
223
|
this.appVersion = params?.appVersion ?? APP_VERSION;
|
|
@@ -219,6 +229,9 @@ export class Client extends ClientAbstract {
|
|
|
219
229
|
this.publicKeys = params?.publicKeys;
|
|
220
230
|
this.autoStart = params?.autoStart ?? true;
|
|
221
231
|
}
|
|
232
|
+
propagateConnectionState(connectionState) {
|
|
233
|
+
return this.handler({ connectionState }, resolve);
|
|
234
|
+
}
|
|
222
235
|
/**
|
|
223
236
|
* Sets the DC and resets the auth key stored in the session provider
|
|
224
237
|
* if the stored DC was not the same as the `dc` parameter.
|
|
@@ -248,38 +261,47 @@ export class Client extends ClientAbstract {
|
|
|
248
261
|
* Before establishing the connection, the session is saved.
|
|
249
262
|
*/
|
|
250
263
|
async connect() {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
this.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
264
|
+
const release = await this.connectMutex.acquire();
|
|
265
|
+
try {
|
|
266
|
+
if (this.connected) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (!this.storageInited) {
|
|
270
|
+
await this.storage.init();
|
|
271
|
+
this.storageInited = true;
|
|
272
|
+
}
|
|
273
|
+
const authKey = await this.storage.getAuthKey();
|
|
274
|
+
if (authKey == null) {
|
|
275
|
+
const plain = new ClientPlain({ initialDc: this.initialDc, transportProvider: this.transportProvider, cdn: this.cdn, publicKeys: this.publicKeys });
|
|
276
|
+
const dc = await this.storage.getDc();
|
|
277
|
+
if (dc != null) {
|
|
278
|
+
plain.setDc(dc);
|
|
279
|
+
}
|
|
280
|
+
await plain.connect();
|
|
281
|
+
const { authKey, salt } = await plain.createAuthKey();
|
|
282
|
+
await plain.disconnect();
|
|
283
|
+
await this.storage.setAuthKey(authKey);
|
|
284
|
+
await this.setAuth(authKey);
|
|
285
|
+
this.state.salt = salt;
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
await this.setAuth(authKey);
|
|
289
|
+
}
|
|
258
290
|
const dc = await this.storage.getDc();
|
|
259
291
|
if (dc != null) {
|
|
260
|
-
|
|
292
|
+
await this.setDc(dc);
|
|
261
293
|
}
|
|
262
|
-
await
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
this.
|
|
268
|
-
|
|
269
|
-
else {
|
|
270
|
-
await this.setAuth(authKey);
|
|
271
|
-
}
|
|
272
|
-
const dc = await this.storage.getDc();
|
|
273
|
-
if (dc != null) {
|
|
274
|
-
await this.setDc(dc);
|
|
294
|
+
await super.connect();
|
|
295
|
+
if (dc == null) {
|
|
296
|
+
await this.storage.setDc(this.initialDc);
|
|
297
|
+
}
|
|
298
|
+
d("encrypted client connected");
|
|
299
|
+
drop(this.receiveLoop());
|
|
300
|
+
drop(this.pingLoop());
|
|
275
301
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
await this.storage.setDc(this.transportProvider.initialDc);
|
|
302
|
+
finally {
|
|
303
|
+
release();
|
|
279
304
|
}
|
|
280
|
-
d("encrypted client connected");
|
|
281
|
-
drop(this.receiveLoop());
|
|
282
|
-
drop(this.pingLoop());
|
|
283
305
|
}
|
|
284
306
|
async fetchState(source) {
|
|
285
307
|
const state = await this.invoke(new functions.UpdatesGetState());
|
|
@@ -353,7 +375,7 @@ export class Client extends ClientAbstract {
|
|
|
353
375
|
params = mustPrompt("Bot token:");
|
|
354
376
|
}
|
|
355
377
|
else {
|
|
356
|
-
params = { phone: () => mustPrompt("Phone number:"), code: () => mustPrompt("Verification code:"), password: () => mustPrompt(
|
|
378
|
+
params = { phone: () => mustPrompt("Phone number:"), code: () => mustPrompt("Verification code:"), password: () => mustPrompt("Password:") };
|
|
357
379
|
}
|
|
358
380
|
}
|
|
359
381
|
dAuth("authorizing with %s", typeof params === "string" ? "bot token" : params instanceof types.AuthExportedAuthorization ? "exported authorization" : "AuthorizeUserParams");
|
|
@@ -513,7 +535,7 @@ export class Client extends ClientAbstract {
|
|
|
513
535
|
await this.authorize(params);
|
|
514
536
|
}
|
|
515
537
|
async receiveLoop() {
|
|
516
|
-
if (!this.auth) {
|
|
538
|
+
if (!this.auth || !this.transport) {
|
|
517
539
|
throw new Error("Not connected");
|
|
518
540
|
}
|
|
519
541
|
while (this.connected) {
|
|
@@ -522,7 +544,7 @@ export class Client extends ClientAbstract {
|
|
|
522
544
|
await this.send(new types.MsgsAck({ msgIds: [...this.toAcknowledge] }));
|
|
523
545
|
this.toAcknowledge.clear();
|
|
524
546
|
}
|
|
525
|
-
const buffer = await this.transport.receive();
|
|
547
|
+
const buffer = await this.transport.transport.receive();
|
|
526
548
|
let decrypted;
|
|
527
549
|
try {
|
|
528
550
|
decrypted = await (decryptMessage(buffer, this.auth.key, this.auth.id, this.sessionId));
|
|
@@ -539,8 +561,8 @@ export class Client extends ClientAbstract {
|
|
|
539
561
|
body = new TLReader(gunzip(body.packedData)).readObject();
|
|
540
562
|
}
|
|
541
563
|
dRecv("received %s", body.constructor.name);
|
|
542
|
-
if (body instanceof types.
|
|
543
|
-
|
|
564
|
+
if (body instanceof types.TypeUpdates || body instanceof types.TypeUpdate) {
|
|
565
|
+
this.processUpdatesQueue.add(() => this.processUpdates(body));
|
|
544
566
|
}
|
|
545
567
|
else if (message.body instanceof RPCResult) {
|
|
546
568
|
let result = message.body.result;
|
|
@@ -567,7 +589,10 @@ export class Client extends ClientAbstract {
|
|
|
567
589
|
}
|
|
568
590
|
};
|
|
569
591
|
if (result instanceof types.TypeUpdates || result instanceof types.TypeUpdate) {
|
|
570
|
-
this.
|
|
592
|
+
this.processUpdatesQueue.add(async () => {
|
|
593
|
+
await this.processUpdates(result);
|
|
594
|
+
resolvePromise();
|
|
595
|
+
});
|
|
571
596
|
}
|
|
572
597
|
else {
|
|
573
598
|
await this.processResult(result);
|
|
@@ -619,7 +644,7 @@ export class Client extends ClientAbstract {
|
|
|
619
644
|
}
|
|
620
645
|
}
|
|
621
646
|
async invoke(function_, noWait) {
|
|
622
|
-
if (!this.auth) {
|
|
647
|
+
if (!this.auth || !this.transport) {
|
|
623
648
|
if (this.autoStart && !this.autoStarted) {
|
|
624
649
|
await this.start();
|
|
625
650
|
}
|
|
@@ -627,7 +652,7 @@ export class Client extends ClientAbstract {
|
|
|
627
652
|
throw new Error("Not connected");
|
|
628
653
|
}
|
|
629
654
|
}
|
|
630
|
-
if (!this.auth) {
|
|
655
|
+
if (!this.auth || !this.transport) {
|
|
631
656
|
UNREACHABLE();
|
|
632
657
|
}
|
|
633
658
|
let seqNo = this.state.seqNo * 2;
|
|
@@ -637,7 +662,7 @@ export class Client extends ClientAbstract {
|
|
|
637
662
|
}
|
|
638
663
|
const messageId = this.lastMsgId = getMessageId(this.lastMsgId);
|
|
639
664
|
const message = new Message_(messageId, seqNo, function_);
|
|
640
|
-
await this.transport.send(await encryptMessage(message, this.auth.key, this.auth.id, this.state.salt, this.sessionId));
|
|
665
|
+
await this.transport.transport.send(await encryptMessage(message, this.auth.key, this.auth.id, this.state.salt, this.sessionId));
|
|
641
666
|
d("invoked %s", function_.constructor.name);
|
|
642
667
|
if (noWait) {
|
|
643
668
|
return;
|
|
@@ -689,222 +714,160 @@ export class Client extends ClientAbstract {
|
|
|
689
714
|
}
|
|
690
715
|
}
|
|
691
716
|
}
|
|
692
|
-
async
|
|
693
|
-
const
|
|
694
|
-
|
|
695
|
-
if (
|
|
696
|
-
(
|
|
697
|
-
(update instanceof types.UpdateReadHistoryInbox) ||
|
|
698
|
-
(update instanceof types.UpdateReadHistoryOutbox) ||
|
|
699
|
-
(update instanceof types.UpdateWebPage) ||
|
|
700
|
-
(update instanceof types.UpdateReadMessagesContents) ||
|
|
701
|
-
(update instanceof types.UpdateEditMessage) ||
|
|
702
|
-
(update instanceof types.UpdateFolderPeers) ||
|
|
703
|
-
(update instanceof types.UpdatePinnedMessages) ||
|
|
704
|
-
(update instanceof types.UpdatePinnedChannelMessages) ||
|
|
705
|
-
(update instanceof types.UpdateShortMessage) ||
|
|
706
|
-
(update instanceof types.UpdateShortChatMessage) ||
|
|
707
|
-
(update instanceof types.UpdateShortSentMessage)) {
|
|
708
|
-
if (update.pts != 0 && update.ptsCount != 0) {
|
|
709
|
-
const localState = await this.getLocalState();
|
|
710
|
-
if (localState.pts + update.ptsCount > update.pts) {
|
|
711
|
-
// the update is already applied
|
|
712
|
-
return;
|
|
713
|
-
}
|
|
714
|
-
else if (localState.pts + update.ptsCount < update.pts) {
|
|
715
|
-
// there is an update gap that needs to be filled
|
|
716
|
-
throw UPDATE_GAP;
|
|
717
|
-
}
|
|
718
|
-
localState.pts = update.pts;
|
|
719
|
-
d("applied update with pts %d", update.pts);
|
|
720
|
-
await this.storage.setState(localState);
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
else if (usePts &&
|
|
724
|
-
((update instanceof types.UpdateNewChannelMessage) ||
|
|
725
|
-
(update instanceof types.UpdateDeleteChannelMessages) ||
|
|
726
|
-
(update instanceof types.UpdateEditChannelMessage) ||
|
|
727
|
-
(update instanceof types.UpdateChannelWebPage))) {
|
|
728
|
-
const channelId = update instanceof types.UpdateNewChannelMessage || update instanceof types.UpdateEditChannelMessage ? update.message.peerId[as](types.PeerChannel).channelId : update.channelId;
|
|
729
|
-
let localPts = await this.storage.getChannelPts(channelId);
|
|
730
|
-
if (!localPts) {
|
|
731
|
-
localPts = update.pts - update.ptsCount;
|
|
732
|
-
}
|
|
733
|
-
if (localPts + update.ptsCount > update.pts) {
|
|
734
|
-
// already applied
|
|
735
|
-
return;
|
|
736
|
-
}
|
|
737
|
-
else if (localPts + update.ptsCount < update.pts) {
|
|
738
|
-
// should call channelGetDifference
|
|
739
|
-
throw UPDATE_GAP;
|
|
740
|
-
}
|
|
741
|
-
d("applied update with pts %d", update.pts);
|
|
742
|
-
await this.storage.setChannelPts(channelId, update.pts);
|
|
717
|
+
async checkGap(pts, ptsCount, assertNoGap) {
|
|
718
|
+
const localState = await this.getLocalState();
|
|
719
|
+
if (localState.pts + ptsCount < pts) {
|
|
720
|
+
if (assertNoGap) {
|
|
721
|
+
UNREACHABLE();
|
|
743
722
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
await this.storage.setMessage(peerToChatId(update.message.peerId), update.message.id, update.message);
|
|
747
|
-
}
|
|
723
|
+
else {
|
|
724
|
+
await this.recoverUpdateGap("processUpdates");
|
|
748
725
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
async checkChannelGap(channelId, pts, ptsCount, assertNoGap) {
|
|
729
|
+
let localPts = await this.storage.getChannelPts(channelId);
|
|
730
|
+
if (!localPts) {
|
|
731
|
+
localPts = pts - ptsCount;
|
|
732
|
+
}
|
|
733
|
+
if (localPts + ptsCount < pts) {
|
|
734
|
+
if (assertNoGap) {
|
|
735
|
+
UNREACHABLE();
|
|
753
736
|
}
|
|
754
|
-
else
|
|
755
|
-
|
|
756
|
-
const chatId = await this.storage.getMessageChat(message);
|
|
757
|
-
if (chatId) {
|
|
758
|
-
await this.storage.setMessage(chatId, message, null);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
737
|
+
else {
|
|
738
|
+
await this.recoverChannelUpdateGap(channelId, "processUpdates");
|
|
761
739
|
}
|
|
762
|
-
// apply update (call listeners)
|
|
763
|
-
this.updateHandler?.(this, update);
|
|
764
|
-
}
|
|
765
|
-
finally {
|
|
766
|
-
release();
|
|
767
740
|
}
|
|
768
741
|
}
|
|
769
|
-
async
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
742
|
+
async processUpdates(updates_, assertNoGap = false) {
|
|
743
|
+
/// First, individual updates (Update[1]) and updateShort* are extracted from Updates.[2]
|
|
744
|
+
///
|
|
745
|
+
/// If an updatesTooLong[3] was received, an update gap recovery is initiated and no further action will be taken.
|
|
746
|
+
///
|
|
747
|
+
/// [1]: https://core.telegram.org/type/Update
|
|
748
|
+
/// [2]: https://core.telegram.org/type/Updates
|
|
749
|
+
/// [3]: https://core.telegram.org/constructor/updatesTooLong
|
|
750
|
+
let updates;
|
|
751
|
+
if (updates_ instanceof types.UpdatesCombined || updates_ instanceof types.Updates) {
|
|
752
|
+
updates = updates_.updates;
|
|
753
|
+
}
|
|
754
|
+
else if (updates_ instanceof types.UpdateShort) {
|
|
755
|
+
updates = [updates_.update];
|
|
756
|
+
}
|
|
757
|
+
else if (updates_ instanceof types.UpdateShortMessage ||
|
|
758
|
+
updates_ instanceof types.UpdateShortChatMessage ||
|
|
759
|
+
updates_ instanceof types.UpdateShortSentMessage) {
|
|
760
|
+
updates = [updates_];
|
|
761
|
+
}
|
|
762
|
+
else if (updates_ instanceof types.UpdatesTooLong) {
|
|
763
|
+
await this.recoverUpdateGap("updatesTooLong");
|
|
764
|
+
return;
|
|
776
765
|
}
|
|
777
|
-
if (
|
|
778
|
-
|
|
779
|
-
UNREACHABLE();
|
|
766
|
+
else if (updates_ instanceof types.TypeUpdate) {
|
|
767
|
+
updates = [updates_];
|
|
780
768
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
try {
|
|
784
|
-
await this.applyUpdateNoGap(update);
|
|
785
|
-
release();
|
|
769
|
+
else {
|
|
770
|
+
UNREACHABLE();
|
|
786
771
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
772
|
+
/// Then, we go through each Update and updateShort*, and see if they are order-sensitive.
|
|
773
|
+
/// If they were, we check the local state to see if it is OK to process them right away.
|
|
774
|
+
///
|
|
775
|
+
/// If we there was a gap, a recovery process will be initiated and the processing will be postponed.
|
|
776
|
+
let localState = null;
|
|
777
|
+
let originalPts = null;
|
|
778
|
+
const channelPtsMap = new Map();
|
|
779
|
+
for (const update of updates) {
|
|
780
|
+
if (hasPts(update)) {
|
|
781
|
+
if (update.pts == 0) {
|
|
782
|
+
continue;
|
|
796
783
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
(update instanceof types.UpdateReadMessagesContents) ||
|
|
803
|
-
(update instanceof types.UpdateEditMessage) ||
|
|
804
|
-
(update instanceof types.UpdateFolderPeers) ||
|
|
805
|
-
(update instanceof types.UpdatePinnedMessages) ||
|
|
806
|
-
(update instanceof types.UpdatePinnedChannelMessages) ||
|
|
807
|
-
(update instanceof types.UpdateShortMessage) ||
|
|
808
|
-
(update instanceof types.UpdateShortChatMessage) ||
|
|
809
|
-
(update instanceof types.UpdateShortSentMessage)) {
|
|
810
|
-
await this.recoverUpdateGap("applyUpdate");
|
|
784
|
+
await this.checkGap(update.pts, update.ptsCount, assertNoGap);
|
|
785
|
+
localState ??= await this.getLocalState();
|
|
786
|
+
originalPts ??= localState.pts;
|
|
787
|
+
if (localState.pts + update.ptsCount > update.pts) {
|
|
788
|
+
updates = updates.filter((v) => v != update);
|
|
811
789
|
}
|
|
812
790
|
else {
|
|
813
|
-
|
|
814
|
-
UNREACHABLE();
|
|
791
|
+
localState.pts = update.pts;
|
|
815
792
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
793
|
+
}
|
|
794
|
+
else if (hasChannelPts(update)) {
|
|
795
|
+
if (update.pts == 0) {
|
|
796
|
+
continue;
|
|
820
797
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
throw err;
|
|
828
|
-
}
|
|
798
|
+
const ptsCount = "ptsCount" in update ? update.ptsCount : 1;
|
|
799
|
+
const channelId = update instanceof types.UpdateNewChannelMessage || update instanceof types.UpdateEditChannelMessage ? update.message.peerId[as](types.PeerChannel).channelId : update.channelId;
|
|
800
|
+
await this.checkChannelGap(channelId, update.pts, ptsCount, assertNoGap);
|
|
801
|
+
let currentPts = channelPtsMap.get(channelId);
|
|
802
|
+
if (currentPts === undefined) {
|
|
803
|
+
currentPts = await this.storage.getChannelPts(channelId);
|
|
829
804
|
}
|
|
830
|
-
|
|
831
|
-
|
|
805
|
+
currentPts ??= update.pts - ptsCount;
|
|
806
|
+
if (currentPts + ptsCount > update.pts) {
|
|
807
|
+
updates = updates.filter((v) => v != update);
|
|
808
|
+
}
|
|
809
|
+
else {
|
|
810
|
+
channelPtsMap.set(channelId, update.pts);
|
|
832
811
|
}
|
|
833
812
|
}
|
|
834
|
-
|
|
835
|
-
|
|
813
|
+
}
|
|
814
|
+
if (!assertNoGap) {
|
|
815
|
+
if (localState != null && originalPts != null && localState.pts != originalPts) {
|
|
816
|
+
await this.storage.setState(localState);
|
|
817
|
+
}
|
|
818
|
+
for (const [channelId, pts] of channelPtsMap.entries()) {
|
|
819
|
+
await this.storage.setChannelPts(channelId, pts);
|
|
836
820
|
}
|
|
837
821
|
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
updates instanceof types.UpdateShortSentMessage) {
|
|
854
|
-
await this.setUpdateStateDate(updates.date);
|
|
855
|
-
await this.applyUpdate(updates);
|
|
856
|
-
}
|
|
857
|
-
else if (updates instanceof types.UpdatesTooLong) {
|
|
858
|
-
await this.recoverUpdateGap("updatesTooLong");
|
|
859
|
-
}
|
|
860
|
-
else if (updates instanceof types.UpdatesCombined) {
|
|
861
|
-
await this.setUpdateStateDate(updates.date);
|
|
862
|
-
await this.processChats(updates.chats);
|
|
863
|
-
await this.processUsers(updates.users);
|
|
864
|
-
for (const update of updates.updates) {
|
|
865
|
-
await this.processUpdates(update, release);
|
|
866
|
-
}
|
|
867
|
-
}
|
|
822
|
+
/// We process the updates when we are sure there is no gap.
|
|
823
|
+
if (updates_ instanceof types.Updates || updates_ instanceof types.UpdatesCombined) {
|
|
824
|
+
await this.processChats(updates_.chats);
|
|
825
|
+
await this.processUsers(updates_.users);
|
|
826
|
+
await this.setUpdateStateDate(updates_.date);
|
|
827
|
+
}
|
|
828
|
+
else if (updates_ instanceof types.UpdateShort) {
|
|
829
|
+
await this.setUpdateStateDate(updates_.date);
|
|
830
|
+
}
|
|
831
|
+
const updatesToHandle = new Array();
|
|
832
|
+
for (const update of updates) {
|
|
833
|
+
if (update instanceof types.UpdateShortMessage ||
|
|
834
|
+
update instanceof types.UpdateShortChatMessage ||
|
|
835
|
+
update instanceof types.UpdateShortSentMessage) {
|
|
836
|
+
await this.setUpdateStateDate(update.date);
|
|
868
837
|
}
|
|
869
|
-
else if (
|
|
870
|
-
if (
|
|
871
|
-
await this.storage.setChannelPts(
|
|
838
|
+
else if (update instanceof types.UpdateChannelTooLong) {
|
|
839
|
+
if (update.pts != undefined) {
|
|
840
|
+
await this.storage.setChannelPts(update.channelId, update.pts);
|
|
872
841
|
}
|
|
873
|
-
await this.recoverChannelUpdateGap(
|
|
842
|
+
await this.recoverChannelUpdateGap(update.channelId, "updateChannelTooLong");
|
|
874
843
|
}
|
|
875
|
-
else {
|
|
876
|
-
|
|
877
|
-
|
|
844
|
+
else if (update instanceof types.UpdateUserName) {
|
|
845
|
+
await this.storage.updateUsernames("user", update.userId, update.usernames.map((v) => v[as](types.Username)).map((v) => v.username));
|
|
846
|
+
}
|
|
847
|
+
else if (update instanceof types.UpdatePtsChanged) {
|
|
848
|
+
await this.fetchState("updatePtsChanged");
|
|
849
|
+
if (this.updateState) {
|
|
850
|
+
await this.storage.setState(this.updateState);
|
|
878
851
|
}
|
|
879
|
-
else
|
|
880
|
-
|
|
881
|
-
if (this.updateState) {
|
|
882
|
-
await this.storage.setState(this.updateState);
|
|
883
|
-
}
|
|
884
|
-
else {
|
|
885
|
-
UNREACHABLE();
|
|
886
|
-
}
|
|
852
|
+
else {
|
|
853
|
+
UNREACHABLE();
|
|
887
854
|
}
|
|
888
|
-
|
|
855
|
+
}
|
|
856
|
+
/// If there were any Update, they will be passed to the update handling queue.
|
|
857
|
+
if (update instanceof types.TypeUpdate) {
|
|
858
|
+
updatesToHandle.push(update);
|
|
889
859
|
}
|
|
890
860
|
}
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
}
|
|
861
|
+
this.handleUpdateQueue.add(async () => {
|
|
862
|
+
for (const update of updatesToHandle) {
|
|
863
|
+
await this.handleUpdate(update);
|
|
864
|
+
}
|
|
865
|
+
});
|
|
897
866
|
}
|
|
898
867
|
async setUpdateStateDate(date) {
|
|
899
|
-
const
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
localState.date = date;
|
|
903
|
-
await this.storage.setState(localState);
|
|
904
|
-
}
|
|
905
|
-
finally {
|
|
906
|
-
release();
|
|
907
|
-
}
|
|
868
|
+
const localState = await this.getLocalState();
|
|
869
|
+
localState.date = date;
|
|
870
|
+
await this.storage.setState(localState);
|
|
908
871
|
}
|
|
909
872
|
async getLocalState() {
|
|
910
873
|
let localState = await this.storage.getState();
|
|
@@ -928,7 +891,7 @@ export class Client extends ClientAbstract {
|
|
|
928
891
|
}
|
|
929
892
|
async recoverUpdateGap(source) {
|
|
930
893
|
dGap("recovering from update gap [%s]", source);
|
|
931
|
-
|
|
894
|
+
await this.propagateConnectionState("updating");
|
|
932
895
|
try {
|
|
933
896
|
let state = await this.getLocalState();
|
|
934
897
|
while (true) {
|
|
@@ -937,10 +900,10 @@ export class Client extends ClientAbstract {
|
|
|
937
900
|
await this.processChats(difference.chats);
|
|
938
901
|
await this.processUsers(difference.users);
|
|
939
902
|
for (const message of difference.newMessages) {
|
|
940
|
-
await this.
|
|
903
|
+
await this.processUpdates(new types.UpdateNewMessage({ message, pts: 0, ptsCount: 0 }), true);
|
|
941
904
|
}
|
|
942
905
|
for (const update of difference.otherUpdates) {
|
|
943
|
-
await this.
|
|
906
|
+
await this.processUpdates(update, true);
|
|
944
907
|
}
|
|
945
908
|
if (difference instanceof types.UpdatesDifference) {
|
|
946
909
|
await this.storage.setState(difference.state[as](types.UpdatesState));
|
|
@@ -955,6 +918,7 @@ export class Client extends ClientAbstract {
|
|
|
955
918
|
}
|
|
956
919
|
}
|
|
957
920
|
else if (difference instanceof types.UpdatesDifferenceTooLong) {
|
|
921
|
+
// TODO: we actually do now
|
|
958
922
|
// stored messages should be invalidated in case we store messages in the future
|
|
959
923
|
state.pts = difference.pts;
|
|
960
924
|
dGap("received differenceTooLong");
|
|
@@ -970,61 +934,55 @@ export class Client extends ClientAbstract {
|
|
|
970
934
|
}
|
|
971
935
|
}
|
|
972
936
|
finally {
|
|
973
|
-
|
|
937
|
+
this.stateChangeHandler(this.connected);
|
|
974
938
|
}
|
|
975
939
|
}
|
|
976
940
|
async recoverChannelUpdateGap(channelId, source) {
|
|
977
941
|
dGapC("recovering channel update gap [%o, %s]", channelId, source);
|
|
978
|
-
const
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
await this.
|
|
993
|
-
for (const message of difference.newMessages) {
|
|
994
|
-
await this.applyUpdateNoGap(new types.UpdateNewChannelMessage({ message, pts: 0, ptsCount: 0 }), false);
|
|
995
|
-
}
|
|
996
|
-
for (const update of difference.otherUpdates) {
|
|
997
|
-
await this.applyUpdateNoGap(update, false);
|
|
998
|
-
}
|
|
999
|
-
await this.storage.setChannelPts(channelId, difference.pts);
|
|
1000
|
-
dGapC("recovered from update gap [%o, %s]", channelId, source);
|
|
1001
|
-
break;
|
|
942
|
+
const pts_ = await this.storage.getChannelPts(channelId);
|
|
943
|
+
let pts = pts_ == null ? 1 : pts_;
|
|
944
|
+
while (true) {
|
|
945
|
+
const { accessHash } = await this.getInputPeer(ZERO_CHANNEL_ID + -Number(channelId)).then((v) => v[as](types.InputPeerChannel));
|
|
946
|
+
const difference = await this.invoke(new functions.UpdatesGetChannelDifference({
|
|
947
|
+
pts,
|
|
948
|
+
channel: new types.InputChannel({ channelId, accessHash: accessHash }),
|
|
949
|
+
filter: new types.ChannelMessagesFilterEmpty(),
|
|
950
|
+
limit: await this.storage.getAccountType() == "user" ? CHANNEL_DIFFERENCE_LIMIT_USER : CHANNEL_DIFFERENCE_LIMIT_BOT,
|
|
951
|
+
}));
|
|
952
|
+
if (difference instanceof types.UpdatesChannelDifference) {
|
|
953
|
+
await this.processChats(difference.chats);
|
|
954
|
+
await this.processUsers(difference.users);
|
|
955
|
+
for (const message of difference.newMessages) {
|
|
956
|
+
await this.processUpdates(new types.UpdateNewChannelMessage({ message, pts: 0, ptsCount: 0 }), true);
|
|
1002
957
|
}
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
dGapC("received channelDifferenceTooLong");
|
|
1006
|
-
await this.processChats(difference.chats);
|
|
1007
|
-
await this.processUsers(difference.users);
|
|
1008
|
-
for (const message of difference.messages) {
|
|
1009
|
-
await this.applyUpdateNoGap(new types.UpdateNewChannelMessage({ message, pts: 0, ptsCount: 0 }), false);
|
|
1010
|
-
}
|
|
1011
|
-
const pts_ = difference.dialog[as](types.Dialog).pts;
|
|
1012
|
-
if (pts_ != undefined) {
|
|
1013
|
-
pts = pts_;
|
|
1014
|
-
}
|
|
1015
|
-
else {
|
|
1016
|
-
UNREACHABLE();
|
|
1017
|
-
}
|
|
1018
|
-
dGapC("processed channelDifferenceTooLong");
|
|
958
|
+
for (const update of difference.otherUpdates) {
|
|
959
|
+
await this.processUpdates(update, true);
|
|
1019
960
|
}
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
961
|
+
await this.storage.setChannelPts(channelId, difference.pts);
|
|
962
|
+
dGapC("recovered from update gap [%o, %s]", channelId, source);
|
|
963
|
+
break;
|
|
964
|
+
}
|
|
965
|
+
else if (difference instanceof types.UpdatesChannelDifferenceTooLong) {
|
|
966
|
+
// invalidate messages
|
|
967
|
+
dGapC("received channelDifferenceTooLong");
|
|
968
|
+
await this.processChats(difference.chats);
|
|
969
|
+
await this.processUsers(difference.users);
|
|
970
|
+
for (const message of difference.messages) {
|
|
971
|
+
await this.processUpdates(new types.UpdateNewChannelMessage({ message, pts: 0, ptsCount: 0 }), true);
|
|
972
|
+
}
|
|
973
|
+
const pts_ = difference.dialog[as](types.Dialog).pts;
|
|
974
|
+
if (pts_ != undefined) {
|
|
975
|
+
pts = pts_;
|
|
976
|
+
}
|
|
977
|
+
else {
|
|
978
|
+
UNREACHABLE();
|
|
1023
979
|
}
|
|
980
|
+
dGapC("processed channelDifferenceTooLong");
|
|
981
|
+
}
|
|
982
|
+
else if (difference instanceof types.UpdatesChannelDifferenceEmpty) {
|
|
983
|
+
dGapC("there was no update gap");
|
|
984
|
+
break;
|
|
1024
985
|
}
|
|
1025
|
-
}
|
|
1026
|
-
finally {
|
|
1027
|
-
release();
|
|
1028
986
|
}
|
|
1029
987
|
}
|
|
1030
988
|
async getInputPeer(id) {
|
|
@@ -1341,7 +1299,8 @@ export class Client extends ClientAbstract {
|
|
|
1341
1299
|
langPack: this.langPack,
|
|
1342
1300
|
systemLangCode: this.systemLangCode,
|
|
1343
1301
|
systemVersion: this.systemVersion,
|
|
1344
|
-
|
|
1302
|
+
cdn: true,
|
|
1303
|
+
});
|
|
1345
1304
|
let dc = String(dcId);
|
|
1346
1305
|
if (this.dcId < 0) {
|
|
1347
1306
|
dc += "-test";
|
|
@@ -1452,4 +1411,68 @@ export class Client extends ClientAbstract {
|
|
|
1452
1411
|
}
|
|
1453
1412
|
return constructUser(users[0][as](types.User));
|
|
1454
1413
|
}
|
|
1414
|
+
async handleUpdate(update) {
|
|
1415
|
+
if (update instanceof types.UpdateNewMessage || update instanceof types.UpdateNewMessage || update instanceof types.UpdateNewChannelMessage || update instanceof types.UpdateNewChannelMessage) {
|
|
1416
|
+
if (update.message instanceof types.Message || update.message instanceof types.MessageService) {
|
|
1417
|
+
await this.storage.setMessage(peerToChatId(update.message.peerId), update.message.id, update.message);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
else if (update instanceof types.UpdateDeleteChannelMessages) {
|
|
1421
|
+
for (const message of update.messages) {
|
|
1422
|
+
await this.storage.setMessage(getChannelChatId(update.channelId), message, null);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
else if (update instanceof types.UpdateDeleteMessages) {
|
|
1426
|
+
for (const message of update.messages) {
|
|
1427
|
+
const chatId = await this.storage.getMessageChat(message);
|
|
1428
|
+
if (chatId) {
|
|
1429
|
+
await this.storage.setMessage(chatId, message, null);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
if (update instanceof types.UpdateNewMessage ||
|
|
1434
|
+
update instanceof types.UpdateNewChannelMessage ||
|
|
1435
|
+
update instanceof types.UpdateEditMessage ||
|
|
1436
|
+
update instanceof types.UpdateEditChannelMessage) {
|
|
1437
|
+
const key = update instanceof types.UpdateNewMessage || update instanceof types.UpdateNewChannelMessage ? "message" : "editedMessage";
|
|
1438
|
+
const message = await constructMessage(update.message, this[getEntity].bind(this), this.getMessage.bind(this), this[getStickerSetName].bind(this));
|
|
1439
|
+
await this.handler({ [key]: message }, resolve);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
use(middleware) {
|
|
1443
|
+
const handler = this.handler;
|
|
1444
|
+
this.handler = async (upd, next) => {
|
|
1445
|
+
let called = false;
|
|
1446
|
+
await handler(upd, async () => {
|
|
1447
|
+
if (called)
|
|
1448
|
+
return;
|
|
1449
|
+
called = true;
|
|
1450
|
+
await middleware(upd, next);
|
|
1451
|
+
});
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
on(filter, handler) {
|
|
1455
|
+
const type = typeof filter === "string" ? filter : filter[0];
|
|
1456
|
+
const keys = Array.isArray(filter) ? filter.slice(1) : [];
|
|
1457
|
+
this.use((update, next) => {
|
|
1458
|
+
if (type in update) {
|
|
1459
|
+
if (keys.length > 0) {
|
|
1460
|
+
for (const key of keys) {
|
|
1461
|
+
// deno-lint-ignore ban-ts-comment
|
|
1462
|
+
// @ts-ignore
|
|
1463
|
+
if (!(key in update[type])) {
|
|
1464
|
+
return next();
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
// deno-lint-ignore ban-ts-comment
|
|
1469
|
+
// @ts-ignore
|
|
1470
|
+
return handler(update, next);
|
|
1471
|
+
}
|
|
1472
|
+
else {
|
|
1473
|
+
return next();
|
|
1474
|
+
}
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1455
1477
|
}
|
|
1478
|
+
const resolve = () => Promise.resolve();
|