teleproto 1.214.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.
Files changed (222) hide show
  1. package/CryptoFile.d.ts +2 -0
  2. package/CryptoFile.js +37 -0
  3. package/Helpers.d.ts +150 -0
  4. package/Helpers.js +477 -0
  5. package/LICENSE +23 -0
  6. package/Password.d.ts +14 -0
  7. package/Password.js +270 -0
  8. package/README.md +98 -0
  9. package/Utils.d.ts +227 -0
  10. package/Utils.js +1247 -0
  11. package/Version.d.ts +1 -0
  12. package/Version.js +4 -0
  13. package/client/2fa.d.ts +48 -0
  14. package/client/2fa.js +108 -0
  15. package/client/TelegramClient.d.ts +1043 -0
  16. package/client/TelegramClient.js +1181 -0
  17. package/client/account.d.ts +0 -0
  18. package/client/account.js +1 -0
  19. package/client/auth.d.ts +92 -0
  20. package/client/auth.js +388 -0
  21. package/client/bots.d.ts +6 -0
  22. package/client/bots.js +23 -0
  23. package/client/buttons.d.ts +4 -0
  24. package/client/buttons.js +86 -0
  25. package/client/chats.d.ts +45 -0
  26. package/client/chats.js +350 -0
  27. package/client/dialogs.d.ts +52 -0
  28. package/client/dialogs.js +174 -0
  29. package/client/downloads.d.ts +156 -0
  30. package/client/downloads.js +614 -0
  31. package/client/fs.d.ts +1 -0
  32. package/client/fs.js +17 -0
  33. package/client/index.d.ts +15 -0
  34. package/client/index.js +64 -0
  35. package/client/messageParse.d.ts +17 -0
  36. package/client/messageParse.js +191 -0
  37. package/client/messages.d.ts +263 -0
  38. package/client/messages.js +813 -0
  39. package/client/os.d.ts +2 -0
  40. package/client/os.js +37 -0
  41. package/client/path.d.ts +2 -0
  42. package/client/path.js +7 -0
  43. package/client/telegramBaseClient.d.ts +237 -0
  44. package/client/telegramBaseClient.js +368 -0
  45. package/client/updates.d.ts +33 -0
  46. package/client/updates.js +260 -0
  47. package/client/uploads.d.ts +136 -0
  48. package/client/uploads.js +514 -0
  49. package/client/users.d.ts +29 -0
  50. package/client/users.js +490 -0
  51. package/crypto/AuthKey.d.ts +19 -0
  52. package/crypto/AuthKey.js +76 -0
  53. package/crypto/CTR.d.ts +5 -0
  54. package/crypto/CTR.js +16 -0
  55. package/crypto/Factorizator.d.ts +19 -0
  56. package/crypto/Factorizator.js +74 -0
  57. package/crypto/IGE.d.ts +11 -0
  58. package/crypto/IGE.js +115 -0
  59. package/crypto/RSA.d.ts +14 -0
  60. package/crypto/RSA.js +70 -0
  61. package/crypto/converters.d.ts +19 -0
  62. package/crypto/converters.js +52 -0
  63. package/crypto/crypto.d.ts +19 -0
  64. package/crypto/crypto.js +58 -0
  65. package/crypto/words.d.ts +6 -0
  66. package/crypto/words.js +47 -0
  67. package/define.d.ts +77 -0
  68. package/entityCache.d.ts +7 -0
  69. package/entityCache.js +79 -0
  70. package/errors/Common.d.ts +71 -0
  71. package/errors/Common.js +122 -0
  72. package/errors/RPCBaseErrors.d.ts +84 -0
  73. package/errors/RPCBaseErrors.js +134 -0
  74. package/errors/RPCErrorList.d.ts +37 -0
  75. package/errors/RPCErrorList.js +117 -0
  76. package/errors/index.d.ts +11 -0
  77. package/errors/index.js +32 -0
  78. package/events/Album.d.ts +36 -0
  79. package/events/Album.js +104 -0
  80. package/events/CallbackQuery.d.ts +73 -0
  81. package/events/CallbackQuery.js +193 -0
  82. package/events/DeletedMessage.d.ts +45 -0
  83. package/events/DeletedMessage.js +63 -0
  84. package/events/EditedMessage.d.ts +30 -0
  85. package/events/EditedMessage.js +41 -0
  86. package/events/NewMessage.d.ts +89 -0
  87. package/events/NewMessage.js +182 -0
  88. package/events/Raw.d.ts +29 -0
  89. package/events/Raw.js +43 -0
  90. package/events/common.d.ts +66 -0
  91. package/events/common.js +141 -0
  92. package/events/index.d.ts +2 -0
  93. package/events/index.js +8 -0
  94. package/extensions/AsyncQueue.d.ts +10 -0
  95. package/extensions/AsyncQueue.js +32 -0
  96. package/extensions/BinaryReader.d.ts +99 -0
  97. package/extensions/BinaryReader.js +242 -0
  98. package/extensions/BinaryWriter.d.ts +6 -0
  99. package/extensions/BinaryWriter.js +15 -0
  100. package/extensions/Deferred.d.ts +8 -0
  101. package/extensions/Deferred.js +16 -0
  102. package/extensions/Logger.d.ts +54 -0
  103. package/extensions/Logger.js +128 -0
  104. package/extensions/MessagePacker.d.ts +21 -0
  105. package/extensions/MessagePacker.js +154 -0
  106. package/extensions/PendingState.d.ts +12 -0
  107. package/extensions/PendingState.js +29 -0
  108. package/extensions/PromisedNetSockets.d.ts +24 -0
  109. package/extensions/PromisedNetSockets.js +192 -0
  110. package/extensions/PromisedWebSockets.d.ts +18 -0
  111. package/extensions/PromisedWebSockets.js +124 -0
  112. package/extensions/html.d.ts +5 -0
  113. package/extensions/html.js +228 -0
  114. package/extensions/index.d.ts +7 -0
  115. package/extensions/index.js +17 -0
  116. package/extensions/markdown.d.ts +5 -0
  117. package/extensions/markdown.js +76 -0
  118. package/extensions/markdownv2.d.ts +5 -0
  119. package/extensions/markdownv2.js +51 -0
  120. package/extensions/net.d.ts +1 -0
  121. package/extensions/net.js +17 -0
  122. package/extensions/socks.d.ts +1 -0
  123. package/extensions/socks.js +17 -0
  124. package/index.d.ts +14 -0
  125. package/index.js +62 -0
  126. package/inspect.d.ts +1 -0
  127. package/inspect.js +5 -0
  128. package/network/Authenticator.d.ts +12 -0
  129. package/network/Authenticator.js +192 -0
  130. package/network/MTProtoPlainSender.d.ts +19 -0
  131. package/network/MTProtoPlainSender.js +74 -0
  132. package/network/MTProtoSender.d.ts +290 -0
  133. package/network/MTProtoSender.js +873 -0
  134. package/network/MTProtoState.d.ts +102 -0
  135. package/network/MTProtoState.js +267 -0
  136. package/network/RequestState.d.ts +18 -0
  137. package/network/RequestState.js +35 -0
  138. package/network/connection/Connection.d.ts +69 -0
  139. package/network/connection/Connection.js +162 -0
  140. package/network/connection/TCPAbridged.d.ts +19 -0
  141. package/network/connection/TCPAbridged.js +58 -0
  142. package/network/connection/TCPFull.d.ts +16 -0
  143. package/network/connection/TCPFull.js +61 -0
  144. package/network/connection/TCPMTProxy.d.ts +49 -0
  145. package/network/connection/TCPMTProxy.js +121 -0
  146. package/network/connection/TCPObfuscated.d.ts +18 -0
  147. package/network/connection/TCPObfuscated.js +78 -0
  148. package/network/connection/index.d.ts +4 -0
  149. package/network/connection/index.js +11 -0
  150. package/network/index.d.ts +11 -0
  151. package/network/index.js +23 -0
  152. package/package.json +50 -0
  153. package/requestIter.d.ts +24 -0
  154. package/requestIter.js +109 -0
  155. package/sessions/Abstract.d.ts +103 -0
  156. package/sessions/Abstract.js +6 -0
  157. package/sessions/Memory.d.ts +38 -0
  158. package/sessions/Memory.js +272 -0
  159. package/sessions/StoreSession.d.ts +14 -0
  160. package/sessions/StoreSession.js +77 -0
  161. package/sessions/StringSession.d.ts +32 -0
  162. package/sessions/StringSession.js +116 -0
  163. package/sessions/index.d.ts +4 -0
  164. package/sessions/index.js +11 -0
  165. package/sessions/localStorage.d.ts +1 -0
  166. package/sessions/localStorage.js +4 -0
  167. package/tl/AllTLObjects.d.ts +3 -0
  168. package/tl/AllTLObjects.js +17 -0
  169. package/tl/MTProtoRequest.d.ts +18 -0
  170. package/tl/MTProtoRequest.js +38 -0
  171. package/tl/api.d.ts +32488 -0
  172. package/tl/api.js +507 -0
  173. package/tl/apiTl.d.ts +2 -0
  174. package/tl/apiTl.js +2209 -0
  175. package/tl/core/GZIPPacked.d.ts +15 -0
  176. package/tl/core/GZIPPacked.js +51 -0
  177. package/tl/core/MessageContainer.d.ts +12 -0
  178. package/tl/core/MessageContainer.js +42 -0
  179. package/tl/core/RPCResult.d.ts +14 -0
  180. package/tl/core/RPCResult.js +32 -0
  181. package/tl/core/TLMessage.d.ts +10 -0
  182. package/tl/core/TLMessage.js +14 -0
  183. package/tl/core/index.d.ts +6 -0
  184. package/tl/core/index.js +16 -0
  185. package/tl/custom/button.d.ts +24 -0
  186. package/tl/custom/button.js +78 -0
  187. package/tl/custom/chatGetter.d.ts +29 -0
  188. package/tl/custom/chatGetter.js +116 -0
  189. package/tl/custom/dialog.d.ts +30 -0
  190. package/tl/custom/dialog.js +40 -0
  191. package/tl/custom/draft.d.ts +21 -0
  192. package/tl/custom/draft.js +48 -0
  193. package/tl/custom/file.d.ts +21 -0
  194. package/tl/custom/file.js +68 -0
  195. package/tl/custom/forward.d.ts +15 -0
  196. package/tl/custom/forward.js +47 -0
  197. package/tl/custom/index.d.ts +1 -0
  198. package/tl/custom/index.js +5 -0
  199. package/tl/custom/inlineResult.d.ts +32 -0
  200. package/tl/custom/inlineResult.js +87 -0
  201. package/tl/custom/inlineResults.d.ts +20 -0
  202. package/tl/custom/inlineResults.js +26 -0
  203. package/tl/custom/message.d.ts +427 -0
  204. package/tl/custom/message.js +716 -0
  205. package/tl/custom/messageButton.d.ts +54 -0
  206. package/tl/custom/messageButton.js +152 -0
  207. package/tl/custom/senderGetter.d.ts +28 -0
  208. package/tl/custom/senderGetter.js +55 -0
  209. package/tl/generateModule.d.ts +1 -0
  210. package/tl/generateModule.js +17 -0
  211. package/tl/generationHelpers.d.ts +11 -0
  212. package/tl/generationHelpers.js +289 -0
  213. package/tl/index.d.ts +3 -0
  214. package/tl/index.js +10 -0
  215. package/tl/patched/index.d.ts +2 -0
  216. package/tl/patched/index.js +76 -0
  217. package/tl/schemaTl.d.ts +2 -0
  218. package/tl/schemaTl.js +64 -0
  219. package/tl/types-generator/generate.d.ts +1 -0
  220. package/tl/types-generator/generate.js +84 -0
  221. package/tl/types-generator/template.d.ts +6 -0
  222. package/tl/types-generator/template.js +257 -0
