@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.
- 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 +30 -19
- package/esm/client/3_client.js +319 -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/tl/0_tl_raw_reader.d.ts +2 -0
- package/esm/tl/0_tl_raw_reader.js +3 -1
- package/esm/tl/3_tl_reader.js +2 -2
- 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 +30 -19
- package/script/client/3_client.js +318 -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/tl/0_tl_raw_reader.d.ts +2 -0
- package/script/tl/0_tl_raw_reader.js +5 -2
- package/script/tl/3_tl_reader.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,14 +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
|
+
import { TLError } from "../tl/0_tl_raw_reader.js";
|
|
30
|
+
import { Queue } from "../utilities/0_queue.js";
|
|
29
31
|
const d = debug("Client");
|
|
30
32
|
const dGap = debug("Client/recoverUpdateGap");
|
|
31
33
|
const dGapC = debug("Client/recoverChannelUpdateGap");
|
|
32
34
|
const dAuth = debug("Client/authorize");
|
|
33
35
|
const dRecv = debug("Client/receiveLoop");
|
|
34
|
-
const UPDATE_GAP = Symbol();
|
|
35
36
|
export const getEntity = Symbol();
|
|
36
37
|
export const getStickerSetName = Symbol();
|
|
37
38
|
export const handleMigrationError = Symbol();
|
|
@@ -50,8 +51,8 @@ export class Client extends ClientAbstract {
|
|
|
50
51
|
* @param apiHash App's API hash from [my.telegram.org/apps](https://my.telegram.org/apps). Defaults to empty string (unset).
|
|
51
52
|
* @param params Other parameters.
|
|
52
53
|
*/
|
|
53
|
-
constructor(storage = new StorageMemory(), apiId = 0, apiHash = "", params
|
|
54
|
-
super(params
|
|
54
|
+
constructor(storage = new StorageMemory(), apiId = 0, apiHash = "", params) {
|
|
55
|
+
super(params);
|
|
55
56
|
Object.defineProperty(this, "storage", {
|
|
56
57
|
enumerable: true,
|
|
57
58
|
configurable: true,
|
|
@@ -106,12 +107,6 @@ export class Client extends ClientAbstract {
|
|
|
106
107
|
writable: true,
|
|
107
108
|
value: void 0
|
|
108
109
|
});
|
|
109
|
-
Object.defineProperty(this, "updateHandler", {
|
|
110
|
-
enumerable: true,
|
|
111
|
-
configurable: true,
|
|
112
|
-
writable: true,
|
|
113
|
-
value: null
|
|
114
|
-
});
|
|
115
110
|
Object.defineProperty(this, "parseMode", {
|
|
116
111
|
enumerable: true,
|
|
117
112
|
configurable: true,
|
|
@@ -166,12 +161,26 @@ export class Client extends ClientAbstract {
|
|
|
166
161
|
writable: true,
|
|
167
162
|
value: void 0
|
|
168
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
|
+
});
|
|
169
172
|
Object.defineProperty(this, "storageInited", {
|
|
170
173
|
enumerable: true,
|
|
171
174
|
configurable: true,
|
|
172
175
|
writable: true,
|
|
173
176
|
value: false
|
|
174
177
|
});
|
|
178
|
+
Object.defineProperty(this, "connectMutex", {
|
|
179
|
+
enumerable: true,
|
|
180
|
+
configurable: true,
|
|
181
|
+
writable: true,
|
|
182
|
+
value: new Mutex()
|
|
183
|
+
});
|
|
175
184
|
Object.defineProperty(this, "connectionInited", {
|
|
176
185
|
enumerable: true,
|
|
177
186
|
configurable: true,
|
|
@@ -190,23 +199,25 @@ export class Client extends ClientAbstract {
|
|
|
190
199
|
writable: true,
|
|
191
200
|
value: 0n
|
|
192
201
|
});
|
|
193
|
-
Object.defineProperty(this, "
|
|
202
|
+
Object.defineProperty(this, "handleUpdateQueue", {
|
|
194
203
|
enumerable: true,
|
|
195
204
|
configurable: true,
|
|
196
205
|
writable: true,
|
|
197
|
-
value: new
|
|
206
|
+
value: new Queue()
|
|
198
207
|
});
|
|
199
|
-
Object.defineProperty(this, "
|
|
208
|
+
Object.defineProperty(this, "processUpdatesQueue", {
|
|
200
209
|
enumerable: true,
|
|
201
210
|
configurable: true,
|
|
202
211
|
writable: true,
|
|
203
|
-
value: new
|
|
212
|
+
value: new Queue()
|
|
204
213
|
});
|
|
205
|
-
Object.defineProperty(this, "
|
|
214
|
+
Object.defineProperty(this, "handler", {
|
|
206
215
|
enumerable: true,
|
|
207
216
|
configurable: true,
|
|
208
217
|
writable: true,
|
|
209
|
-
value:
|
|
218
|
+
value: (_upd, next) => {
|
|
219
|
+
next();
|
|
220
|
+
}
|
|
210
221
|
});
|
|
211
222
|
this.parseMode = params?.parseMode ?? ParseMode.None;
|
|
212
223
|
this.appVersion = params?.appVersion ?? APP_VERSION;
|
|
@@ -218,6 +229,9 @@ export class Client extends ClientAbstract {
|
|
|
218
229
|
this.publicKeys = params?.publicKeys;
|
|
219
230
|
this.autoStart = params?.autoStart ?? true;
|
|
220
231
|
}
|
|
232
|
+
propagateConnectionState(connectionState) {
|
|
233
|
+
return this.handler({ connectionState }, resolve);
|
|
234
|
+
}
|
|
221
235
|
/**
|
|
222
236
|
* Sets the DC and resets the auth key stored in the session provider
|
|
223
237
|
* if the stored DC was not the same as the `dc` parameter.
|
|
@@ -247,38 +261,47 @@ export class Client extends ClientAbstract {
|
|
|
247
261
|
* Before establishing the connection, the session is saved.
|
|
248
262
|
*/
|
|
249
263
|
async connect() {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
this.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
+
}
|
|
257
290
|
const dc = await this.storage.getDc();
|
|
258
291
|
if (dc != null) {
|
|
259
|
-
|
|
292
|
+
await this.setDc(dc);
|
|
260
293
|
}
|
|
261
|
-
await
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
this.
|
|
267
|
-
|
|
268
|
-
else {
|
|
269
|
-
await this.setAuth(authKey);
|
|
270
|
-
}
|
|
271
|
-
const dc = await this.storage.getDc();
|
|
272
|
-
if (dc != null) {
|
|
273
|
-
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());
|
|
274
301
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
await this.storage.setDc(this.transportProvider.initialDc);
|
|
302
|
+
finally {
|
|
303
|
+
release();
|
|
278
304
|
}
|
|
279
|
-
d("encrypted client connected");
|
|
280
|
-
drop(this.receiveLoop());
|
|
281
|
-
drop(this.pingLoop());
|
|
282
305
|
}
|
|
283
306
|
async fetchState(source) {
|
|
284
307
|
const state = await this.invoke(new functions.UpdatesGetState());
|
|
@@ -352,7 +375,7 @@ export class Client extends ClientAbstract {
|
|
|
352
375
|
params = mustPrompt("Bot token:");
|
|
353
376
|
}
|
|
354
377
|
else {
|
|
355
|
-
params = { phone: () => mustPrompt("Phone number:"), code: () => mustPrompt("Verification code:"), password: () => mustPrompt(
|
|
378
|
+
params = { phone: () => mustPrompt("Phone number:"), code: () => mustPrompt("Verification code:"), password: () => mustPrompt("Password:") };
|
|
356
379
|
}
|
|
357
380
|
}
|
|
358
381
|
dAuth("authorizing with %s", typeof params === "string" ? "bot token" : params instanceof types.AuthExportedAuthorization ? "exported authorization" : "AuthorizeUserParams");
|
|
@@ -512,7 +535,7 @@ export class Client extends ClientAbstract {
|
|
|
512
535
|
await this.authorize(params);
|
|
513
536
|
}
|
|
514
537
|
async receiveLoop() {
|
|
515
|
-
if (!this.auth) {
|
|
538
|
+
if (!this.auth || !this.transport) {
|
|
516
539
|
throw new Error("Not connected");
|
|
517
540
|
}
|
|
518
541
|
while (this.connected) {
|
|
@@ -521,7 +544,7 @@ export class Client extends ClientAbstract {
|
|
|
521
544
|
await this.send(new types.MsgsAck({ msgIds: [...this.toAcknowledge] }));
|
|
522
545
|
this.toAcknowledge.clear();
|
|
523
546
|
}
|
|
524
|
-
const buffer = await this.transport.receive();
|
|
547
|
+
const buffer = await this.transport.transport.receive();
|
|
525
548
|
let decrypted;
|
|
526
549
|
try {
|
|
527
550
|
decrypted = await (decryptMessage(buffer, this.auth.key, this.auth.id, this.sessionId));
|
|
@@ -538,8 +561,8 @@ export class Client extends ClientAbstract {
|
|
|
538
561
|
body = new TLReader(gunzip(body.packedData)).readObject();
|
|
539
562
|
}
|
|
540
563
|
dRecv("received %s", body.constructor.name);
|
|
541
|
-
if (body instanceof types.
|
|
542
|
-
|
|
564
|
+
if (body instanceof types.TypeUpdates || body instanceof types.TypeUpdate) {
|
|
565
|
+
this.processUpdatesQueue.add(() => this.processUpdates(body));
|
|
543
566
|
}
|
|
544
567
|
else if (message.body instanceof RPCResult) {
|
|
545
568
|
let result = message.body.result;
|
|
@@ -566,7 +589,10 @@ export class Client extends ClientAbstract {
|
|
|
566
589
|
}
|
|
567
590
|
};
|
|
568
591
|
if (result instanceof types.TypeUpdates || result instanceof types.TypeUpdate) {
|
|
569
|
-
this.
|
|
592
|
+
this.processUpdatesQueue.add(async () => {
|
|
593
|
+
await this.processUpdates(result);
|
|
594
|
+
resolvePromise();
|
|
595
|
+
});
|
|
570
596
|
}
|
|
571
597
|
else {
|
|
572
598
|
await this.processResult(result);
|
|
@@ -596,6 +622,10 @@ export class Client extends ClientAbstract {
|
|
|
596
622
|
if (!this.connected) {
|
|
597
623
|
break;
|
|
598
624
|
}
|
|
625
|
+
else if (err instanceof TLError) {
|
|
626
|
+
dRecv("failed to deserialize: %o", err);
|
|
627
|
+
drop(this.recoverUpdateGap("deserialize"));
|
|
628
|
+
}
|
|
599
629
|
else {
|
|
600
630
|
throw err;
|
|
601
631
|
}
|
|
@@ -614,7 +644,7 @@ export class Client extends ClientAbstract {
|
|
|
614
644
|
}
|
|
615
645
|
}
|
|
616
646
|
async invoke(function_, noWait) {
|
|
617
|
-
if (!this.auth) {
|
|
647
|
+
if (!this.auth || !this.transport) {
|
|
618
648
|
if (this.autoStart && !this.autoStarted) {
|
|
619
649
|
await this.start();
|
|
620
650
|
}
|
|
@@ -622,7 +652,7 @@ export class Client extends ClientAbstract {
|
|
|
622
652
|
throw new Error("Not connected");
|
|
623
653
|
}
|
|
624
654
|
}
|
|
625
|
-
if (!this.auth) {
|
|
655
|
+
if (!this.auth || !this.transport) {
|
|
626
656
|
UNREACHABLE();
|
|
627
657
|
}
|
|
628
658
|
let seqNo = this.state.seqNo * 2;
|
|
@@ -632,7 +662,7 @@ export class Client extends ClientAbstract {
|
|
|
632
662
|
}
|
|
633
663
|
const messageId = this.lastMsgId = getMessageId(this.lastMsgId);
|
|
634
664
|
const message = new Message_(messageId, seqNo, function_);
|
|
635
|
-
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));
|
|
636
666
|
d("invoked %s", function_.constructor.name);
|
|
637
667
|
if (noWait) {
|
|
638
668
|
return;
|
|
@@ -684,222 +714,160 @@ export class Client extends ClientAbstract {
|
|
|
684
714
|
}
|
|
685
715
|
}
|
|
686
716
|
}
|
|
687
|
-
async
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
if (
|
|
691
|
-
(
|
|
692
|
-
(update instanceof types.UpdateReadHistoryInbox) ||
|
|
693
|
-
(update instanceof types.UpdateReadHistoryOutbox) ||
|
|
694
|
-
(update instanceof types.UpdateWebPage) ||
|
|
695
|
-
(update instanceof types.UpdateReadMessagesContents) ||
|
|
696
|
-
(update instanceof types.UpdateEditMessage) ||
|
|
697
|
-
(update instanceof types.UpdateFolderPeers) ||
|
|
698
|
-
(update instanceof types.UpdatePinnedMessages) ||
|
|
699
|
-
(update instanceof types.UpdatePinnedChannelMessages) ||
|
|
700
|
-
(update instanceof types.UpdateShortMessage) ||
|
|
701
|
-
(update instanceof types.UpdateShortChatMessage) ||
|
|
702
|
-
(update instanceof types.UpdateShortSentMessage)) {
|
|
703
|
-
if (update.pts != 0 && update.ptsCount != 0) {
|
|
704
|
-
const localState = await this.getLocalState();
|
|
705
|
-
if (localState.pts + update.ptsCount > update.pts) {
|
|
706
|
-
// the update is already applied
|
|
707
|
-
return;
|
|
708
|
-
}
|
|
709
|
-
else if (localState.pts + update.ptsCount < update.pts) {
|
|
710
|
-
// there is an update gap that needs to be filled
|
|
711
|
-
throw UPDATE_GAP;
|
|
712
|
-
}
|
|
713
|
-
localState.pts = update.pts;
|
|
714
|
-
d("applied update with pts %d", update.pts);
|
|
715
|
-
await this.storage.setState(localState);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
else if (usePts &&
|
|
719
|
-
((update instanceof types.UpdateNewChannelMessage) ||
|
|
720
|
-
(update instanceof types.UpdateDeleteChannelMessages) ||
|
|
721
|
-
(update instanceof types.UpdateEditChannelMessage) ||
|
|
722
|
-
(update instanceof types.UpdateChannelWebPage))) {
|
|
723
|
-
const channelId = update instanceof types.UpdateNewChannelMessage || update instanceof types.UpdateEditChannelMessage ? update.message.peerId[as](types.PeerChannel).channelId : update.channelId;
|
|
724
|
-
let localPts = await this.storage.getChannelPts(channelId);
|
|
725
|
-
if (!localPts) {
|
|
726
|
-
localPts = update.pts - update.ptsCount;
|
|
727
|
-
}
|
|
728
|
-
if (localPts + update.ptsCount > update.pts) {
|
|
729
|
-
// already applied
|
|
730
|
-
return;
|
|
731
|
-
}
|
|
732
|
-
else if (localPts + update.ptsCount < update.pts) {
|
|
733
|
-
// should call channelGetDifference
|
|
734
|
-
throw UPDATE_GAP;
|
|
735
|
-
}
|
|
736
|
-
d("applied update with pts %d", update.pts);
|
|
737
|
-
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();
|
|
738
722
|
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
await this.storage.setMessage(peerToChatId(update.message.peerId), update.message.id, update.message);
|
|
742
|
-
}
|
|
723
|
+
else {
|
|
724
|
+
await this.recoverUpdateGap("processUpdates");
|
|
743
725
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
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();
|
|
748
736
|
}
|
|
749
|
-
else
|
|
750
|
-
|
|
751
|
-
const chatId = await this.storage.getMessageChat(message);
|
|
752
|
-
if (chatId) {
|
|
753
|
-
await this.storage.setMessage(chatId, message, null);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
737
|
+
else {
|
|
738
|
+
await this.recoverChannelUpdateGap(channelId, "processUpdates");
|
|
756
739
|
}
|
|
757
|
-
// apply update (call listeners)
|
|
758
|
-
this.updateHandler?.(this, update);
|
|
759
|
-
}
|
|
760
|
-
finally {
|
|
761
|
-
release();
|
|
762
740
|
}
|
|
763
741
|
}
|
|
764
|
-
async
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
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;
|
|
771
765
|
}
|
|
772
|
-
if (
|
|
773
|
-
|
|
774
|
-
UNREACHABLE();
|
|
766
|
+
else if (updates_ instanceof types.TypeUpdate) {
|
|
767
|
+
updates = [updates_];
|
|
775
768
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
try {
|
|
779
|
-
await this.applyUpdateNoGap(update);
|
|
780
|
-
release();
|
|
769
|
+
else {
|
|
770
|
+
UNREACHABLE();
|
|
781
771
|
}
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
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;
|
|
791
783
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
(update instanceof types.UpdateReadMessagesContents) ||
|
|
798
|
-
(update instanceof types.UpdateEditMessage) ||
|
|
799
|
-
(update instanceof types.UpdateFolderPeers) ||
|
|
800
|
-
(update instanceof types.UpdatePinnedMessages) ||
|
|
801
|
-
(update instanceof types.UpdatePinnedChannelMessages) ||
|
|
802
|
-
(update instanceof types.UpdateShortMessage) ||
|
|
803
|
-
(update instanceof types.UpdateShortChatMessage) ||
|
|
804
|
-
(update instanceof types.UpdateShortSentMessage)) {
|
|
805
|
-
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);
|
|
806
789
|
}
|
|
807
790
|
else {
|
|
808
|
-
|
|
809
|
-
UNREACHABLE();
|
|
791
|
+
localState.pts = update.pts;
|
|
810
792
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
793
|
+
}
|
|
794
|
+
else if (hasChannelPts(update)) {
|
|
795
|
+
if (update.pts == 0) {
|
|
796
|
+
continue;
|
|
815
797
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
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);
|
|
804
|
+
}
|
|
805
|
+
currentPts ??= update.pts;
|
|
806
|
+
if (currentPts + ptsCount > update.pts) {
|
|
807
|
+
updates = updates.filter((v) => v != update);
|
|
824
808
|
}
|
|
825
|
-
|
|
826
|
-
|
|
809
|
+
else {
|
|
810
|
+
channelPtsMap.set(channelId, update.pts);
|
|
827
811
|
}
|
|
828
812
|
}
|
|
829
|
-
|
|
830
|
-
|
|
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);
|
|
831
820
|
}
|
|
832
821
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
updates instanceof types.UpdateShortSentMessage) {
|
|
849
|
-
await this.setUpdateStateDate(updates.date);
|
|
850
|
-
await this.applyUpdate(updates);
|
|
851
|
-
}
|
|
852
|
-
else if (updates instanceof types.UpdatesTooLong) {
|
|
853
|
-
await this.recoverUpdateGap("updatesTooLong");
|
|
854
|
-
}
|
|
855
|
-
else if (updates instanceof types.UpdatesCombined) {
|
|
856
|
-
await this.setUpdateStateDate(updates.date);
|
|
857
|
-
await this.processChats(updates.chats);
|
|
858
|
-
await this.processUsers(updates.users);
|
|
859
|
-
for (const update of updates.updates) {
|
|
860
|
-
await this.processUpdates(update, release);
|
|
861
|
-
}
|
|
862
|
-
}
|
|
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);
|
|
863
837
|
}
|
|
864
|
-
else if (
|
|
865
|
-
if (
|
|
866
|
-
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);
|
|
867
841
|
}
|
|
868
|
-
await this.recoverChannelUpdateGap(
|
|
842
|
+
await this.recoverChannelUpdateGap(update.channelId, "updateChannelTooLong");
|
|
869
843
|
}
|
|
870
|
-
else {
|
|
871
|
-
|
|
872
|
-
|
|
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);
|
|
873
851
|
}
|
|
874
|
-
else
|
|
875
|
-
|
|
876
|
-
if (this.updateState) {
|
|
877
|
-
await this.storage.setState(this.updateState);
|
|
878
|
-
}
|
|
879
|
-
else {
|
|
880
|
-
UNREACHABLE();
|
|
881
|
-
}
|
|
852
|
+
else {
|
|
853
|
+
UNREACHABLE();
|
|
882
854
|
}
|
|
883
|
-
|
|
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);
|
|
884
859
|
}
|
|
885
860
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
}
|
|
861
|
+
this.handleUpdateQueue.add(async () => {
|
|
862
|
+
for (const update of updatesToHandle) {
|
|
863
|
+
await this.handleUpdate(update);
|
|
864
|
+
}
|
|
865
|
+
});
|
|
892
866
|
}
|
|
893
867
|
async setUpdateStateDate(date) {
|
|
894
|
-
const
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
localState.date = date;
|
|
898
|
-
await this.storage.setState(localState);
|
|
899
|
-
}
|
|
900
|
-
finally {
|
|
901
|
-
release();
|
|
902
|
-
}
|
|
868
|
+
const localState = await this.getLocalState();
|
|
869
|
+
localState.date = date;
|
|
870
|
+
await this.storage.setState(localState);
|
|
903
871
|
}
|
|
904
872
|
async getLocalState() {
|
|
905
873
|
let localState = await this.storage.getState();
|
|
@@ -923,7 +891,7 @@ export class Client extends ClientAbstract {
|
|
|
923
891
|
}
|
|
924
892
|
async recoverUpdateGap(source) {
|
|
925
893
|
dGap("recovering from update gap [%s]", source);
|
|
926
|
-
|
|
894
|
+
await this.propagateConnectionState("updating");
|
|
927
895
|
try {
|
|
928
896
|
let state = await this.getLocalState();
|
|
929
897
|
while (true) {
|
|
@@ -932,10 +900,10 @@ export class Client extends ClientAbstract {
|
|
|
932
900
|
await this.processChats(difference.chats);
|
|
933
901
|
await this.processUsers(difference.users);
|
|
934
902
|
for (const message of difference.newMessages) {
|
|
935
|
-
await this.
|
|
903
|
+
await this.processUpdates(new types.UpdateNewMessage({ message, pts: 0, ptsCount: 0 }), true);
|
|
936
904
|
}
|
|
937
905
|
for (const update of difference.otherUpdates) {
|
|
938
|
-
await this.
|
|
906
|
+
await this.processUpdates(update, true);
|
|
939
907
|
}
|
|
940
908
|
if (difference instanceof types.UpdatesDifference) {
|
|
941
909
|
await this.storage.setState(difference.state[as](types.UpdatesState));
|
|
@@ -950,6 +918,7 @@ export class Client extends ClientAbstract {
|
|
|
950
918
|
}
|
|
951
919
|
}
|
|
952
920
|
else if (difference instanceof types.UpdatesDifferenceTooLong) {
|
|
921
|
+
// TODO: we actually do now
|
|
953
922
|
// stored messages should be invalidated in case we store messages in the future
|
|
954
923
|
state.pts = difference.pts;
|
|
955
924
|
dGap("received differenceTooLong");
|
|
@@ -965,61 +934,55 @@ export class Client extends ClientAbstract {
|
|
|
965
934
|
}
|
|
966
935
|
}
|
|
967
936
|
finally {
|
|
968
|
-
|
|
937
|
+
this.stateChangeHandler(this.connected);
|
|
969
938
|
}
|
|
970
939
|
}
|
|
971
940
|
async recoverChannelUpdateGap(channelId, source) {
|
|
972
941
|
dGapC("recovering channel update gap [%o, %s]", channelId, source);
|
|
973
|
-
const
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
await this.
|
|
988
|
-
for (const message of difference.newMessages) {
|
|
989
|
-
await this.applyUpdateNoGap(new types.UpdateNewChannelMessage({ message, pts: 0, ptsCount: 0 }), false);
|
|
990
|
-
}
|
|
991
|
-
for (const update of difference.otherUpdates) {
|
|
992
|
-
await this.applyUpdateNoGap(update, false);
|
|
993
|
-
}
|
|
994
|
-
await this.storage.setChannelPts(channelId, difference.pts);
|
|
995
|
-
dGapC("recovered from update gap [%o, %s]", channelId, source);
|
|
996
|
-
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);
|
|
997
957
|
}
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
dGapC("received channelDifferenceTooLong");
|
|
1001
|
-
await this.processChats(difference.chats);
|
|
1002
|
-
await this.processUsers(difference.users);
|
|
1003
|
-
for (const message of difference.messages) {
|
|
1004
|
-
await this.applyUpdateNoGap(new types.UpdateNewChannelMessage({ message, pts: 0, ptsCount: 0 }), false);
|
|
1005
|
-
}
|
|
1006
|
-
const pts_ = difference.dialog[as](types.Dialog).pts;
|
|
1007
|
-
if (pts_ != undefined) {
|
|
1008
|
-
pts = pts_;
|
|
1009
|
-
}
|
|
1010
|
-
else {
|
|
1011
|
-
UNREACHABLE();
|
|
1012
|
-
}
|
|
1013
|
-
dGapC("processed channelDifferenceTooLong");
|
|
958
|
+
for (const update of difference.otherUpdates) {
|
|
959
|
+
await this.processUpdates(update, true);
|
|
1014
960
|
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
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_;
|
|
1018
976
|
}
|
|
977
|
+
else {
|
|
978
|
+
UNREACHABLE();
|
|
979
|
+
}
|
|
980
|
+
dGapC("processed channelDifferenceTooLong");
|
|
981
|
+
}
|
|
982
|
+
else if (difference instanceof types.UpdatesChannelDifferenceEmpty) {
|
|
983
|
+
dGapC("there was no update gap");
|
|
984
|
+
break;
|
|
1019
985
|
}
|
|
1020
|
-
}
|
|
1021
|
-
finally {
|
|
1022
|
-
release();
|
|
1023
986
|
}
|
|
1024
987
|
}
|
|
1025
988
|
async getInputPeer(id) {
|
|
@@ -1336,7 +1299,8 @@ export class Client extends ClientAbstract {
|
|
|
1336
1299
|
langPack: this.langPack,
|
|
1337
1300
|
systemLangCode: this.systemLangCode,
|
|
1338
1301
|
systemVersion: this.systemVersion,
|
|
1339
|
-
|
|
1302
|
+
cdn: true,
|
|
1303
|
+
});
|
|
1340
1304
|
let dc = String(dcId);
|
|
1341
1305
|
if (this.dcId < 0) {
|
|
1342
1306
|
dc += "-test";
|
|
@@ -1447,4 +1411,68 @@ export class Client extends ClientAbstract {
|
|
|
1447
1411
|
}
|
|
1448
1412
|
return constructUser(users[0][as](types.User));
|
|
1449
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 middleware(upd, async () => {
|
|
1447
|
+
if (called)
|
|
1448
|
+
return;
|
|
1449
|
+
called = true;
|
|
1450
|
+
await handler(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
|
+
}
|
|
1450
1477
|
}
|
|
1478
|
+
const resolve = () => Promise.resolve();
|