@mtkruto/node 0.0.835 → 0.0.901
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/client.d.ts +23 -3
- package/esm/client/client.js +497 -76
- package/esm/client/client_abstract.d.ts +2 -0
- package/esm/client/client_abstract.js +14 -3
- package/esm/client/client_plain.js +18 -4
- package/esm/constants.d.ts +7 -5
- package/esm/constants.js +10 -6
- package/esm/deps/deno.land/std@0.190.0/encoding/base64.d.ts +11 -0
- package/esm/deps/deno.land/std@0.190.0/encoding/base64.js +140 -0
- package/esm/deps/deno.land/x/q@v0.0.1/mod.d.ts +6 -0
- package/esm/deps/deno.land/x/q@v0.0.1/mod.js +71 -0
- package/esm/deps.d.ts +2 -0
- package/esm/deps.js +2 -0
- package/esm/mod.d.ts +3 -3
- package/esm/mod.js +3 -3
- package/esm/storage/storage.d.ts +18 -0
- package/esm/storage/storage.js +103 -0
- package/esm/tl/1_tl_object.d.ts +0 -1
- package/esm/tl/1_tl_object.js +1 -1
- package/esm/tl/4_tl_writer.d.ts +5 -0
- package/esm/tl/5_rpc_result.d.ts +9 -0
- package/esm/tl/6_message.d.ts +10 -0
- package/esm/tl/{5_message.js → 6_message.js} +2 -2
- package/esm/tl/{6_message_container.d.ts → 7_message_container.d.ts} +1 -1
- package/esm/tl/{6_message_container.js → 7_message_container.js} +2 -2
- package/esm/transport/transport_provider.d.ts +7 -4
- package/esm/transport/transport_provider.js +11 -8
- package/esm/types/3_message.d.ts +5 -1
- package/esm/types/3_message.js +26 -22
- package/esm/utilities/1_message.d.ts +2 -2
- package/esm/utilities/1_message.js +3 -3
- package/esm/utilities/1_password.js +1 -1
- package/package.json +1 -1
- package/script/client/client.d.ts +23 -3
- package/script/client/client.js +497 -76
- package/script/client/client_abstract.d.ts +2 -0
- package/script/client/client_abstract.js +14 -3
- package/script/client/client_plain.js +18 -4
- package/script/constants.d.ts +7 -5
- package/script/constants.js +34 -7
- package/script/deps/deno.land/std@0.190.0/encoding/base64.d.ts +11 -0
- package/script/deps/deno.land/std@0.190.0/encoding/base64.js +145 -0
- package/script/deps/deno.land/x/q@v0.0.1/mod.d.ts +6 -0
- package/script/deps/deno.land/x/q@v0.0.1/mod.js +75 -0
- package/script/deps.d.ts +2 -0
- package/script/deps.js +6 -1
- package/script/mod.d.ts +3 -3
- package/script/mod.js +3 -3
- package/script/storage/storage.d.ts +18 -0
- package/script/storage/storage.js +126 -0
- package/script/tl/1_tl_object.d.ts +0 -1
- package/script/tl/1_tl_object.js +1 -1
- package/script/tl/4_tl_writer.d.ts +5 -0
- package/script/tl/5_rpc_result.d.ts +9 -0
- package/script/tl/6_message.d.ts +10 -0
- package/script/tl/{5_message.js → 6_message.js} +6 -6
- package/script/tl/{6_message_container.d.ts → 7_message_container.d.ts} +1 -1
- package/script/tl/{6_message_container.js → 7_message_container.js} +4 -4
- package/script/transport/transport_provider.d.ts +7 -4
- package/script/transport/transport_provider.js +11 -8
- package/script/types/3_message.d.ts +5 -1
- package/script/types/3_message.js +26 -22
- package/script/utilities/1_message.d.ts +2 -2
- package/script/utilities/1_message.js +9 -9
- package/script/utilities/1_password.js +1 -1
- package/esm/tl/3_tl_writer.d.ts +0 -5
- package/esm/tl/4_rpc_result.d.ts +0 -8
- package/esm/tl/5_message.d.ts +0 -11
- package/script/tl/3_tl_writer.d.ts +0 -5
- package/script/tl/4_rpc_result.d.ts +0 -8
- package/script/tl/5_message.d.ts +0 -11
- /package/esm/tl/{3_tl_writer.js → 4_tl_writer.js} +0 -0
- /package/esm/tl/{4_rpc_result.js → 5_rpc_result.js} +0 -0
- /package/script/tl/{3_tl_writer.js → 4_tl_writer.js} +0 -0
- /package/script/tl/{4_rpc_result.js → 5_rpc_result.js} +0 -0
package/esm/client/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { debug, gunzip } from "../deps.js";
|
|
2
|
-
import { ackThreshold, DEFAULT_APP_VERSION, DEFAULT_DEVICE_MODEL,
|
|
1
|
+
import { debug, gunzip, Mutex, queue } from "../deps.js";
|
|
2
|
+
import { ackThreshold, CHANNEL_DIFFERENCE_LIMIT_BOT, CHANNEL_DIFFERENCE_LIMIT_USER, DEFAULT_APP_VERSION, DEFAULT_DEVICE_MODEL, DEFAULT_LANG_CODE, DEFAULT_LANG_PACK, DEFAULT_SYSTEM_LANG_CODE, DEFAULT_SYSTEM_VERSION, LAYER, MAX_CHANNEL_ID, MAX_CHAT_ID, USERNAME_TTL, ZERO_CHANNEL_ID } from "../constants.js";
|
|
3
3
|
import { bigIntFromBuffer, getRandomBigInt, getRandomId } from "../utilities/0_bigint.js";
|
|
4
4
|
import { UNREACHABLE } from "../utilities/0_control.js";
|
|
5
5
|
import { sha1 } from "../utilities/0_hash.js";
|
|
@@ -10,9 +10,9 @@ import { as } from "../tl/1_tl_object.js";
|
|
|
10
10
|
import * as types from "../tl/2_types.js";
|
|
11
11
|
import * as functions from "../tl/3_functions.js";
|
|
12
12
|
import { TLReader } from "../tl/3_tl_reader.js";
|
|
13
|
-
import { RPCResult } from "../tl/
|
|
14
|
-
import { Message } from "../tl/
|
|
15
|
-
import { MessageContainer } from "../tl/
|
|
13
|
+
import { RPCResult } from "../tl/5_rpc_result.js";
|
|
14
|
+
import { Message as Message_ } from "../tl/6_message.js"; // MTProto API message
|
|
15
|
+
import { MessageContainer } from "../tl/7_message_container.js";
|
|
16
16
|
import { ClientAbstract } from "./client_abstract.js";
|
|
17
17
|
import { ClientPlain } from "./client_plain.js";
|
|
18
18
|
import { StorageMemory } from "../storage/storage_memory.js";
|
|
@@ -21,8 +21,12 @@ import { replyKeyboardRemoveToTlObject } from "../types/0_reply_keyboard_remove.
|
|
|
21
21
|
import { forceReplyToTlObject } from "../types/0_force_reply.js";
|
|
22
22
|
import { replyKeyboardMarkupToTlObject } from "../types/2_reply_keyboard_markup.js";
|
|
23
23
|
import { inlineKeyboardMarkupToTlObject } from "../types/2_inline_keyboard_markup.js";
|
|
24
|
-
import { constructMessage } from "../types/3_message.js";
|
|
24
|
+
import { constructMessage } from "../types/3_message.js"; // high-level wrapper for Telegram API's message
|
|
25
25
|
const d = debug("client");
|
|
26
|
+
const dRecoverUpdateGap = debug("client_recoverUpdateGap");
|
|
27
|
+
const dRecoverChannelUpdateGap = debug("client_recoverChannelUpdateGap");
|
|
28
|
+
const UPDATE_GAP = Symbol();
|
|
29
|
+
export const getEntity = Symbol();
|
|
26
30
|
export const restartAuth = Symbol();
|
|
27
31
|
export var ParseMode;
|
|
28
32
|
(function (ParseMode) {
|
|
@@ -88,7 +92,13 @@ export class Client extends ClientAbstract {
|
|
|
88
92
|
writable: true,
|
|
89
93
|
value: new Set()
|
|
90
94
|
});
|
|
91
|
-
Object.defineProperty(this, "
|
|
95
|
+
Object.defineProperty(this, "updateState", {
|
|
96
|
+
enumerable: true,
|
|
97
|
+
configurable: true,
|
|
98
|
+
writable: true,
|
|
99
|
+
value: void 0
|
|
100
|
+
});
|
|
101
|
+
Object.defineProperty(this, "updateHandler", {
|
|
92
102
|
enumerable: true,
|
|
93
103
|
configurable: true,
|
|
94
104
|
writable: true,
|
|
@@ -142,6 +152,79 @@ export class Client extends ClientAbstract {
|
|
|
142
152
|
writable: true,
|
|
143
153
|
value: false
|
|
144
154
|
});
|
|
155
|
+
Object.defineProperty(this, "messageProcessQueue", {
|
|
156
|
+
enumerable: true,
|
|
157
|
+
configurable: true,
|
|
158
|
+
writable: true,
|
|
159
|
+
value: queue(async (message) => {
|
|
160
|
+
let body = message.body;
|
|
161
|
+
if (body instanceof types.GZIPPacked) {
|
|
162
|
+
body = new TLReader(gunzip(body.packedData)).readObject();
|
|
163
|
+
}
|
|
164
|
+
d("received %s", body.constructor.name);
|
|
165
|
+
if (body instanceof types.Updates || body instanceof types.TypeUpdate) {
|
|
166
|
+
await this.processUpdates(body);
|
|
167
|
+
}
|
|
168
|
+
else if (message.body instanceof RPCResult) {
|
|
169
|
+
let result = message.body.result;
|
|
170
|
+
if (result instanceof types.GZIPPacked) {
|
|
171
|
+
result = new TLReader(gunzip(result.packedData)).readObject();
|
|
172
|
+
}
|
|
173
|
+
if (result instanceof types.RPCError) {
|
|
174
|
+
d("RPCResult: %d %s", result.errorCode, result.errorMessage);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
d("RPCResult: %s", result.constructor.name);
|
|
178
|
+
}
|
|
179
|
+
if (result instanceof types.Updates || result instanceof types.TypeUpdate) {
|
|
180
|
+
await this.processUpdates(result);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
await this.processResult(result);
|
|
184
|
+
}
|
|
185
|
+
const promise = this.promises.get(message.body.messageId);
|
|
186
|
+
if (promise) {
|
|
187
|
+
if (result instanceof types.RPCError) {
|
|
188
|
+
promise.reject(result);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
promise.resolve(result);
|
|
192
|
+
}
|
|
193
|
+
this.promises.delete(message.body.messageId);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else if (message.body instanceof types.Pong) {
|
|
197
|
+
const promise = this.promises.get(message.body.msgId);
|
|
198
|
+
if (promise) {
|
|
199
|
+
promise.resolve(message.body);
|
|
200
|
+
this.promises.delete(message.body.msgId);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else if (message.body instanceof types.BadMsgNotification || message.body instanceof types.BadServerSalt) {
|
|
204
|
+
if (message.body instanceof types.BadServerSalt) {
|
|
205
|
+
this.state.salt = message.body.newServerSalt;
|
|
206
|
+
}
|
|
207
|
+
const promise = this.promises.get(message.body.badMsgId);
|
|
208
|
+
if (promise) {
|
|
209
|
+
promise.resolve(message.body);
|
|
210
|
+
this.promises.delete(message.body.badMsgId);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
this.toAcknowledge.add(message.id);
|
|
214
|
+
}, 2)
|
|
215
|
+
});
|
|
216
|
+
Object.defineProperty(this, "updateApplicationMutex", {
|
|
217
|
+
enumerable: true,
|
|
218
|
+
configurable: true,
|
|
219
|
+
writable: true,
|
|
220
|
+
value: new Mutex()
|
|
221
|
+
});
|
|
222
|
+
Object.defineProperty(this, "updateGapRecoveryMutex", {
|
|
223
|
+
enumerable: true,
|
|
224
|
+
configurable: true,
|
|
225
|
+
writable: true,
|
|
226
|
+
value: new Mutex()
|
|
227
|
+
});
|
|
145
228
|
this.parseMode = params?.parseMode ?? ParseMode.None;
|
|
146
229
|
this.appVersion = params?.appVersion ?? DEFAULT_APP_VERSION;
|
|
147
230
|
this.deviceModel = params?.deviceModel ?? DEFAULT_DEVICE_MODEL;
|
|
@@ -164,6 +247,7 @@ export class Client extends ClientAbstract {
|
|
|
164
247
|
if (await this.storage.getDc() != dc) {
|
|
165
248
|
await this.storage.setDc(dc);
|
|
166
249
|
await this.storage.setAuthKey(null);
|
|
250
|
+
await this.storage.getAuthKey();
|
|
167
251
|
}
|
|
168
252
|
super.setDc(dc);
|
|
169
253
|
}
|
|
@@ -205,12 +289,17 @@ export class Client extends ClientAbstract {
|
|
|
205
289
|
}
|
|
206
290
|
await super.connect();
|
|
207
291
|
if (dc == null) {
|
|
208
|
-
await this.storage.setDc(
|
|
292
|
+
await this.storage.setDc(this.transportProvider.initialDc);
|
|
209
293
|
}
|
|
210
294
|
d("enrypted client connected");
|
|
211
295
|
this.receiveLoop();
|
|
212
296
|
this.pingLoop();
|
|
213
297
|
}
|
|
298
|
+
async fetchState(source) {
|
|
299
|
+
const state = await this.invoke(new functions.UpdatesGetState());
|
|
300
|
+
this.updateState = state;
|
|
301
|
+
d("state fetched [%s]", source);
|
|
302
|
+
}
|
|
214
303
|
/**
|
|
215
304
|
* Calls [initConnection](1) and authorizes the client with one of the following:
|
|
216
305
|
*
|
|
@@ -260,6 +349,7 @@ export class Client extends ClientAbstract {
|
|
|
260
349
|
const password = typeof params.password === "string" ? params.password : await params.password();
|
|
261
350
|
const input = await checkPassword(password, ap);
|
|
262
351
|
await this.invoke(new functions.AuthCheckPassword({ password: input }));
|
|
352
|
+
await this.storage.setAccountType("user");
|
|
263
353
|
d("authorized as user");
|
|
264
354
|
break;
|
|
265
355
|
}
|
|
@@ -282,7 +372,7 @@ export class Client extends ClientAbstract {
|
|
|
282
372
|
}
|
|
283
373
|
};
|
|
284
374
|
try {
|
|
285
|
-
await this.
|
|
375
|
+
await this.fetchState("authorize");
|
|
286
376
|
d("already authorized");
|
|
287
377
|
return;
|
|
288
378
|
}
|
|
@@ -323,6 +413,7 @@ export class Client extends ClientAbstract {
|
|
|
323
413
|
}
|
|
324
414
|
else {
|
|
325
415
|
signedIn = true;
|
|
416
|
+
await this.storage.setAccountType("user");
|
|
326
417
|
d("authorized as user");
|
|
327
418
|
break;
|
|
328
419
|
}
|
|
@@ -357,6 +448,7 @@ export class Client extends ClientAbstract {
|
|
|
357
448
|
}
|
|
358
449
|
else {
|
|
359
450
|
await this.invoke(new functions.AuthImportBotAuthorization({ apiId: this.apiId, apiHash: this.apiHash, botAuthToken: params, flags: 0 }));
|
|
451
|
+
await this.storage.setAccountType("bot");
|
|
360
452
|
d("authorized as bot");
|
|
361
453
|
}
|
|
362
454
|
}
|
|
@@ -384,6 +476,14 @@ export class Client extends ClientAbstract {
|
|
|
384
476
|
throw err;
|
|
385
477
|
}
|
|
386
478
|
}
|
|
479
|
+
finally {
|
|
480
|
+
try {
|
|
481
|
+
await this.fetchState("authorize");
|
|
482
|
+
}
|
|
483
|
+
catch (_err) {
|
|
484
|
+
//
|
|
485
|
+
}
|
|
486
|
+
}
|
|
387
487
|
}
|
|
388
488
|
async receiveLoop() {
|
|
389
489
|
if (!this.auth) {
|
|
@@ -416,58 +516,7 @@ export class Client extends ClientAbstract {
|
|
|
416
516
|
}
|
|
417
517
|
const messages = decrypted instanceof MessageContainer ? decrypted.messages : [decrypted];
|
|
418
518
|
for (const message of messages) {
|
|
419
|
-
|
|
420
|
-
if (body instanceof types.GZIPPacked) {
|
|
421
|
-
body = new TLReader(gunzip(body.packedData)).readObject();
|
|
422
|
-
}
|
|
423
|
-
d("received %s", body.constructor.name);
|
|
424
|
-
if (body instanceof types.Updates) {
|
|
425
|
-
this.processUpdates(body);
|
|
426
|
-
}
|
|
427
|
-
else if (message.body instanceof RPCResult) {
|
|
428
|
-
let result = message.body.result;
|
|
429
|
-
if (result instanceof types.GZIPPacked) {
|
|
430
|
-
result = new TLReader(gunzip(result.packedData)).readObject();
|
|
431
|
-
}
|
|
432
|
-
if (result instanceof types.RPCError) {
|
|
433
|
-
d("RPCResult: %d %s", result.errorCode, result.errorMessage);
|
|
434
|
-
}
|
|
435
|
-
else {
|
|
436
|
-
d("RPCResult: %s", result.constructor.name);
|
|
437
|
-
}
|
|
438
|
-
const promise = this.promises.get(message.body.messageId);
|
|
439
|
-
if (promise) {
|
|
440
|
-
if (result instanceof types.RPCError) {
|
|
441
|
-
promise.reject(result);
|
|
442
|
-
}
|
|
443
|
-
else {
|
|
444
|
-
if (result instanceof types.Updates) {
|
|
445
|
-
await this.processChats(result.chats);
|
|
446
|
-
await this.processUsers(result.users);
|
|
447
|
-
}
|
|
448
|
-
promise.resolve(result);
|
|
449
|
-
}
|
|
450
|
-
this.promises.delete(message.body.messageId);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
else if (message.body instanceof types.Pong) {
|
|
454
|
-
const promise = this.promises.get(message.body.msgId);
|
|
455
|
-
if (promise) {
|
|
456
|
-
promise.resolve(message.body);
|
|
457
|
-
this.promises.delete(message.body.msgId);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
else if (message.body instanceof types.BadMsgNotification || message.body instanceof types.BadServerSalt) {
|
|
461
|
-
if (message.body instanceof types.BadServerSalt) {
|
|
462
|
-
this.state.salt = message.body.newServerSalt;
|
|
463
|
-
}
|
|
464
|
-
const promise = this.promises.get(message.body.badMsgId);
|
|
465
|
-
if (promise) {
|
|
466
|
-
promise.resolve(message.body);
|
|
467
|
-
this.promises.delete(message.body.badMsgId);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
this.toAcknowledge.add(message.id);
|
|
519
|
+
this.messageProcessQueue.push(message);
|
|
471
520
|
}
|
|
472
521
|
}
|
|
473
522
|
}
|
|
@@ -491,7 +540,7 @@ export class Client extends ClientAbstract {
|
|
|
491
540
|
seqNo++;
|
|
492
541
|
this.state.seqNo++;
|
|
493
542
|
}
|
|
494
|
-
const message = new
|
|
543
|
+
const message = new Message_(getMessageId(), seqNo, function_);
|
|
495
544
|
await this.transport.send(await encryptMessage(message, this.auth.key, this.auth.id, this.state.salt, this.sessionId));
|
|
496
545
|
d("invoked %s", function_.constructor.name);
|
|
497
546
|
if (noWait) {
|
|
@@ -516,6 +565,7 @@ export class Client extends ClientAbstract {
|
|
|
516
565
|
async processChats(chats) {
|
|
517
566
|
for (const chat of chats) {
|
|
518
567
|
if (chat instanceof types.Channel && chat.accessHash) {
|
|
568
|
+
await this.storage.setEntity(chat);
|
|
519
569
|
await this.storage.setChannelAccessHash(chat.id, chat.accessHash);
|
|
520
570
|
if (chat.username) {
|
|
521
571
|
await this.storage.updateUsernames("channel", chat.id, [chat.username]);
|
|
@@ -524,11 +574,15 @@ export class Client extends ClientAbstract {
|
|
|
524
574
|
await this.storage.updateUsernames("channel", chat.id, chat.usernames.map((v) => v[as](types.Username)).map((v) => v.username));
|
|
525
575
|
}
|
|
526
576
|
}
|
|
577
|
+
else if (chat instanceof types.Chat) {
|
|
578
|
+
await this.storage.setEntity(chat);
|
|
579
|
+
}
|
|
527
580
|
}
|
|
528
581
|
}
|
|
529
582
|
async processUsers(users) {
|
|
530
583
|
for (const user of users) {
|
|
531
584
|
if (user instanceof types.User && user.accessHash) {
|
|
585
|
+
await this.storage.setEntity(user);
|
|
532
586
|
await this.storage.setUserAccessHash(user.id, user.accessHash);
|
|
533
587
|
if (user.username) {
|
|
534
588
|
await this.storage.updateUsernames("user", user.id, [user.username]);
|
|
@@ -539,19 +593,310 @@ export class Client extends ClientAbstract {
|
|
|
539
593
|
}
|
|
540
594
|
}
|
|
541
595
|
}
|
|
596
|
+
async applyUpdateNoGap(update, usePts = true) {
|
|
597
|
+
const release = await this.updateApplicationMutex.acquire();
|
|
598
|
+
try {
|
|
599
|
+
if ((update instanceof types.UpdateNewMessage) ||
|
|
600
|
+
(update instanceof types.UpdateDeleteMessages) ||
|
|
601
|
+
(update instanceof types.UpdateReadHistoryInbox) ||
|
|
602
|
+
(update instanceof types.UpdateReadHistoryOutbox) ||
|
|
603
|
+
(update instanceof types.UpdateWebPage) ||
|
|
604
|
+
(update instanceof types.UpdateReadMessagesContents) ||
|
|
605
|
+
(update instanceof types.UpdateEditMessage) ||
|
|
606
|
+
(update instanceof types.UpdateFolderPeers) ||
|
|
607
|
+
(update instanceof types.UpdatePinnedMessages) ||
|
|
608
|
+
(update instanceof types.UpdatePinnedChannelMessages) ||
|
|
609
|
+
(update instanceof types.UpdateShortMessage) ||
|
|
610
|
+
(update instanceof types.UpdateShortChatMessage) ||
|
|
611
|
+
(update instanceof types.UpdateShortSentMessage)) {
|
|
612
|
+
const localState = await this.getLocalState();
|
|
613
|
+
if (localState.pts + update.ptsCount > update.pts) {
|
|
614
|
+
// the update is already applied
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
else if (localState.pts + update.ptsCount < update.pts) {
|
|
618
|
+
// there is an update gap that needs to be filled
|
|
619
|
+
throw UPDATE_GAP;
|
|
620
|
+
}
|
|
621
|
+
localState.pts = update.pts;
|
|
622
|
+
d("applied update with pts %d", update.pts);
|
|
623
|
+
await this.storage.setState(localState);
|
|
624
|
+
}
|
|
625
|
+
else if (usePts &&
|
|
626
|
+
((update instanceof types.UpdateNewChannelMessage) ||
|
|
627
|
+
(update instanceof types.UpdateDeleteChannelMessages) ||
|
|
628
|
+
(update instanceof types.UpdateEditChannelMessage) ||
|
|
629
|
+
(update instanceof types.UpdateChannelWebPage))) {
|
|
630
|
+
const channelId = update instanceof types.UpdateNewChannelMessage || update instanceof types.UpdateEditChannelMessage ? update.message[as](types.Message).peerId[as](types.PeerChannel).channelId : update.channelId;
|
|
631
|
+
let localPts = await this.storage.getChannelPts(channelId);
|
|
632
|
+
if (!localPts) {
|
|
633
|
+
localPts = update.pts - update.ptsCount;
|
|
634
|
+
}
|
|
635
|
+
if (localPts + update.ptsCount > update.pts) {
|
|
636
|
+
// already applied
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
else if (localPts + update.ptsCount < update.pts) {
|
|
640
|
+
// should call channelGetDifference
|
|
641
|
+
throw UPDATE_GAP;
|
|
642
|
+
}
|
|
643
|
+
d("applied update with pts %d", update.pts);
|
|
644
|
+
await this.storage.setChannelPts(channelId, update.pts);
|
|
645
|
+
}
|
|
646
|
+
// apply update (call listeners)
|
|
647
|
+
this.updateHandler?.(this, update);
|
|
648
|
+
}
|
|
649
|
+
finally {
|
|
650
|
+
release();
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
async applyUpdate(update) {
|
|
654
|
+
if (update instanceof types.TypeUpdates &&
|
|
655
|
+
!(update instanceof types.UpdateShortMessage) &&
|
|
656
|
+
!(update instanceof types.UpdateShortChatMessage) &&
|
|
657
|
+
!(update instanceof types.UpdateShortSentMessage)) {
|
|
658
|
+
// other constructors inheriting Updates are not applicable
|
|
659
|
+
UNREACHABLE();
|
|
660
|
+
}
|
|
661
|
+
if (update instanceof types.TypeUpdate && update instanceof types.UpdateChannelTooLong) {
|
|
662
|
+
// updateChannelTooLong is not applicable
|
|
663
|
+
UNREACHABLE();
|
|
664
|
+
}
|
|
665
|
+
// can't apply updates when filling gap
|
|
666
|
+
const release = await this.updateGapRecoveryMutex.acquire();
|
|
667
|
+
try {
|
|
668
|
+
await this.applyUpdateNoGap(update);
|
|
669
|
+
release();
|
|
670
|
+
}
|
|
671
|
+
catch (err) {
|
|
672
|
+
release();
|
|
673
|
+
if (err == UPDATE_GAP) {
|
|
674
|
+
if ((update instanceof types.UpdateNewChannelMessage) ||
|
|
675
|
+
(update instanceof types.UpdateDeleteChannelMessages) ||
|
|
676
|
+
(update instanceof types.UpdateEditChannelMessage) ||
|
|
677
|
+
(update instanceof types.UpdateChannelWebPage)) {
|
|
678
|
+
const channelId = update instanceof types.UpdateNewChannelMessage || update instanceof types.UpdateEditChannelMessage ? update.message[as](types.Message).peerId[as](types.PeerChannel).channelId : update.channelId;
|
|
679
|
+
await this.recoverChannelUpdateGap(channelId, "applyUpdate");
|
|
680
|
+
}
|
|
681
|
+
else if ((update instanceof types.UpdateNewMessage) ||
|
|
682
|
+
(update instanceof types.UpdateDeleteMessages) ||
|
|
683
|
+
(update instanceof types.UpdateReadHistoryInbox) ||
|
|
684
|
+
(update instanceof types.UpdateReadHistoryOutbox) ||
|
|
685
|
+
(update instanceof types.UpdateWebPage) ||
|
|
686
|
+
(update instanceof types.UpdateReadMessagesContents) ||
|
|
687
|
+
(update instanceof types.UpdateEditMessage) ||
|
|
688
|
+
(update instanceof types.UpdateFolderPeers) ||
|
|
689
|
+
(update instanceof types.UpdatePinnedMessages) ||
|
|
690
|
+
(update instanceof types.UpdatePinnedChannelMessages) ||
|
|
691
|
+
(update instanceof types.UpdateShortMessage) ||
|
|
692
|
+
(update instanceof types.UpdateShortChatMessage) ||
|
|
693
|
+
(update instanceof types.UpdateShortSentMessage)) {
|
|
694
|
+
await this.recoverUpdateGap("applyUpdate");
|
|
695
|
+
}
|
|
696
|
+
else {
|
|
697
|
+
// can't detect update gap from other types of updates
|
|
698
|
+
UNREACHABLE();
|
|
699
|
+
}
|
|
700
|
+
// just for integrity
|
|
701
|
+
const release = await this.updateGapRecoveryMutex.acquire();
|
|
702
|
+
try {
|
|
703
|
+
await this.applyUpdateNoGap(update);
|
|
704
|
+
}
|
|
705
|
+
catch (err) {
|
|
706
|
+
if (err == UPDATE_GAP) {
|
|
707
|
+
// the gap must have been filled until now
|
|
708
|
+
UNREACHABLE();
|
|
709
|
+
}
|
|
710
|
+
else {
|
|
711
|
+
throw err;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
finally {
|
|
715
|
+
release();
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
else {
|
|
719
|
+
throw err;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
542
723
|
async processUpdates(updates) {
|
|
543
724
|
try {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
await this.
|
|
725
|
+
if (updates instanceof types.TypeUpdates) {
|
|
726
|
+
if (updates instanceof types.Updates) {
|
|
727
|
+
await this.processChats(updates.chats);
|
|
728
|
+
await this.processUsers(updates.users);
|
|
729
|
+
await this.setUpdateStateDate(updates.date);
|
|
730
|
+
for (const update of updates.updates) {
|
|
731
|
+
await this.processUpdates(update);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
else if (updates instanceof types.UpdateShortMessage ||
|
|
735
|
+
updates instanceof types.UpdateShortChatMessage ||
|
|
736
|
+
updates instanceof types.UpdateShortSentMessage) {
|
|
737
|
+
await this.setUpdateStateDate(updates.date);
|
|
738
|
+
await this.applyUpdate(updates);
|
|
739
|
+
}
|
|
740
|
+
else if (updates instanceof types.UpdatesTooLong) {
|
|
741
|
+
await this.recoverUpdateGap("updatesTooLong");
|
|
742
|
+
}
|
|
743
|
+
else if (updates instanceof types.UpdatesCombined) {
|
|
744
|
+
await this.setUpdateStateDate(updates.date);
|
|
745
|
+
await this.processChats(updates.chats);
|
|
746
|
+
await this.processUsers(updates.users);
|
|
747
|
+
for (const update of updates.updates) {
|
|
748
|
+
await this.processUpdates(update);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
else if (updates instanceof types.TypeUpdate && updates instanceof types.UpdateChannelTooLong) {
|
|
753
|
+
if (updates.pts != undefined) {
|
|
754
|
+
await this.storage.setChannelPts(updates.channelId, updates.pts);
|
|
755
|
+
}
|
|
756
|
+
await this.recoverChannelUpdateGap(updates.channelId, "updateChannelTooLong");
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
if (updates instanceof types.UpdateUserName) {
|
|
760
|
+
await this.storage.updateUsernames("user", updates.userId, updates.usernames.map((v) => v[as](types.Username)).map((v) => v.username));
|
|
761
|
+
}
|
|
762
|
+
else if (updates instanceof types.UpdatePtsChanged) {
|
|
763
|
+
await this.fetchState("updatePtsChanged");
|
|
764
|
+
if (this.updateState) {
|
|
765
|
+
await this.storage.setState(this.updateState);
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
UNREACHABLE();
|
|
769
|
+
}
|
|
549
770
|
}
|
|
771
|
+
await this.applyUpdate(updates);
|
|
550
772
|
}
|
|
551
|
-
await this.updatesHandler?.(this, updates);
|
|
552
773
|
}
|
|
553
774
|
catch (err) {
|
|
554
|
-
|
|
775
|
+
d("error processing updates: %O", err);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
async setUpdateStateDate(date) {
|
|
779
|
+
const release = await this.updateApplicationMutex.acquire();
|
|
780
|
+
try {
|
|
781
|
+
const localState = await this.getLocalState();
|
|
782
|
+
localState.date = date;
|
|
783
|
+
await this.storage.setState(localState);
|
|
784
|
+
}
|
|
785
|
+
finally {
|
|
786
|
+
release();
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
async getLocalState() {
|
|
790
|
+
let localState = await this.storage.getState();
|
|
791
|
+
if (!localState) {
|
|
792
|
+
if (this.updateState) {
|
|
793
|
+
localState = this.updateState;
|
|
794
|
+
await this.storage.setState(localState);
|
|
795
|
+
}
|
|
796
|
+
else {
|
|
797
|
+
UNREACHABLE();
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
return localState;
|
|
801
|
+
}
|
|
802
|
+
async recoverUpdateGap(source) {
|
|
803
|
+
dRecoverUpdateGap("recovering from update gap [%s]", source);
|
|
804
|
+
const release = await this.updateGapRecoveryMutex.acquire();
|
|
805
|
+
try {
|
|
806
|
+
let state = await this.getLocalState();
|
|
807
|
+
while (true) {
|
|
808
|
+
const difference = await this.invoke(new functions.UpdatesGetDifference({ pts: state.pts, date: state.date, qts: state.qts }));
|
|
809
|
+
if (difference instanceof types.UpdatesDifference || difference instanceof types.UpdatesDifferenceSlice) {
|
|
810
|
+
await this.processChats(difference.chats);
|
|
811
|
+
await this.processUsers(difference.users);
|
|
812
|
+
for (const message of difference.newMessages) {
|
|
813
|
+
await this.applyUpdateNoGap(new types.UpdateNewMessage({ message, pts: 0, ptsCount: 0 }));
|
|
814
|
+
}
|
|
815
|
+
for (const update of difference.otherUpdates) {
|
|
816
|
+
await this.applyUpdateNoGap(update);
|
|
817
|
+
}
|
|
818
|
+
if (difference instanceof types.UpdatesDifference) {
|
|
819
|
+
dRecoverUpdateGap("recovered from update gap");
|
|
820
|
+
break;
|
|
821
|
+
}
|
|
822
|
+
else if (difference instanceof types.UpdatesDifferenceSlice) {
|
|
823
|
+
state = difference.intermediateState[as](types.UpdatesState);
|
|
824
|
+
}
|
|
825
|
+
else {
|
|
826
|
+
UNREACHABLE();
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
else if (difference instanceof types.UpdatesDifferenceTooLong) {
|
|
830
|
+
// stored messages should be invalidated in case we store messages in the future
|
|
831
|
+
state.pts = difference.pts;
|
|
832
|
+
dRecoverUpdateGap("received differenceTooLong");
|
|
833
|
+
}
|
|
834
|
+
else if (difference instanceof types.UpdatesDifferenceEmpty) {
|
|
835
|
+
await this.setUpdateStateDate(difference.date);
|
|
836
|
+
dRecoverUpdateGap("there was no update gap");
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
839
|
+
else {
|
|
840
|
+
UNREACHABLE();
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
finally {
|
|
845
|
+
release();
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
async recoverChannelUpdateGap(channelId, source) {
|
|
849
|
+
dRecoverChannelUpdateGap("recovering channel update gap [%o, %s]", channelId, source);
|
|
850
|
+
const release = await this.updateGapRecoveryMutex.acquire();
|
|
851
|
+
try {
|
|
852
|
+
const pts_ = await this.storage.getChannelPts(channelId);
|
|
853
|
+
let pts = pts_ == null ? 1 : pts_;
|
|
854
|
+
while (true) {
|
|
855
|
+
const { accessHash } = await this.getInputPeer(ZERO_CHANNEL_ID + -Number(channelId)).then((v) => v[as](types.InputPeerChannel));
|
|
856
|
+
const difference = await this.invoke(new functions.UpdatesGetChannelDifference({
|
|
857
|
+
pts,
|
|
858
|
+
channel: new types.InputChannel({ channelId, accessHash: accessHash }),
|
|
859
|
+
filter: new types.ChannelMessagesFilterEmpty(),
|
|
860
|
+
limit: await this.storage.getAccountType() == "user" ? CHANNEL_DIFFERENCE_LIMIT_USER : CHANNEL_DIFFERENCE_LIMIT_BOT,
|
|
861
|
+
}));
|
|
862
|
+
if (difference instanceof types.UpdatesChannelDifference) {
|
|
863
|
+
await this.processChats(difference.chats);
|
|
864
|
+
await this.processUsers(difference.users);
|
|
865
|
+
for (const message of difference.newMessages) {
|
|
866
|
+
await this.applyUpdateNoGap(new types.UpdateNewChannelMessage({ message, pts: 0, ptsCount: 0 }), false);
|
|
867
|
+
}
|
|
868
|
+
for (const update of difference.otherUpdates) {
|
|
869
|
+
await this.applyUpdateNoGap(update, false);
|
|
870
|
+
}
|
|
871
|
+
await this.storage.setChannelPts(channelId, difference.pts);
|
|
872
|
+
dRecoverChannelUpdateGap("recovered from update gap");
|
|
873
|
+
break;
|
|
874
|
+
}
|
|
875
|
+
else if (difference instanceof types.UpdatesChannelDifferenceTooLong) {
|
|
876
|
+
// invalidate messages
|
|
877
|
+
dRecoverChannelUpdateGap("received channelDifferenceTooLong");
|
|
878
|
+
await this.processChats(difference.chats);
|
|
879
|
+
await this.processUsers(difference.users);
|
|
880
|
+
for (const message of difference.messages) {
|
|
881
|
+
await this.applyUpdateNoGap(new types.UpdateNewChannelMessage({ message, pts: 0, ptsCount: 0 }), false);
|
|
882
|
+
}
|
|
883
|
+
const pts_ = difference.dialog[as](types.Dialog).pts;
|
|
884
|
+
if (pts_ != undefined) {
|
|
885
|
+
pts = pts_;
|
|
886
|
+
}
|
|
887
|
+
else {
|
|
888
|
+
UNREACHABLE();
|
|
889
|
+
}
|
|
890
|
+
dRecoverChannelUpdateGap("processed channelDifferenceTooLong");
|
|
891
|
+
}
|
|
892
|
+
else if (difference instanceof types.UpdatesChannelDifferenceEmpty) {
|
|
893
|
+
dRecoverChannelUpdateGap("there was no update gap");
|
|
894
|
+
break;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
finally {
|
|
899
|
+
release();
|
|
555
900
|
}
|
|
556
901
|
}
|
|
557
902
|
async getInputPeer(id) {
|
|
@@ -620,6 +965,50 @@ export class Client extends ClientAbstract {
|
|
|
620
965
|
throw new Error("ID format unknown or not implemented");
|
|
621
966
|
}
|
|
622
967
|
}
|
|
968
|
+
[getEntity](peer) {
|
|
969
|
+
const type = peer instanceof types.PeerUser ? "user" : peer instanceof types.PeerChat ? "chat" : peer instanceof types.PeerChannel ? "channel" : UNREACHABLE();
|
|
970
|
+
const id = peer instanceof types.PeerUser ? peer.userId : peer instanceof types.PeerChat ? peer.chatId : peer instanceof types.PeerChannel ? peer.channelId : UNREACHABLE();
|
|
971
|
+
return this.storage.getEntity(type, id);
|
|
972
|
+
}
|
|
973
|
+
async processResult(result) {
|
|
974
|
+
if (result instanceof types.MessagesDialogs ||
|
|
975
|
+
result instanceof types.MessagesDialogsSlice ||
|
|
976
|
+
result instanceof types.MessagesMessages ||
|
|
977
|
+
result instanceof types.MessagesMessagesSlice ||
|
|
978
|
+
result instanceof types.MessagesChannelMessages ||
|
|
979
|
+
result instanceof types.MessagesChatFull ||
|
|
980
|
+
result instanceof types.ContactsFound ||
|
|
981
|
+
result instanceof types.AccountPrivacyRules ||
|
|
982
|
+
result instanceof types.ContactsResolvedPeer ||
|
|
983
|
+
result instanceof types.ChannelsChannelParticipants ||
|
|
984
|
+
result instanceof types.ChannelsChannelParticipant ||
|
|
985
|
+
result instanceof types.MessagesPeerDialogs ||
|
|
986
|
+
result instanceof types.ContactsTopPeers ||
|
|
987
|
+
result instanceof types.ChannelsAdminLogResults ||
|
|
988
|
+
result instanceof types.HelpRecentMeURLs ||
|
|
989
|
+
result instanceof types.MessagesInactiveChats ||
|
|
990
|
+
result instanceof types.HelpPromoData ||
|
|
991
|
+
result instanceof types.MessagesMessageViews ||
|
|
992
|
+
result instanceof types.MessagesDiscussionMessage ||
|
|
993
|
+
result instanceof types.PhoneGroupCall ||
|
|
994
|
+
result instanceof types.PhoneGroupParticipants ||
|
|
995
|
+
result instanceof types.PhoneJoinAsPeers ||
|
|
996
|
+
result instanceof types.MessagesSponsoredMessages ||
|
|
997
|
+
result instanceof types.MessagesSearchResultsCalendar ||
|
|
998
|
+
result instanceof types.ChannelsSendAsPeers ||
|
|
999
|
+
result instanceof types.UsersUserFull ||
|
|
1000
|
+
result instanceof types.MessagesPeerSettings ||
|
|
1001
|
+
result instanceof types.MessagesMessageReactionsList ||
|
|
1002
|
+
result instanceof types.MessagesForumTopics ||
|
|
1003
|
+
result instanceof types.AccountAutoSaveSettings ||
|
|
1004
|
+
result instanceof types.ChatlistsExportedInvites ||
|
|
1005
|
+
result instanceof types.ChatlistsChatlistInviteAlready ||
|
|
1006
|
+
result instanceof types.ChatlistsChatlistInvite ||
|
|
1007
|
+
result instanceof types.ChatlistsChatlistUpdates) {
|
|
1008
|
+
await this.processChats(result.chats);
|
|
1009
|
+
await this.processUsers(result.users);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
623
1012
|
async sendMessage(chatId, text, params) {
|
|
624
1013
|
const entities_ = params?.entities ?? [];
|
|
625
1014
|
const parseMode = params?.parseMode ?? this.parseMode;
|
|
@@ -665,7 +1054,7 @@ export class Client extends ClientAbstract {
|
|
|
665
1054
|
const topMsgId = params?.messageThreadId;
|
|
666
1055
|
const sendAs = params?.sendAs ? await this.getInputPeer(params.sendAs) : undefined;
|
|
667
1056
|
const entities = entities_?.length > 0 ? entities_.map((v) => messageEntityToTlObject(v)) : undefined;
|
|
668
|
-
const
|
|
1057
|
+
const result = await this.invoke(new functions.MessagesSendMessage({
|
|
669
1058
|
peer,
|
|
670
1059
|
randomId,
|
|
671
1060
|
message,
|
|
@@ -677,15 +1066,47 @@ export class Client extends ClientAbstract {
|
|
|
677
1066
|
sendAs,
|
|
678
1067
|
entities,
|
|
679
1068
|
replyMarkup,
|
|
680
|
-
}))
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
1069
|
+
}));
|
|
1070
|
+
if (result instanceof types.Updates) {
|
|
1071
|
+
for (const update of result.updates) {
|
|
1072
|
+
if (update instanceof types.UpdateNewMessage) {
|
|
1073
|
+
return constructMessage(update.message[as](types.Message), this[getEntity].bind(this));
|
|
1074
|
+
}
|
|
1075
|
+
else if (update instanceof types.UpdateNewChannelMessage) {
|
|
1076
|
+
return constructMessage(update.message[as](types.Message), this[getEntity].bind(this));
|
|
1077
|
+
}
|
|
684
1078
|
}
|
|
685
|
-
|
|
686
|
-
|
|
1079
|
+
}
|
|
1080
|
+
else if (result instanceof types.UpdateShortSentMessage || result instanceof types.UpdateShortSentMessage) {
|
|
1081
|
+
const message = await this.getMessage(chatId, result.id);
|
|
1082
|
+
if (message != null) {
|
|
1083
|
+
return message;
|
|
687
1084
|
}
|
|
688
1085
|
}
|
|
689
1086
|
UNREACHABLE();
|
|
690
1087
|
}
|
|
1088
|
+
async getMessages(chatId, messageIds) {
|
|
1089
|
+
const peer = await this.getInputPeer(chatId);
|
|
1090
|
+
let messages_;
|
|
1091
|
+
if (peer instanceof types.InputPeerChannel) {
|
|
1092
|
+
messages_ = await this.invoke(new functions.ChannelsGetMessages({
|
|
1093
|
+
channel: new types.InputChannel({ channelId: peer.channelId, accessHash: peer.accessHash }),
|
|
1094
|
+
id: messageIds.map((v) => new types.InputMessageID({ id: v })),
|
|
1095
|
+
})).then((v) => v[as](types.MessagesMessages));
|
|
1096
|
+
}
|
|
1097
|
+
else {
|
|
1098
|
+
messages_ = await this.invoke(new functions.MessagesGetMessages({
|
|
1099
|
+
id: messageIds.map((v) => new types.InputMessageID({ id: v })),
|
|
1100
|
+
})).then((v) => v[as](types.MessagesMessages));
|
|
1101
|
+
}
|
|
1102
|
+
const messages = new Array();
|
|
1103
|
+
for (const message_ of messages_.messages) {
|
|
1104
|
+
messages.push(await constructMessage(message_[as](types.Message), this[getEntity].bind(this)));
|
|
1105
|
+
}
|
|
1106
|
+
return messages;
|
|
1107
|
+
}
|
|
1108
|
+
async getMessage(chatId, messageId) {
|
|
1109
|
+
const messages = await this.getMessages(chatId, [messageId]);
|
|
1110
|
+
return messages[0] ?? null;
|
|
1111
|
+
}
|
|
691
1112
|
}
|