@@ -0,0 +1,873 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MTProtoSender = void 0;
7
+ /**
8
+ * MTProto Mobile Protocol sender
9
+ * (https://core.telegram.org/mtproto/description)
10
+ * This class is responsible for wrapping requests into `TLMessage`'s,
11
+ * sending them over the network and receiving them in a safe manner.
12
+ *
13
+ * Automatic reconnection due to temporary network issues is a concern
14
+ * for this class as well, including retry of messages that could not
15
+ * be sent successfully.
16
+ *
17
+ * A new authorization key will be generated on connection if no other
18
+ * key exists yet.
19
+ */
20
+ const AuthKey_1 = require("../crypto/AuthKey");
21
+ const MTProtoState_1 = require("./MTProtoState");
22
+ const extensions_1 = require("../extensions");
23
+ const core_1 = require("../tl/core");
24
+ const tl_1 = require("../tl");
25
+ const big_integer_1 = __importDefault(require("big-integer"));
26
+ const Helpers_1 = require("../Helpers");
27
+ const RequestState_1 = require("./RequestState");
28
+ const Authenticator_1 = require("./Authenticator");
29
+ const MTProtoPlainSender_1 = require("./MTProtoPlainSender");
30
+ const errors_1 = require("../errors");
31
+ const _1 = require(".");
32
+ const Logger_1 = require("../extensions/Logger");
33
+ const async_mutex_1 = require("async-mutex");
34
+ const PendingState_1 = require("../extensions/PendingState");
35
+ var MsgsAck = tl_1.Api.MsgsAck;
36
+ class MTProtoSender {
37
+ /**
38
+ * @param authKey
39
+ * @param opts
40
+ */
41
+ constructor(authKey, opts) {
42
+ this._exportedSenderPromises = new Map();
43
+ const args = Object.assign(Object.assign({}, MTProtoSender.DEFAULT_OPTIONS), opts);
44
+ this._finishedConnecting = false;
45
+ this._cancelSend = false;
46
+ this._abortController = new AbortController();
47
+ this._connection = undefined;
48
+ this._log = args.logger;
49
+ this._dcId = args.dcId;
50
+ this._retries = args.retries;
51
+ this._currentRetries = 0;
52
+ this._reconnectRetries = args.reconnectRetries;
53
+ this._delay = args.delay;
54
+ this._autoReconnect = args.autoReconnect;
55
+ this._connectTimeout = args.connectTimeout;
56
+ this._authKeyCallback = args.authKeyCallback;
57
+ this._updateCallback = args.updateCallback;
58
+ this._autoReconnectCallback = args.autoReconnectCallback;
59
+ this._isMainSender = args.isMainSender;
60
+ this._senderCallback = args.senderCallback;
61
+ this._client = args.client;
62
+ this._onConnectionBreak = args.onConnectionBreak;
63
+ this._securityChecks = args.securityChecks;
64
+ this._connectMutex = new async_mutex_1.Mutex();
65
+ this._exportedSenderPromises = args._exportedSenderPromises;
66
+ /**
67
+ * whether we disconnected ourself or telegram did it.
68
+ */
69
+ this.userDisconnected = false;
70
+ /**
71
+ * If a disconnection happens for any other reason and it
72
+ * was *not* user action then the pending messages won't
73
+ * be cleared but on explicit user disconnection all the
74
+ * pending futures should be cancelled.
75
+ */
76
+ this.isConnecting = false;
77
+ this._authenticated = false;
78
+ this._userConnected = false;
79
+ this.isReconnecting = false;
80
+ this._reconnecting = false;
81
+ this._disconnected = true;
82
+ /**
83
+ * We need to join the loops upon disconnection
84
+ */
85
+ this._sendLoopHandle = null;
86
+ this._recvLoopHandle = null;
87
+ /**
88
+ * Preserving the references of the AuthKey and state is important
89
+ */
90
+ this.authKey = authKey || new AuthKey_1.AuthKey();
91
+ this._state = new MTProtoState_1.MTProtoState(this.authKey, this._log, this._securityChecks);
92
+ /**
93
+ * Outgoing messages are put in a queue and sent in a batch.
94
+ * Note that here we're also storing their ``_RequestState``.
95
+ */
96
+ this._sendQueue = new extensions_1.MessagePacker(this._state, this._log);
97
+ /**
98
+ * Sent states are remembered until a response is received.
99
+ */
100
+ this._pendingState = new PendingState_1.PendingState();
101
+ /**
102
+ * Responses must be acknowledged, and we can also batch these.
103
+ */
104
+ this._pendingAck = new Set();
105
+ /**
106
+ * Similar to pending_messages but only for the last acknowledges.
107
+ * These can't go in pending_messages because no acknowledge for them
108
+ * is received, but we may still need to resend their state on bad salts.
109
+ */
110
+ this._lastAcks = [];
111
+ /**
112
+ * Jump table from response ID to method that handles it
113
+ */
114
+ this._handlers = {
115
+ [core_1.RPCResult.CONSTRUCTOR_ID.toString()]: this._handleRPCResult.bind(this),
116
+ [core_1.MessageContainer.CONSTRUCTOR_ID.toString()]: this._handleContainer.bind(this),
117
+ [core_1.GZIPPacked.CONSTRUCTOR_ID.toString()]: this._handleGzipPacked.bind(this),
118
+ [tl_1.Api.Pong.CONSTRUCTOR_ID.toString()]: this._handlePong.bind(this),
119
+ [tl_1.Api.BadServerSalt.CONSTRUCTOR_ID.toString()]: this._handleBadServerSalt.bind(this),
120
+ [tl_1.Api.BadMsgNotification.CONSTRUCTOR_ID.toString()]: this._handleBadNotification.bind(this),
121
+ [tl_1.Api.MsgDetailedInfo.CONSTRUCTOR_ID.toString()]: this._handleDetailedInfo.bind(this),
122
+ [tl_1.Api.MsgNewDetailedInfo.CONSTRUCTOR_ID.toString()]: this._handleNewDetailedInfo.bind(this),
123
+ [tl_1.Api.NewSessionCreated.CONSTRUCTOR_ID.toString()]: this._handleNewSessionCreated.bind(this),
124
+ [tl_1.Api.MsgsAck.CONSTRUCTOR_ID.toString()]: this._handleAck.bind(this),
125
+ [tl_1.Api.FutureSalts.CONSTRUCTOR_ID.toString()]: this._handleFutureSalts.bind(this),
126
+ [tl_1.Api.MsgsStateReq.CONSTRUCTOR_ID.toString()]: this._handleStateForgotten.bind(this),
127
+ [tl_1.Api.MsgResendReq.CONSTRUCTOR_ID.toString()]: this._handleStateForgotten.bind(this),
128
+ [tl_1.Api.MsgsAllInfo.CONSTRUCTOR_ID.toString()]: this._handleMsgAll.bind(this),
129
+ };
130
+ }
131
+ set dcId(dcId) {
132
+ this._dcId = dcId;
133
+ }
134
+ get dcId() {
135
+ return this._dcId;
136
+ }
137
+ // Public API
138
+ /**
139
+ * Connects to the specified given connection using the given auth key.
140
+ */
141
+ async connect(connection, force) {
142
+ this.userDisconnected = false;
143
+ if (this._userConnected && !force) {
144
+ this._log.info("User is already connected!");
145
+ return false;
146
+ }
147
+ this.isConnecting = true;
148
+ this._connection = connection;
149
+ this._finishedConnecting = false;
150
+ for (let attempt = 0; attempt < this._retries; attempt++) {
151
+ try {
152
+ await this._connect();
153
+ if (this._updateCallback) {
154
+ this._updateCallback(this._client, new _1.UpdateConnectionState(_1.UpdateConnectionState.connected));
155
+ }
156
+ this._finishedConnecting = true;
157
+ break;
158
+ }
159
+ catch (err) {
160
+ if (this._updateCallback && attempt === 0) {
161
+ this._updateCallback(this._client, new _1.UpdateConnectionState(_1.UpdateConnectionState.disconnected));
162
+ }
163
+ this._log.error(`WebSocket connection failed attempt: ${attempt + 1}`);
164
+ if (this._client._errorHandler) {
165
+ await this._client._errorHandler(err);
166
+ }
167
+ if (this._log.canSend(Logger_1.LogLevel.ERROR)) {
168
+ console.error(err);
169
+ }
170
+ await (0, Helpers_1.sleep)(this._delay);
171
+ }
172
+ }
173
+ this.isConnecting = false;
174
+ return this._finishedConnecting;
175
+ }
176
+ isConnected() {
177
+ return this._userConnected;
178
+ }
179
+ _transportConnected() {
180
+ return (!this._reconnecting &&
181
+ this._connection &&
182
+ this._connection._connected);
183
+ }
184
+ /**
185
+ * Cleanly disconnects the instance from the network, cancels
186
+ * all pending requests, and closes the send and receive loops.
187
+ */
188
+ async disconnect() {
189
+ this.userDisconnected = true;
190
+ this._log.warn("Disconnecting...");
191
+ await this._disconnect();
192
+ }
193
+ /**
194
+ *
195
+ This method enqueues the given request to be sent. Its send
196
+ state will be saved until a response arrives, and a ``Future``
197
+ that will be resolved when the response arrives will be returned:
198
+
199
+ .. code-block:: javascript
200
+
201
+ async def method():
202
+ # Sending (enqueued for the send loop)
203
+ future = sender.send(request)
204
+ # Receiving (waits for the receive loop to read the result)
205
+ result = await future
206
+
207
+ Designed like this because Telegram may send the response at
208
+ any point, and it can send other items while one waits for it.
209
+ Once the response for this future arrives, it is set with the
210
+ received result, quite similar to how a ``receive()`` call
211
+ would otherwise work.
212
+
213
+ Since the receiving part is "built in" the future, it's
214
+ impossible to await receive a result that was never sent.
215
+ * @param request
216
+ * @returns {RequestState}
217
+ */
218
+ send(request) {
219
+ const state = new RequestState_1.RequestState(request);
220
+ this._log.debug(`Send ${request.className}`);
221
+ this._sendQueue.append(state);
222
+ return state.promise;
223
+ }
224
+ addStateToQueue(state) {
225
+ this._sendQueue.append(state);
226
+ }
227
+ /**
228
+ * Performs the actual connection, retrying, generating the
229
+ * authorization key if necessary, and starting the send and
230
+ * receive loops.
231
+ * @returns {Promise<void>}
232
+ * @private
233
+ */
234
+ async _connect() {
235
+ const connection = this._connection;
236
+ if (!connection.isConnected()) {
237
+ this._log.info("Connecting to {0}...".replace("{0}", connection.toString()));
238
+ await connection.connect();
239
+ this._log.debug("Connection success!");
240
+ }
241
+ if (!this.authKey.getKey()) {
242
+ const plain = new MTProtoPlainSender_1.MTProtoPlainSender(connection, this._log);
243
+ this._log.debug("New auth_key attempt ...");
244
+ const res = await (0, Authenticator_1.doAuthentication)(plain, this._log);
245
+ this._log.debug("Generated new auth_key successfully");
246
+ await this.authKey.setKey(res.authKey);
247
+ this._state.timeOffset = res.timeOffset;
248
+ if (this._authKeyCallback) {
249
+ await this._authKeyCallback(this.authKey, this._dcId);
250
+ }
251
+ }
252
+ else {
253
+ this._authenticated = true;
254
+ this._log.debug("Already have an auth key ...");
255
+ }
256
+ this._userConnected = true;
257
+ this.isReconnecting = false;
258
+ if (!this._sendLoopHandle) {
259
+ this._log.debug("Starting send loop");
260
+ this._sendLoopHandle = this._sendLoop();
261
+ }
262
+ if (!this._recvLoopHandle) {
263
+ this._log.debug("Starting receive loop");
264
+ this._recvLoopHandle = this._recvLoop();
265
+ }
266
+ // _disconnected only completes after manual disconnection
267
+ // or errors after which the sender cannot continue such
268
+ // as failing to reconnect or any unexpected error.
269
+ this._log.info("Connection to %s complete!".replace("%s", connection.toString()));
270
+ }
271
+ async _disconnect() {
272
+ const connection = this._connection;
273
+ if (this._updateCallback) {
274
+ this._updateCallback(this._client, new _1.UpdateConnectionState(_1.UpdateConnectionState.disconnected));
275
+ }
276
+ if (connection === undefined) {
277
+ this._log.info("Not disconnecting (already have no connection)");
278
+ return;
279
+ }
280
+ this._log.info("Disconnecting from %s...".replace("%s", connection.toString()));
281
+ this._userConnected = false;
282
+ this._log.debug("Closing current connection...");
283
+ // Signal abort to any ongoing operations
284
+ this._abortController.abort();
285
+ await connection.disconnect();
286
+ }
287
+ _cancelLoops() {
288
+ this._cancelSend = true;
289
+ this._abortController.abort();
290
+ }
291
+ /**
292
+ * This loop is responsible for popping items off the send
293
+ * queue, encrypting them, and sending them over the network.
294
+ * Besides `connect`, only this method ever sends data.
295
+ * @returns {Promise<void>}
296
+ * @private
297
+ */
298
+ async _sendLoop() {
299
+ // Retry previous pending requests
300
+ this._sendQueue.prepend(this._pendingState.values());
301
+ this._pendingState.clear();
302
+ while (this._userConnected && !this.isReconnecting) {
303
+ const appendAcks = () => {
304
+ if (this._pendingAck.size) {
305
+ const ack = new RequestState_1.RequestState(new MsgsAck({ msgIds: Array(...this._pendingAck) }));
306
+ this._sendQueue.append(ack);
307
+ this._lastAcks.push(ack);
308
+ if (this._lastAcks.length >= 10) {
309
+ this._lastAcks.shift();
310
+ }
311
+ this._pendingAck.clear();
312
+ }
313
+ };
314
+ appendAcks();
315
+ this._log.debug(`Waiting for messages to send... ${this.isReconnecting}`);
316
+ // TODO Wait for the connection send queue to be empty?
317
+ // This means that while it's not empty we can wait for
318
+ // more messages to be added to the send queue.
319
+ await this._sendQueue.wait();
320
+ // If we've had new ACKs appended while waiting for messages to send, add them to queue
321
+ appendAcks();
322
+ const res = await this._sendQueue.get();
323
+ this._log.debug(`Got ${res === null || res === void 0 ? void 0 : res.batch.length} message(s) to send`);
324
+ if (this.isReconnecting) {
325
+ this._log.debug("Reconnecting");
326
+ this._sendLoopHandle = undefined;
327
+ return;
328
+ }
329
+ if (!res) {
330
+ continue;
331
+ }
332
+ let { data } = res;
333
+ const { batch } = res;
334
+ this._log.debug(`Encrypting ${batch.length} message(s) in ${data.length} bytes for sending`);
335
+ this._log.debug(`Sending ${batch.map((m) => m.request.className)}`);
336
+ data = await this._state.encryptMessageData(data);
337
+ for (const state of batch) {
338
+ if (!Array.isArray(state)) {
339
+ if (state.request.classType === "request") {
340
+ this._pendingState.set(state.msgId, state);
341
+ }
342
+ }
343
+ else {
344
+ for (const s of state) {
345
+ if (s.request.classType === "request") {
346
+ this._pendingState.set(s.msgId, s);
347
+ }
348
+ }
349
+ }
350
+ }
351
+ try {
352
+ await this._connection.send(data);
353
+ }
354
+ catch (e) {
355
+ /** when the server disconnects us we want to reconnect */
356
+ if (!this.userDisconnected) {
357
+ this._log.debug(`Connection closed while sending data ${e}`);
358
+ if (this._log.canSend(Logger_1.LogLevel.DEBUG)) {
359
+ console.error(e);
360
+ }
361
+ this.reconnect();
362
+ }
363
+ this._sendLoopHandle = undefined;
364
+ return;
365
+ }
366
+ this._log.debug("Encrypted messages put in a queue to be sent");
367
+ }
368
+ this._sendLoopHandle = undefined;
369
+ }
370
+ async _recvLoop() {
371
+ // Create a new abort controller for this loop
372
+ this._abortController = new AbortController();
373
+ const signal = this._abortController.signal;
374
+ let body;
375
+ let message;
376
+ while (this._userConnected && !this.isReconnecting) {
377
+ // Check if the operation has been aborted
378
+ if (signal.aborted) {
379
+ this._log.debug("Receive loop aborted");
380
+ this._recvLoopHandle = undefined;
381
+ return;
382
+ }
383
+ this._log.debug("Receiving items from the network...");
384
+ try {
385
+ // Here we could pass the signal to connection.recv() if that method supports abort signals
386
+ body = await this._connection.recv();
387
+ }
388
+ catch (e) {
389
+ // If aborted, exit gracefully
390
+ if (signal.aborted) {
391
+ this._log.debug("Receive operation was aborted");
392
+ this._recvLoopHandle = undefined;
393
+ return;
394
+ }
395
+ if (this._currentRetries > this._reconnectRetries) {
396
+ for (const state of this._pendingState.values()) {
397
+ state.reject("Maximum reconnection retries reached. Aborting!");
398
+ }
399
+ this.userDisconnected = true;
400
+ return;
401
+ }
402
+ /** when the server disconnects us we want to reconnect */
403
+ if (!this.userDisconnected) {
404
+ this._log.warn("Connection closed while receiving data");
405
+ if (this._log.canSend(Logger_1.LogLevel.WARN)) {
406
+ console.error(e);
407
+ }
408
+ this.reconnect();
409
+ }
410
+ this._recvLoopHandle = undefined;
411
+ return;
412
+ }
413
+ try {
414
+ message = await this._state.decryptMessageData(body);
415
+ }
416
+ catch (e) {
417
+ this._log.debug(`Error while receiving items from the network ${e}`);
418
+ if (e instanceof errors_1.TypeNotFoundError) {
419
+ // Received object which we don't know how to deserialize
420
+ this._log.info(`Type ${e.invalidConstructorId} not found, remaining data ${e.remaining}`);
421
+ continue;
422
+ }
423
+ else if (e instanceof errors_1.SecurityError) {
424
+ // A step while decoding had the incorrect data. This message
425
+ // should not be considered safe and it should be ignored.
426
+ this._log.warn(`Security error while unpacking a received message: ${e}`);
427
+ continue;
428
+ }
429
+ else if (e instanceof errors_1.InvalidBufferError) {
430
+ // 404 means that the server has "forgotten" our auth key and we need to create a new one.
431
+ if (e.code === 404) {
432
+ this._handleBadAuthKey();
433
+ }
434
+ else {
435
+ // this happens sometimes when telegram is having some internal issues.
436
+ // reconnecting should be enough usually
437
+ // since the data we sent and received is probably wrong now.
438
+ this._log.warn(`Invalid buffer ${e.code} for dc ${this._dcId}`);
439
+ this.reconnect();
440
+ }
441
+ this._recvLoopHandle = undefined;
442
+ return;
443
+ }
444
+ else {
445
+ this._log.error("Unhandled error while receiving data");
446
+ if (this._client._errorHandler) {
447
+ await this._client._errorHandler(e);
448
+ }
449
+ if (this._log.canSend(Logger_1.LogLevel.ERROR)) {
450
+ console.log(e);
451
+ }
452
+ this.reconnect();
453
+ this._recvLoopHandle = undefined;
454
+ return;
455
+ }
456
+ }
457
+ try {
458
+ await this._processMessage(message);
459
+ }
460
+ catch (e) {
461
+ // `RPCError` errors except for 'AUTH_KEY_UNREGISTERED' should be handled by the client
462
+ if (e instanceof errors_1.RPCError) {
463
+ if (e.message === "AUTH_KEY_UNREGISTERED" ||
464
+ e.message === "SESSION_REVOKED") {
465
+ // 'AUTH_KEY_UNREGISTERED' for the main sender is thrown when unauthorized and should be ignored
466
+ this._handleBadAuthKey(true);
467
+ }
468
+ }
469
+ else {
470
+ this._log.error("Unhandled error while receiving data");
471
+ if (this._client._errorHandler) {
472
+ await this._client._errorHandler(e);
473
+ }
474
+ if (this._log.canSend(Logger_1.LogLevel.ERROR)) {
475
+ console.log(e);
476
+ }
477
+ }
478
+ }
479
+ this._currentRetries = 0;
480
+ }
481
+ this._recvLoopHandle = undefined;
482
+ }
483
+ // Response Handlers
484
+ _handleBadAuthKey(shouldSkipForMain = false) {
485
+ if (shouldSkipForMain && this._isMainSender) {
486
+ return;
487
+ }
488
+ this._log.warn(`Broken authorization key for dc ${this._dcId}, resetting...`);
489
+ if (this._isMainSender && this._updateCallback) {
490
+ this._updateCallback(this._client, new _1.UpdateConnectionState(_1.UpdateConnectionState.broken));
491
+ }
492
+ else if (!this._isMainSender && this._onConnectionBreak) {
493
+ this._onConnectionBreak(this._dcId);
494
+ }
495
+ }
496
+ /**
497
+ * Adds the given message to the list of messages that must be
498
+ * acknowledged and dispatches control to different ``_handle_*``
499
+ * method based on its type.
500
+ * @param message
501
+ * @returns {Promise<void>}
502
+ * @private
503
+ */
504
+ async _processMessage(message) {
505
+ this._pendingAck.add(message.msgId);
506
+ message.obj = await message.obj;
507
+ let handler = this._handlers[message.obj.CONSTRUCTOR_ID.toString()];
508
+ if (!handler) {
509
+ handler = this._handleUpdate.bind(this);
510
+ }
511
+ await handler(message);
512
+ }
513
+ /**
514
+ * Pops the states known to match the given ID from pending messages.
515
+ * This method should be used when the response isn't specific.
516
+ * @param msgId
517
+ * @returns {*[]}
518
+ * @private
519
+ */
520
+ _popStates(msgId) {
521
+ var _a;
522
+ const state = this._pendingState.getAndDelete(msgId);
523
+ if (state) {
524
+ return [state];
525
+ }
526
+ const toPop = [];
527
+ for (const pendingState of this._pendingState.values()) {
528
+ if ((_a = pendingState.containerId) === null || _a === void 0 ? void 0 : _a.equals(msgId)) {
529
+ toPop.push(pendingState.msgId);
530
+ }
531
+ }
532
+ if (toPop.length) {
533
+ const temp = [];
534
+ for (const x of toPop) {
535
+ temp.push(this._pendingState.getAndDelete(x));
536
+ }
537
+ return temp;
538
+ }
539
+ for (const ack of this._lastAcks) {
540
+ if (ack.msgId === msgId) {
541
+ return [ack];
542
+ }
543
+ }
544
+ return [];
545
+ }
546
+ /**
547
+ * Handles the result for Remote Procedure Calls:
548
+ * rpc_result#f35c6d01 req_msg_id:long result:bytes = RpcResult;
549
+ * This is where the future results for sent requests are set.
550
+ * @param message
551
+ * @returns {Promise<void>}
552
+ * @private
553
+ */
554
+ _handleRPCResult(message) {
555
+ const result = message.obj;
556
+ const state = this._pendingState.getAndDelete(result.reqMsgId);
557
+ this._log.debug(`Handling RPC result for message ${result.reqMsgId}`);
558
+ if (!state) {
559
+ // TODO We should not get responses to things we never sent
560
+ // However receiving a File() with empty bytes is "common".
561
+ // See #658, #759 and #958. They seem to happen in a container
562
+ // which contain the real response right after.
563
+ try {
564
+ const reader = new extensions_1.BinaryReader(result.body);
565
+ if (!(reader.tgReadObject() instanceof tl_1.Api.upload.File)) {
566
+ throw new errors_1.TypeNotFoundError(0, Buffer.alloc(0));
567
+ }
568
+ }
569
+ catch (e) {
570
+ if (e instanceof errors_1.TypeNotFoundError) {
571
+ this._log.info(`Received response without parent request: ${result.body}`);
572
+ return;
573
+ }
574
+ throw e;
575
+ }
576
+ return;
577
+ }
578
+ if (result.error) {
579
+ // eslint-disable-next-line new-cap
580
+ const error = (0, errors_1.RPCMessageToError)(result.error, state.request);
581
+ this._sendQueue.append(new RequestState_1.RequestState(new MsgsAck({ msgIds: [state.msgId] })));
582
+ state.reject(error);
583
+ throw error;
584
+ }
585
+ else {
586
+ try {
587
+ const reader = new extensions_1.BinaryReader(result.body);
588
+ const read = state.request.readResult(reader);
589
+ this._log.debug(`Handling RPC result ${read === null || read === void 0 ? void 0 : read.className}`);
590
+ state.resolve(read);
591
+ }
592
+ catch (err) {
593
+ state.reject(err);
594
+ throw err;
595
+ }
596
+ }
597
+ }
598
+ /**
599
+ * Processes the inner messages of a container with many of them:
600
+ * msg_container#73f1f8dc messages:vector<%Message> = MessageContainer;
601
+ * @param message
602
+ * @returns {Promise<void>}
603
+ * @private
604
+ */
605
+ async _handleContainer(message) {
606
+ this._log.debug("Handling container");
607
+ for (const innerMessage of message.obj.messages) {
608
+ await this._processMessage(innerMessage);
609
+ }
610
+ }
611
+ /**
612
+ * Unpacks the data from a gzipped object and processes it:
613
+ * gzip_packed#3072cfa1 packed_data:bytes = Object;
614
+ * @param message
615
+ * @returns {Promise<void>}
616
+ * @private
617
+ */
618
+ async _handleGzipPacked(message) {
619
+ this._log.debug("Handling gzipped data");
620
+ const reader = new extensions_1.BinaryReader(message.obj.data);
621
+ message.obj = reader.tgReadObject();
622
+ await this._processMessage(message);
623
+ }
624
+ async _handleUpdate(message) {
625
+ if (message.obj.SUBCLASS_OF_ID !== 0x8af52aac) {
626
+ // crc32(b'Updates')
627
+ this._log.warn(`Note: ${message.obj.className} is not an update, not dispatching it`);
628
+ return;
629
+ }
630
+ this._log.debug("Handling update " + message.obj.className);
631
+ if (this._updateCallback) {
632
+ this._updateCallback(this._client, message.obj);
633
+ }
634
+ }
635
+ /**
636
+ * Handles pong results, which don't come inside a ``RPCResult``
637
+ * but are still sent through a request:
638
+ * pong#347773c5 msg_id:long ping_id:long = Pong;
639
+ * @param message
640
+ * @returns {Promise<void>}
641
+ * @private
642
+ */
643
+ async _handlePong(message) {
644
+ const pong = message.obj;
645
+ this._log.debug(`Handling pong for message ${pong.msgId}`);
646
+ const state = this._pendingState.get(pong.msgId.toString());
647
+ this._pendingState.delete(pong.msgId.toString());
648
+ // Todo Check result
649
+ if (state) {
650
+ state.resolve(pong);
651
+ }
652
+ }
653
+ /**
654
+ * Corrects the currently used server salt to use the right value
655
+ * before enqueuing the rejected message to be re-sent:
656
+ * bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int
657
+ * error_code:int new_server_salt:long = BadMsgNotification;
658
+ * @param message
659
+ * @returns {Promise<void>}
660
+ * @private
661
+ */
662
+ async _handleBadServerSalt(message) {
663
+ const badSalt = message.obj;
664
+ this._log.debug(`Handling bad salt for message ${badSalt.badMsgId}`);
665
+ this._state.salt = badSalt.newServerSalt;
666
+ const states = this._popStates(badSalt.badMsgId);
667
+ this._sendQueue.extend(states);
668
+ this._log.debug(`${states.length} message(s) will be resent`);
669
+ }
670
+ /**
671
+ * Adjusts the current state to be correct based on the
672
+ * received bad message notification whenever possible:
673
+ * bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int
674
+ * error_code:int = BadMsgNotification;
675
+ * @param message
676
+ * @returns {Promise<void>}
677
+ * @private
678
+ */
679
+ async _handleBadNotification(message) {
680
+ const badMsg = message.obj;
681
+ const states = this._popStates(badMsg.badMsgId);
682
+ this._log.debug(`Handling bad msg ${JSON.stringify(badMsg)}`);
683
+ if ([16, 17].includes(badMsg.errorCode)) {
684
+ // Sent msg_id too low or too high (respectively).
685
+ // Use the current msg_id to determine the right time offset.
686
+ const to = this._state.updateTimeOffset((0, big_integer_1.default)(message.msgId));
687
+ this._log.info(`System clock is wrong, set time offset to ${to}s`);
688
+ }
689
+ else if (badMsg.errorCode === 32) {
690
+ // msg_seqno too low, so just pump it up by some "large" amount
691
+ // TODO A better fix would be to start with a new fresh session ID
692
+ this._state._sequence += 64;
693
+ }
694
+ else if (badMsg.errorCode === 33) {
695
+ // msg_seqno too high never seems to happen but just in case
696
+ this._state._sequence -= 16;
697
+ }
698
+ else {
699
+ for (const state of states) {
700
+ state.reject(new errors_1.BadMessageError(state.request, badMsg.errorCode));
701
+ }
702
+ return;
703
+ }
704
+ // Messages are to be re-sent once we've corrected the issue
705
+ this._sendQueue.extend(states);
706
+ this._log.debug(`${states.length} messages will be resent due to bad msg`);
707
+ }
708
+ /**
709
+ * Updates the current status with the received detailed information:
710
+ * msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long
711
+ * bytes:int status:int = MsgDetailedInfo;
712
+ * @param message
713
+ * @returns {Promise<void>}
714
+ * @private
715
+ */
716
+ async _handleDetailedInfo(message) {
717
+ // TODO https://goo.gl/VvpCC6
718
+ const msgId = message.obj.answerMsgId;
719
+ this._log.debug(`Handling detailed info for message ${msgId}`);
720
+ this._pendingAck.add(msgId);
721
+ }
722
+ /**
723
+ * Updates the current status with the received detailed information:
724
+ * msg_new_detailed_info#809db6df answer_msg_id:long
725
+ * bytes:int status:int = MsgDetailedInfo;
726
+ * @param message
727
+ * @returns {Promise<void>}
728
+ * @private
729
+ */
730
+ async _handleNewDetailedInfo(message) {
731
+ // TODO https://goo.gl/VvpCC6
732
+ const msgId = message.obj.answerMsgId;
733
+ this._log.debug(`Handling new detailed info for message ${msgId}`);
734
+ this._pendingAck.add(msgId);
735
+ }
736
+ /**
737
+ * Updates the current status with the received session information:
738
+ * new_session_created#9ec20908 first_msg_id:long unique_id:long
739
+ * server_salt:long = NewSession;
740
+ * @param message
741
+ * @returns {Promise<void>}
742
+ * @private
743
+ */
744
+ async _handleNewSessionCreated(message) {
745
+ // TODO https://goo.gl/LMyN7A
746
+ this._log.debug("Handling new session created");
747
+ this._state.salt = message.obj.serverSalt;
748
+ }
749
+ /**
750
+ * Handles a server acknowledge about our messages. Normally these can be ignored
751
+ */
752
+ _handleAck() { }
753
+ /**
754
+ * Handles future salt results, which don't come inside a
755
+ * ``rpc_result`` but are still sent through a request:
756
+ * future_salts#ae500895 req_msg_id:long now:int
757
+ * salts:vector<future_salt> = FutureSalts;
758
+ * @param message
759
+ * @returns {Promise<void>}
760
+ * @private
761
+ */
762
+ async _handleFutureSalts(message) {
763
+ this._log.debug(`Handling future salts for message ${message.msgId}`);
764
+ const state = this._pendingState.getAndDelete(message.msgId);
765
+ if (state) {
766
+ state.resolve(message.obj);
767
+ }
768
+ }
769
+ /**
770
+ * Handles both :tl:`MsgsStateReq` and :tl:`MsgResendReq` by
771
+ * enqueuing a :tl:`MsgsStateInfo` to be sent at a later point.
772
+ * @param message
773
+ * @returns {Promise<void>}
774
+ * @private
775
+ */
776
+ async _handleStateForgotten(message) {
777
+ this._sendQueue.append(new RequestState_1.RequestState(new tl_1.Api.MsgsStateInfo({
778
+ reqMsgId: message.msgId,
779
+ info: String.fromCharCode(1).repeat(message.obj.msgIds),
780
+ })));
781
+ }
782
+ /**
783
+ * Handles :tl:`MsgsAllInfo` by doing nothing (yet).
784
+ * @param message
785
+ * @returns {Promise<void>}
786
+ * @private
787
+ */
788
+ async _handleMsgAll(message) { }
789
+ reconnect() {
790
+ if (this._userConnected && !this.isReconnecting) {
791
+ this.isReconnecting = true;
792
+ this._currentRetries++;
793
+ if (this._isMainSender) {
794
+ this._log.debug("Reconnecting all senders");
795
+ for (const promise of this._exportedSenderPromises.values()) {
796
+ promise
797
+ .then((sender) => {
798
+ if (sender && !sender._isMainSender) {
799
+ sender.reconnect();
800
+ }
801
+ })
802
+ .catch((error) => {
803
+ this._log.warn("Error getting sender to reconnect to");
804
+ });
805
+ }
806
+ }
807
+ // we want to wait a second between each reconnect try to not flood the server with reconnects
808
+ // in case of internal server issues.
809
+ (0, Helpers_1.sleep)(1000).then(() => {
810
+ this._log.info("Started reconnecting");
811
+ this._reconnect();
812
+ });
813
+ }
814
+ }
815
+ async _reconnect() {
816
+ try {
817
+ this._log.warn("[Reconnect] Closing current connection...");
818
+ await this._disconnect();
819
+ }
820
+ catch (err) {
821
+ this._log.warn("Error happened while disconnecting");
822
+ if (this._client._errorHandler) {
823
+ await this._client._errorHandler(err);
824
+ }
825
+ if (this._log.canSend(Logger_1.LogLevel.ERROR)) {
826
+ console.error(err);
827
+ }
828
+ }
829
+ this._log.debug(`Adding ${this._sendQueue._pendingStates.length} old request to resend`);
830
+ for (let i = 0; i < this._sendQueue._pendingStates.length; i++) {
831
+ if (this._sendQueue._pendingStates[i].msgId != undefined) {
832
+ this._pendingState.set(this._sendQueue._pendingStates[i].msgId, this._sendQueue._pendingStates[i]);
833
+ }
834
+ }
835
+ this._sendQueue.clear();
836
+ this._state.reset();
837
+ const connection = this._connection;
838
+ // For some reason reusing existing connection caused stuck requests
839
+ // @ts-ignore
840
+ const newConnection = new connection.constructor({
841
+ ip: connection._ip,
842
+ port: connection._port,
843
+ dcId: connection._dcId,
844
+ loggers: connection._log,
845
+ proxy: connection._proxy,
846
+ testServers: connection._testServers,
847
+ socket: this._client.networkSocket,
848
+ });
849
+ await this.connect(newConnection, true);
850
+ this.isReconnecting = false;
851
+ this._sendQueue.prepend(this._pendingState.values());
852
+ this._pendingState.clear();
853
+ if (this._autoReconnectCallback) {
854
+ await this._autoReconnectCallback();
855
+ }
856
+ }
857
+ }
858
+ exports.MTProtoSender = MTProtoSender;
859
+ MTProtoSender.DEFAULT_OPTIONS = {
860
+ logger: null,
861
+ reconnectRetries: Infinity,
862
+ retries: Infinity,
863
+ delay: 2000,
864
+ autoReconnect: true,
865
+ connectTimeout: null,
866
+ authKeyCallback: null,
867
+ updateCallback: null,
868
+ autoReconnectCallback: null,
869
+ isMainSender: null,
870
+ senderCallback: null,
871
+ onConnectionBreak: undefined,
872
+ securityChecks: true,
873
+ };