@mtcute/core 0.25.3 → 0.25.5

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.
@@ -24,39 +24,122 @@ import { InputStringSessionData } from './utils/string-session.js';
24
24
  */
25
25
  export type ConnectionState = 'offline' | 'connecting' | 'updating' | 'connected';
26
26
  export interface ITelegramClient {
27
+ /** Logger for the client */
27
28
  readonly log: Logger;
29
+ /** Storage manager */
28
30
  readonly storage: PublicPart<TelegramStorageManager>;
31
+ /** App config manager */
29
32
  readonly appConfig: PublicPart<AppConfigManager>;
33
+ /** Timers manager */
30
34
  readonly timers: Pick<TimersManager, 'create' | 'cancel' | 'exists'>;
35
+ /** Signal that will be aborted when the client is destroyed */
31
36
  readonly stopSignal: AbortSignal;
37
+ /** Platform used by the client */
32
38
  readonly platform: ICorePlatform;
39
+ /**
40
+ * **ADVANCED**
41
+ *
42
+ * Do all the preparations, but don't connect just yet.
43
+ * Useful when you want to do some preparations before
44
+ * connecting, like setting up session.
45
+ *
46
+ * Call {@link connect} to actually connect.
47
+ */
33
48
  prepare(): Promise<void>;
49
+ /**
50
+ * Initialize the connection to the primary DC.
51
+ *
52
+ * You shouldn't usually call this method directly as it is called
53
+ * implicitly the first time you call {@link call}.
54
+ */
34
55
  connect(): Promise<void>;
56
+ /**
57
+ * Terminate any connections to the Telegram servers, but keep the client usable
58
+ */
35
59
  disconnect(): Promise<void>;
60
+ /**
61
+ * Destroy the client and all its resources.
62
+ *
63
+ * This will terminate any connections to the Telegram servers,
64
+ * free all the resources, and make the client no longer usable
65
+ */
36
66
  destroy(): Promise<void>;
67
+ /** Notify the client that the user has logged in */
37
68
  notifyLoggedIn(auth: tl.auth.TypeAuthorization | tl.RawUser): Promise<tl.RawUser>;
69
+ /** Notify the client that the user has logged out */
38
70
  notifyLoggedOut(): Promise<void>;
71
+ /** Notify the client that a channel has been opened */
39
72
  notifyChannelOpened(channelId: number, pts?: number): Promise<boolean>;
73
+ /** Notify the client that a channel has been closed */
40
74
  notifyChannelClosed(channelId: number): Promise<boolean>;
75
+ /** Start the updates loop */
41
76
  startUpdatesLoop(): Promise<void>;
77
+ /** Stop the updates loop */
42
78
  stopUpdatesLoop(): Promise<void>;
79
+ /** Call an RPC method */
43
80
  call<T extends tl.RpcMethod>(message: MustEqual<T, tl.RpcMethod>, params?: RpcCallOptions): Promise<tl.RpcCallReturn[T['_']]>;
81
+ /**
82
+ * Import the session from the given session string.
83
+ *
84
+ * Note that the session will only be imported in case
85
+ * the storage is missing authorization (i.e. does not contain
86
+ * auth key for the primary DC), otherwise it will be ignored (unless `force`).
87
+ *
88
+ * @param session Session string to import
89
+ * @param force Whether to overwrite existing session
90
+ */
44
91
  importSession(session: string | InputStringSessionData, force?: boolean): Promise<void>;
92
+ /**
93
+ * Export current session to a single *LONG* string, containing
94
+ * all the needed information.
95
+ *
96
+ * > **Warning!** Anyone with this string will be able
97
+ * > to authorize as you and do anything. Treat this
98
+ * > as your password, and never give it away!
99
+ * >
100
+ * > In case you have accidentally leaked this string,
101
+ * > make sure to revoke this session in account settings:
102
+ * > "Privacy & Security" > "Active sessions" >
103
+ * > find the one containing `mtcute` > Revoke,
104
+ * > or, in case this is a bot, revoke bot token
105
+ * > with [@BotFather](//t.me/botfather)
106
+ */
45
107
  exportSession(): Promise<string>;
108
+ /**
109
+ * Handle an update sent by the server in response to an RPC call
110
+ *
111
+ * @param updates Updates to handle
112
+ * @param noDispatch Whether the updates inside should not be dispatched as events
113
+ */
46
114
  handleClientUpdate(updates: tl.TypeUpdates, noDispatch?: boolean): void;
115
+ /** Emitted when a low-level `Updates` updates is received */
47
116
  onServerUpdate: Emitter<tl.TypeUpdates>;
117
+ /** Emitted when an update is received from the server. Requires updates loop to be running */
48
118
  onRawUpdate: Emitter<RawUpdateInfo>;
119
+ /** Emitted when the connection state changes */
49
120
  onConnectionState: Emitter<ConnectionState>;
121
+ /** Emitted when an error occurs */
50
122
  onError: Emitter<Error>;
123
+ /** Get the API credentials for use in authorization methods */
51
124
  getApiCredentials(): Promise<{
52
125
  id: number;
53
126
  hash: string;
54
127
  }>;
128
+ /** Get the number of connections of the given kind */
55
129
  getPoolSize(kind: ConnectionKind, dcId?: number): Promise<number>;
130
+ /** Get the primary DC ID */
56
131
  getPrimaryDcId(): Promise<number>;
132
+ /** Change the primary DC */
57
133
  changePrimaryDc(newDc: number): Promise<void>;
134
+ /** Compute SRP parameters for the given password */
58
135
  computeSrpParams(request: tl.account.RawPassword, password: string): Promise<tl.RawInputCheckPasswordSRP>;
136
+ /** Compute new password hash for the given algorithm and password */
59
137
  computeNewPasswordHash(algo: tl.TypePasswordKdfAlgo, password: string): Promise<Uint8Array>;
138
+ /** Generate a new time-based MTProto message ID */
60
139
  getMtprotoMessageId(): Promise<Long>;
140
+ /**
141
+ * **ADVANCED**
142
+ * Recreate the given DC, forcefully using the IP taken from server config.
143
+ */
61
144
  recreateDc(dcId: number): Promise<void>;
62
145
  }
