teleproto 1.225.3 → 1.227.0

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/Version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "1.225.3";
1
+ export declare const version = "1.227.0";
package/Version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.version = void 0;
4
- exports.version = "1.225.3";
4
+ exports.version = "1.227.0";
@@ -70,7 +70,7 @@ export declare class TelegramClient extends TelegramBaseClient {
70
70
  *
71
71
  * You can now use the client instance to call other api requests.
72
72
  */
73
- start(authParams: authMethods.UserAuthParams | authMethods.BotAuthParams): Promise<void>;
73
+ start(authParams?: authMethods.UserAuthParams | authMethods.BotAuthParams): Promise<void>;
74
74
  /**
75
75
  * Checks whether the current client is authorized or not. (logged in as a user)
76
76
  * @example
@@ -131,11 +131,18 @@ export declare class TelegramClient extends TelegramBaseClient {
131
131
  /**
132
132
  * logs the user using a QR code to be scanned.<br/>
133
133
  * this function generates the QR code that needs to be scanned by mobile.
134
+ *
135
+ * The flow can be cancelled at any time (e.g. the user closed the page) by
136
+ * passing an `abortSignal`; once aborted it stops polling for tokens and
137
+ * rejects with an `AbortError`.
134
138
  * @example
135
139
  * '''ts
136
140
  * await client.connect();
137
- * const user = await client.signInUserWithQrCode({ apiId, apiHash },
138
- * {
141
+ * const controller = new AbortController();
142
+ * // call controller.abort() to cancel the QR login at any time
143
+ * try {
144
+ * const user = await client.signInUserWithQrCode({ apiId, apiHash },
145
+ * {
139
146
  * onError: async function(p1: Error) {
140
147
  * console.log("error", p1);
141
148
  * // true = stop the authentication processes
@@ -150,14 +157,19 @@ export declare class TelegramClient extends TelegramBaseClient {
150
157
  * password: async (hint) => {
151
158
  * // password if needed
152
159
  * return "1111";
153
- * }
160
+ * },
161
+ * abortSignal: controller.signal,
154
162
  * }
155
- * );
156
- * console.log("user is", user);
163
+ * );
164
+ * console.log("user is", user);
165
+ * } catch (err) {
166
+ * if (err.name === "AbortError") console.log("QR login cancelled");
167
+ * else throw err;
168
+ * }
157
169
  *
158
170
  * '''
159
171
  * @param apiCredentials - credentials to be used.
160
- * @param authParams - user auth params.
172
+ * @param authParams - user auth params. Pass `abortSignal` to cancel the flow.
161
173
  */
162
174
  signInUserWithQrCode(apiCredentials: authMethods.ApiCredentials, authParams: authMethods.QrCodeAuthParams): Promise<Api.TypeUser>;
163
175
  /**
@@ -177,11 +177,18 @@ class TelegramClient extends telegramBaseClient_1.TelegramBaseClient {
177
177
  /**
178
178
  * logs the user using a QR code to be scanned.<br/>
179
179
  * this function generates the QR code that needs to be scanned by mobile.
180
+ *
181
+ * The flow can be cancelled at any time (e.g. the user closed the page) by
182
+ * passing an `abortSignal`; once aborted it stops polling for tokens and
183
+ * rejects with an `AbortError`.
180
184
  * @example
181
185
  * '''ts
182
186
  * await client.connect();
183
- * const user = await client.signInUserWithQrCode({ apiId, apiHash },
184
- * {
187
+ * const controller = new AbortController();
188
+ * // call controller.abort() to cancel the QR login at any time
189
+ * try {
190
+ * const user = await client.signInUserWithQrCode({ apiId, apiHash },
191
+ * {
185
192
  * onError: async function(p1: Error) {
186
193
  * console.log("error", p1);
187
194
  * // true = stop the authentication processes
@@ -196,14 +203,19 @@ class TelegramClient extends telegramBaseClient_1.TelegramBaseClient {
196
203
  * password: async (hint) => {
197
204
  * // password if needed
198
205
  * return "1111";
199
- * }
206
+ * },
207
+ * abortSignal: controller.signal,
200
208
  * }
201
- * );
202
- * console.log("user is", user);
209
+ * );
210
+ * console.log("user is", user);
211
+ * } catch (err) {
212
+ * if (err.name === "AbortError") console.log("QR login cancelled");
213
+ * else throw err;
214
+ * }
203
215
  *
204
216
  * '''
205
217
  * @param apiCredentials - credentials to be used.
206
- * @param authParams - user auth params.
218
+ * @param authParams - user auth params. Pass `abortSignal` to cancel the flow.
207
219
  */
208
220
  signInUserWithQrCode(apiCredentials, authParams) {
209
221
  return authMethods.signInUserWithQrCode(this, apiCredentials, authParams);
@@ -14,6 +14,8 @@ export declare class UpdateManager {
14
14
  private globalPtsTimer?;
15
15
  private readonly channels;
16
16
  private readonly pendingSeq;
17
+ private readonly recentMessageKeys;
18
+ private readonly recentMessageQueue;
17
19
  private fetchingDifference;
18
20
  private failTimeoutS;
19
21
  private failRetryTimer?;
@@ -38,6 +40,7 @@ export declare class UpdateManager {
38
40
  private handleShortMessage;
39
41
  private feedUpdate;
40
42
  private dispatch;
43
+ private isDuplicateMessage;
41
44
  private collectEntities;
42
45
  private makeGlobalWaiter;
43
46
  private getOrCreateChannel;
@@ -43,6 +43,7 @@ const NO_UPDATES_TIMEOUT_MS = 15 * 60 * 1000;
43
43
  const FAIL_DIFFERENCE_INITIAL_S = 1;
44
44
  const FAIL_DIFFERENCE_CAP_S = 64;
45
45
  const CHANNEL_DIFFERENCE_LIMIT = 100;
46
+ const RECENT_MESSAGE_BUFFER_SIZE = 1000;
46
47
  function isCommonPtsUpdate(update) {
47
48
  return (update instanceof tl_1.Api.UpdateNewMessage ||
48
49
  update instanceof tl_1.Api.UpdateDeleteMessages ||
@@ -79,6 +80,8 @@ class UpdateManager {
79
80
  this.lastUpdateTime = 0;
80
81
  this.channels = new Map();
81
82
  this.pendingSeq = [];
83
+ this.recentMessageKeys = new Set();
84
+ this.recentMessageQueue = [];
82
85
  this.fetchingDifference = false;
83
86
  this.failTimeoutS = FAIL_DIFFERENCE_INITIAL_S;
84
87
  this.channelFailTimeoutS = new Map();
@@ -306,9 +309,39 @@ class UpdateManager {
306
309
  }
307
310
  dispatch(update, payload) {
308
311
  var _a;
312
+ if (this.isDuplicateMessage(update)) {
313
+ this.client._log.debug("Skip duplicate message update (already dispatched)");
314
+ return;
315
+ }
309
316
  update._entities = (_a = payload.entities) !== null && _a !== void 0 ? _a : new Map();
310
317
  (0, updates_1._dispatchUpdate)(this.client, { update }).catch((e) => this.client._log.error(`Error dispatching update: ${e}`));
311
318
  }
319
+ isDuplicateMessage(update) {
320
+ if (!(update instanceof tl_1.Api.UpdateNewMessage) &&
321
+ !(update instanceof tl_1.Api.UpdateNewChannelMessage)) {
322
+ return false;
323
+ }
324
+ const message = update.message;
325
+ if ((message === null || message === void 0 ? void 0 : message.id) == undefined || message.peerId == undefined)
326
+ return false;
327
+ let peerId;
328
+ try {
329
+ peerId = utils.getPeerId(message.peerId);
330
+ }
331
+ catch (_a) {
332
+ return false;
333
+ }
334
+ const key = `${peerId}:${message.id}`;
335
+ if (this.recentMessageKeys.has(key))
336
+ return true;
337
+ this.recentMessageKeys.add(key);
338
+ this.recentMessageQueue.push(key);
339
+ if (this.recentMessageQueue.length > RECENT_MESSAGE_BUFFER_SIZE) {
340
+ const old = this.recentMessageQueue.shift();
341
+ this.recentMessageKeys.delete(old);
342
+ }
343
+ return false;
344
+ }
312
345
  collectEntities(users, chats) {
313
346
  const entities = new Map();
314
347
  for (const x of [...users, ...chats]) {
package/client/auth.d.ts CHANGED
@@ -86,6 +86,9 @@ export interface QrCodeAuthParams extends UserPasswordAuthParams {
86
86
  /** when an error happens during auth this function will be called with the error.<br/>
87
87
  * if this returns true the auth operation will stop. */
88
88
  onError: (err: Error) => Promise<boolean> | void;
89
+ /** an AbortSignal to cancel the QR login flow (e.g. the user closed the page).<br/>
90
+ * when aborted, the flow stops polling and rejects with an `AbortError`. */
91
+ abortSignal?: AbortSignal;
89
92
  }
90
93
  interface ReturnString {
91
94
  (): string;
@@ -111,7 +114,7 @@ export interface ApiCredentials {
111
114
  apiHash: string;
112
115
  }
113
116
  /** @hidden */
114
- export declare function start(client: TelegramClient, authParams: UserAuthParams | BotAuthParams): Promise<void>;
117
+ export declare function start(client: TelegramClient, authParams?: UserAuthParams | BotAuthParams): Promise<void>;
115
118
  /** @hidden */
116
119
  export declare function checkAuthorization(client: TelegramClient): Promise<boolean>;
117
120
  /** @hidden */
package/client/auth.js CHANGED
@@ -49,6 +49,7 @@ const tl_1 = require("../tl");
49
49
  const utils = __importStar(require("../Utils"));
50
50
  const Helpers_1 = require("../Helpers");
51
51
  const Password_1 = require("../Password");
52
+ const errors_1 = require("../errors");
52
53
  const QR_CODE_TIMEOUT = 30000;
53
54
  // region public methods
54
55
  /** @hidden */
@@ -56,9 +57,23 @@ async function start(client, authParams) {
56
57
  if (!client.connected) {
57
58
  await client.connect();
58
59
  }
59
- if (await client.checkAuthorization()) {
60
+ // Probe authorization inline (instead of checkAuthorization) so we can keep
61
+ // the actual error the server returned — e.g. AuthKeyUnregisteredError or
62
+ // SessionRevokedError on a revoked session — rather than discarding it.
63
+ let authError;
64
+ try {
65
+ await client.invoke(new tl_1.Api.updates.GetState());
60
66
  return;
61
67
  }
68
+ catch (e) {
69
+ authError = e;
70
+ }
71
+ // Not authorized and no way to (re)login: surface the real reason instead
72
+ // of crashing on `"phoneNumber" in undefined` further down in _authFlow.
73
+ if (!authParams ||
74
+ (!("phoneNumber" in authParams) && !("botAuthToken" in authParams))) {
75
+ throw (authError !== null && authError !== void 0 ? authError : new errors_1.UnauthorizedError("Not authorized and no auth parameters were provided to log in.", undefined));
76
+ }
62
77
  const apiCredentials = {
63
78
  apiId: client.apiId,
64
79
  apiHash: client.apiHash,
@@ -248,17 +263,23 @@ async function signInUser(client, apiCredentials, authParams) {
248
263
  await authParams.onError(new Error("Auth failed"));
249
264
  return client.signInUser(apiCredentials, authParams);
250
265
  }
266
+ function qrAbortError() {
267
+ const err = new Error("QR login aborted");
268
+ err.name = "AbortError";
269
+ return err;
270
+ }
251
271
  /** @hidden */
252
272
  async function signInUserWithQrCode(client, apiCredentials, authParams) {
253
- let isScanningComplete = false;
254
273
  if (authParams.qrCode == undefined) {
255
274
  throw new Error("qrCode callback not defined");
256
275
  }
276
+ const { abortSignal } = authParams;
277
+ if (abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted)
278
+ throw qrAbortError();
279
+ let isScanningComplete = false;
280
+ const stopped = () => isScanningComplete || !!(abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted);
257
281
  const inputPromise = (async () => {
258
- while (1) {
259
- if (isScanningComplete) {
260
- break;
261
- }
282
+ while (!stopped()) {
262
283
  const result = await client.invoke(new tl_1.Api.auth.ExportLoginToken({
263
284
  apiId: Number(apiCredentials.apiId),
264
285
  apiHash: apiCredentials.apiHash,
@@ -275,21 +296,24 @@ async function signInUserWithQrCode(client, apiCredentials, authParams) {
275
296
  await (0, Helpers_1.sleep)(QR_CODE_TIMEOUT);
276
297
  }
277
298
  })();
278
- const updatePromise = new Promise((resolve) => {
279
- client.addEventHandler((update) => {
280
- if (update instanceof tl_1.Api.UpdateLoginToken) {
281
- resolve(undefined);
282
- }
283
- });
284
- });
299
+ const Raw = require("../events/Raw").Raw;
300
+ const rawEvent = new Raw({});
301
+ const onUpdate = (update) => {
302
+ if (update instanceof tl_1.Api.UpdateLoginToken)
303
+ resolveUpdate();
304
+ };
305
+ let resolveUpdate;
306
+ const updatePromise = new Promise((resolve) => (resolveUpdate = resolve));
307
+ client.addEventHandler(onUpdate, rawEvent);
308
+ const abortPromise = new Promise((_, reject) => abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.addEventListener("abort", () => reject(qrAbortError()), {
309
+ once: true,
310
+ }));
285
311
  try {
286
- await Promise.race([updatePromise, inputPromise]);
287
- }
288
- catch (err) {
289
- throw err;
312
+ await Promise.race([updatePromise, inputPromise, abortPromise]);
290
313
  }
291
314
  finally {
292
315
  isScanningComplete = true;
316
+ client.removeEventHandler(onUpdate, rawEvent);
293
317
  }
294
318
  try {
295
319
  const result2 = await client.invoke(new tl_1.Api.auth.ExportLoginToken({
@@ -65,15 +65,6 @@ export declare class FloodError extends RPCError {
65
65
  code: number;
66
66
  errorMessage: string;
67
67
  }
68
- /**
69
- * The account is frozen and cannot use this method.
70
- * Clients should call help.getAppConfig to get freeze_since_date,
71
- * freeze_until_date, and freeze_appeal_url.
72
- */
73
- export declare class FrozenError extends RPCError {
74
- code: number;
75
- errorMessage: string;
76
- }
77
68
  /**
78
69
  * An internal server error occurred while a request was being processed
79
70
  * for example, there was a disruption while accessing a database or file
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TimedOutError = exports.ServerError = exports.FrozenError = exports.FloodError = exports.AuthKeyError = exports.NotFoundError = exports.ForbiddenError = exports.UnauthorizedError = exports.BadRequestError = exports.InvalidDCError = exports.RPCError = void 0;
3
+ exports.TimedOutError = exports.ServerError = exports.FloodError = exports.AuthKeyError = exports.NotFoundError = exports.ForbiddenError = exports.UnauthorizedError = exports.BadRequestError = exports.InvalidDCError = exports.RPCError = void 0;
4
4
  const ts_custom_error_1 = require("ts-custom-error");
5
5
  class RPCError extends ts_custom_error_1.CustomError {
6
6
  constructor(message, request, code) {
@@ -107,19 +107,6 @@ class FloodError extends RPCError {
107
107
  }
108
108
  }
109
109
  exports.FloodError = FloodError;
110
- /**
111
- * The account is frozen and cannot use this method.
112
- * Clients should call help.getAppConfig to get freeze_since_date,
113
- * freeze_until_date, and freeze_appeal_url.
114
- */
115
- class FrozenError extends RPCError {
116
- constructor() {
117
- super(...arguments);
118
- this.code = 420;
119
- this.errorMessage = "FROZEN";
120
- }
121
- }
122
- exports.FrozenError = FrozenError;
123
110
  /**
124
111
  * An internal server error occurred while a request was being processed
125
112
  * for example, there was a disruption while accessing a database or file