@@ -24,39 +24,122 @@ import { InputStringSessionData } from './utils/string-session.js';
24
24
  */
25
25
  export type ConnectionState = 'offline' | 'connecting' | 'updating' | 'connected';
26
26
  export interface ITelegramClient {
27
+ /** Logger for the client */
27
28
  readonly log: Logger;
29
+ /** Storage manager */
28
30
  readonly storage: PublicPart<TelegramStorageManager>;
31
+ /** App config manager */
29
32
  readonly appConfig: PublicPart<AppConfigManager>;
33
+ /** Timers manager */
30
34
  readonly timers: Pick<TimersManager, 'create' | 'cancel' | 'exists'>;
35
+ /** Signal that will be aborted when the client is destroyed */
31
36
  readonly stopSignal: AbortSignal;
37
+ /** Platform used by the client */
32
38
  readonly platform: ICorePlatform;
39
+ /**
40
+ * **ADVANCED**
41
+ *
42
+ * Do all the preparations, but don't connect just yet.
43
+ * Useful when you want to do some preparations before
44
+ * connecting, like setting up session.
45
+ *
46
+ * Call {@link connect} to actually connect.
47
+ */
33
48
  prepare(): Promise<void>;
49
+ /**
50
+ * Initialize the connection to the primary DC.
51
+ *
52
+ * You shouldn't usually call this method directly as it is called
53
+ * implicitly the first time you call {@link call}.
54
+ */
34
55
  connect(): Promise<void>;
56
+ /**
57
+ * Terminate any connections to the Telegram servers, but keep the client usable
58
+ */
35
59
  disconnect(): Promise<void>;
60
+ /**
61
+ * Destroy the client and all its resources.
62
+ *
63
+ * This will terminate any connections to the Telegram servers,
64
+ * free all the resources, and make the client no longer usable
65
+ */
36
66
  destroy(): Promise<void>;
67
+ /** Notify the client that the user has logged in */
37
68
  notifyLoggedIn(auth: tl.auth.TypeAuthorization | tl.RawUser): Promise<tl.RawUser>;
69
+ /** Notify the client that the user has logged out */
38
70
  notifyLoggedOut(): Promise<void>;
71
+ /** Notify the client that a channel has been opened */
39
72
  notifyChannelOpened(channelId: number, pts?: number): Promise<boolean>;
73
+ /** Notify the client that a channel has been closed */
40
74
  notifyChannelClosed(channelId: number): Promise<boolean>;
75
+ /** Start the updates loop */
41
76
  startUpdatesLoop(): Promise<void>;
77
+ /** Stop the updates loop */
42
78
  stopUpdatesLoop(): Promise<void>;
79
+ /** Call an RPC method */
43
80
  call<T extends tl.RpcMethod>(message: MustEqual<T, tl.RpcMethod>, params?: RpcCallOptions): Promise<tl.RpcCallReturn[T['_']]>;
81
+ /**
82
+ * Import the session from the given session string.
83
+ *
84
+ * Note that the session will only be imported in case
85
+ * the storage is missing authorization (i.e. does not contain
86
+ * auth key for the primary DC), otherwise it will be ignored (unless `force`).
87
+ *
88
+ * @param session Session string to import
89
+ * @param force Whether to overwrite existing session
90
+ */
44
91
  importSession(session: string | InputStringSessionData, force?: boolean): Promise<void>;
92
+ /**
93
+ * Export current session to a single *LONG* string, containing
94
+ * all the needed information.
95
+ *
96
+ * > **Warning!** Anyone with this string will be able
97
+ * > to authorize as you and do anything. Treat this
98
+ * > as your password, and never give it away!
99
+ * >
100
+ * > In case you have accidentally leaked this string,
101
+ * > make sure to revoke this session in account settings:
102
+ * > "Privacy & Security" > "Active sessions" >
103
+ * > find the one containing `mtcute` > Revoke,
104
+ * > or, in case this is a bot, revoke bot token
105
+ * > with [@BotFather](//t.me/botfather)
106
+ */
45
107
  exportSession(): Promise<string>;
108
+ /**
109
+ * Handle an update sent by the server in response to an RPC call
110
+ *
111
+ * @param updates Updates to handle
112
+ * @param noDispatch Whether the updates inside should not be dispatched as events
113
+ */
46
114
  handleClientUpdate(updates: tl.TypeUpdates, noDispatch?: boolean): void;
115
+ /** Emitted when a low-level `Updates` updates is received */
47
116
  onServerUpdate: Emitter<tl.TypeUpdates>;
117
+ /** Emitted when an update is received from the server. Requires updates loop to be running */
48
118
  onRawUpdate: Emitter<RawUpdateInfo>;
119
+ /** Emitted when the connection state changes */
49
120
  onConnectionState: Emitter<ConnectionState>;
121
+ /** Emitted when an error occurs */
50
122
  onError: Emitter<Error>;
123
+ /** Get the API credentials for use in authorization methods */
51
124
  getApiCredentials(): Promise<{
52
125
  id: number;
53
126
  hash: string;
54
127
  }>;
128
+ /** Get the number of connections of the given kind */
55
129
  getPoolSize(kind: ConnectionKind, dcId?: number): Promise<number>;
130
+ /** Get the primary DC ID */
56
131
  getPrimaryDcId(): Promise<number>;
132
+ /** Change the primary DC */
57
133
  changePrimaryDc(newDc: number): Promise<void>;
134
+ /** Compute SRP parameters for the given password */
58
135
  computeSrpParams(request: tl.account.RawPassword, password: string): Promise<tl.RawInputCheckPasswordSRP>;
136
+ /** Compute new password hash for the given algorithm and password */
59
137
  computeNewPasswordHash(algo: tl.TypePasswordKdfAlgo, password: string): Promise<Uint8Array>;
138
+ /** Generate a new time-based MTProto message ID */
60
139
  getMtprotoMessageId(): Promise<Long>;
140
+ /**
141
+ * **ADVANCED**
142
+ * Recreate the given DC, forcefully using the IP taken from server config.
143
+ */
61
144
  recreateDc(dcId: number): Promise<void>;
62
145
  }
@@ -133,32 +133,40 @@ async function resolvePeer(client, peerId, force = false) {
133
133
  }
134
134
  switch (peerType) {
135
135
  case "user": {
136
- const res = await batchedQueries._getUsersBatched(client, {
137
- _: "inputUser",
138
- userId: bareId,
139
- accessHash: Long.ZERO
140
- });
141
- if (res != null && res._ === "user" && res.accessHash != null) {
142
- return {
143
- _: "inputPeerUser",
136
+ try {
137
+ const res = await batchedQueries._getUsersBatched(client, {
138
+ _: "inputUser",
144
139
  userId: bareId,
145
- accessHash: res.accessHash
146
- };
140
+ accessHash: Long.ZERO
141
+ });
142
+ if (res != null && res._ === "user" && res.accessHash != null) {
143
+ return {
144
+ _: "inputPeerUser",
145
+ userId: bareId,
146
+ accessHash: res.accessHash
147
+ };
148
+ }
149
+ } catch (e) {
150
+ if (!tl.tl.RpcError.is(e, "USER_INVALID")) throw e;
147
151
  }
148
152
  break;
149
153
  }
150
154
  case "channel": {
151
- const res = await batchedQueries._getChannelsBatched(client, {
152
- _: "inputChannel",
153
- channelId: bareId,
154
- accessHash: Long.ZERO
155
- });
156
- if (res != null && res._ === "channel" && res.accessHash != null) {
157
- return {
158
- _: "inputPeerChannel",
155
+ try {
156
+ const res = await batchedQueries._getChannelsBatched(client, {
157
+ _: "inputChannel",
159
158
  channelId: bareId,
160
- accessHash: res.accessHash
161
- };
159
+ accessHash: Long.ZERO
160
+ });
161
+ if (res != null && res._ === "channel" && res.accessHash != null) {
162
+ return {
163
+ _: "inputPeerChannel",
164
+ channelId: bareId,
165
+ accessHash: res.accessHash
166
+ };
167
+ }
168
+ } catch (e) {
169
+ if (!tl.tl.RpcError.is(e, "CHANNEL_INVALID")) throw e;
162
170
  }
163
171
  break;
164
172
  }
@@ -126,32 +126,40 @@ async function resolvePeer(client, peerId, force = false) {
126
126
  }
127
127
  switch (peerType) {
128
128
  case "user": {
129
- const res = await _getUsersBatched(client, {
130
- _: "inputUser",
131
- userId: bareId,
132
- accessHash: Long.ZERO
133
- });
134
- if (res != null && res._ === "user" && res.accessHash != null) {
135
- return {
136
- _: "inputPeerUser",
129
+ try {
130
+ const res = await _getUsersBatched(client, {
131
+ _: "inputUser",
137
132
  userId: bareId,
138
- accessHash: res.accessHash
139
- };
133
+ accessHash: Long.ZERO
134
+ });
135
+ if (res != null && res._ === "user" && res.accessHash != null) {
136
+ return {
137
+ _: "inputPeerUser",
138
+ userId: bareId,
139
+ accessHash: res.accessHash
140
+ };
141
+ }
142
+ } catch (e) {
143
+ if (!tl.RpcError.is(e, "USER_INVALID")) throw e;
140
144
  }
141
145
  break;
142
146
  }
143
147
  case "channel": {
144
- const res = await _getChannelsBatched(client, {
145
- _: "inputChannel",
146
- channelId: bareId,
147
- accessHash: Long.ZERO
148
- });
149
- if (res != null && res._ === "channel" && res.accessHash != null) {
150
- return {
151
- _: "inputPeerChannel",
148
+ try {
149
+ const res = await _getChannelsBatched(client, {
150
+ _: "inputChannel",
152
151
  channelId: bareId,
153
- accessHash: res.accessHash
154
- };
152
+ accessHash: Long.ZERO
153
+ });
154
+ if (res != null && res._ === "channel" && res.accessHash != null) {
155
+ return {
156
+ _: "inputPeerChannel",
157
+ channelId: bareId,
158
+ accessHash: res.accessHash
159
+ };
160
+ }
161
+ } catch (e) {
162
+ if (!tl.RpcError.is(e, "CHANNEL_INVALID")) throw e;
155
163
  }
156
164
  break;
157
165
  }
@@ -219,9 +219,9 @@ class UpdatesManager {
219
219
  }
220
220
  if (this.pendingUpdateContainers.length % WARN_EVERY === 0) {
221
221
  if (this.updatesLoopActive) {
222
- this.log.warn("%d pending update containers, but the updates loop is not active. did you forget to start it?", this.pendingUpdateContainers.length);
223
- } else {
224
222
  this.log.warn("%d pending update containers, can't keep up!", this.pendingUpdateContainers.length);
223
+ } else {
224
+ this.log.warn("%d pending update containers, but the updates loop is not active. did you forget to start it?", this.pendingUpdateContainers.length);
225
225
  }
226
226
  }
227
227
  this.updatesLoopCv.notify();
@@ -212,9 +212,9 @@ class UpdatesManager {
212
212
  }
213
213
  if (this.pendingUpdateContainers.length % WARN_EVERY === 0) {
214
214
  if (this.updatesLoopActive) {
215
- this.log.warn("%d pending update containers, but the updates loop is not active. did you forget to start it?", this.pendingUpdateContainers.length);
216
- } else {
217
215
  this.log.warn("%d pending update containers, can't keep up!", this.pendingUpdateContainers.length);
216
+ } else {
217
+ this.log.warn("%d pending update containers, but the updates loop is not active. did you forget to start it?", this.pendingUpdateContainers.length);
218
218
  }
219
219
  }
220
220
  this.updatesLoopCv.notify();
@@ -225,7 +225,7 @@ class NetworkManager {
225
225
  _: "initConnection",
226
226
  deviceModel,
227
227
  systemVersion: "1.0",
228
- appVersion: "0.25.3",
228
+ appVersion: "0.25.5",
229
229
  systemLangCode: "en",
230
230
  langPack: "",
231
231
  // "langPacks are for official apps only"
@@ -218,7 +218,7 @@ class NetworkManager {
218
218
  _: "initConnection",
219
219
  deviceModel,
220
220
  systemVersion: "1.0",
221
- appVersion: "0.25.3",
221
+ appVersion: "0.25.5",
222
222
  systemLangCode: "en",
223
223
  langPack: "",
224
224
  // "langPacks are for official apps only"
@@ -536,8 +536,9 @@ class SessionConnection extends persistentConnection.PersistentConnection {
536
536
  this.log.error("received rpc_result for %s request %l", msg._, reqMsgId);
537
537
  return;
538
538
  }
539
- longUtils.removeFromLongArray(this._session.queuedStateReq, reqMsgId);
540
539
  const rpc = msg.rpc;
540
+ longUtils.removeFromLongArray(this._session.queuedStateReq, reqMsgId);
541
+ this._session.getStateSchedule.remove(rpc);
541
542
  rpc.resetAbortSignal?.();
542
543
  const resultConstructorId = message.peekUint();
543
544
  let result;
@@ -660,7 +661,6 @@ class SessionConnection extends persistentConnection.PersistentConnection {
660
661
  this._onMessageAcked(rpc.containerId);
661
662
  }
662
663
  longUtils.removeFromLongArray(this._session.queuedResendReq, msgId);
663
- this._session.getStateSchedule.remove(rpc);
664
664
  break;
665
665
  }
666
666
  case "bind":
@@ -529,8 +529,9 @@ class SessionConnection extends PersistentConnection {
529
529
  this.log.error("received rpc_result for %s request %l", msg._, reqMsgId);
530
530
  return;
531
531
  }
532
- removeFromLongArray(this._session.queuedStateReq, reqMsgId);
533
532
  const rpc = msg.rpc;
533
+ removeFromLongArray(this._session.queuedStateReq, reqMsgId);
534
+ this._session.getStateSchedule.remove(rpc);
534
535
  rpc.resetAbortSignal?.();
535
536
  const resultConstructorId = message.peekUint();
536
537
  let result;
@@ -653,7 +654,6 @@ class SessionConnection extends PersistentConnection {
653
654
  this._onMessageAcked(rpc.containerId);
654
655
  }
655
656
  removeFromLongArray(this._session.queuedResendReq, msgId);
656
- this._session.getStateSchedule.remove(rpc);
657
657
  break;
658
658
  }
659
659
  case "bind":
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mtcute/core",
3
3
  "type": "module",
4
- "version": "0.25.3",
4
+ "version": "0.25.5",
5
5
  "description": "Type-safe library for MTProto (Telegram API)",
6
6
  "license": "MIT",
7
7
  "scripts": {},
@@ -10,7 +10,7 @@
10
10
  "@fuman/net": "0.0.15",
11
11
  "@fuman/utils": "0.0.15",
12
12
  "@mtcute/file-id": "^0.24.3",
13
- "@mtcute/tl": "^208.0.0",
13
+ "@mtcute/tl": "^209.0.0",
14
14
  "@mtcute/tl-runtime": "^0.24.3",
15
15
  "@types/events": "3.0.0",
16
16
  "long": "5.2.3"