telegram-tghub-better-new 0.1.2 → 0.2.2

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/Utils.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { Buffer } from "buffer/";
2
- import type { Entity, EntityLike, MessageIDLike } from "./define";
3
- import { Api } from "./tl";
4
2
  import bigInt from "big-integer";
5
- import { EntityCache } from "./entityCache";
6
3
  import type { ParseInterface } from "./client/messageParse";
7
4
  import { CustomFile } from "./client/uploads";
5
+ import type { Entity, EntityLike, MessageIDLike } from "./define";
6
+ import { EntityCache } from "./entityCache";
7
+ import { Api } from "./tl";
8
8
  export declare function getFileInfo(fileLocation: Api.Message | Api.MessageMediaDocument | Api.MessageMediaPhoto | Api.TypeInputFileLocation): {
9
9
  dcId?: number;
10
10
  location: Api.TypeInputFileLocation;
package/Utils.js CHANGED
@@ -35,11 +35,13 @@ exports.parseUsername = parseUsername;
35
35
  exports.rtrim = rtrim;
36
36
  exports.getDisplayName = getDisplayName;
37
37
  const buffer_1 = require("buffer/");
38
- const tl_1 = require("./tl");
39
38
  const big_integer_1 = __importDefault(require("big-integer"));
40
39
  const mime_1 = __importDefault(require("mime"));
40
+ const html_1 = require("./extensions/html");
41
41
  const markdown_1 = require("./extensions/markdown");
42
42
  const markdownv2_1 = require("./extensions/markdownv2");
43
+ const Helpers_1 = require("./Helpers");
44
+ const tl_1 = require("./tl");
43
45
  function getFileInfo(fileLocation) {
44
46
  if (!fileLocation || !fileLocation.SUBCLASS_OF_ID) {
45
47
  _raiseCastFail(fileLocation, "InputFileLocation");
@@ -96,8 +98,6 @@ function* chunks(arr, size = 100) {
96
98
  yield arr.slice(i, i + size);
97
99
  }
98
100
  }
99
- const html_1 = require("./extensions/html");
100
- const Helpers_1 = require("./Helpers");
101
101
  const USERNAME_RE = new RegExp("@|(?:https?:\\/\\/)?(?:www\\.)?" +
102
102
  "(?:telegram\\.(?:me|dog)|t\\.me)\\/(@|joinchat\\/)?", "i");
103
103
  const JPEG_HEADER = buffer_1.Buffer.from("ffd8ffe000104a46494600010100000100010000ffdb004300281c1e231e19282321232d2b28303c64413c37373c7b585d4964918099968f808c8aa0b4e6c3a0aadaad8a8cc8ffcbdaeef5ffffff9bc1fffffffaffe6fdfff8ffdb0043012b2d2d3c353c76414176f8a58ca5f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8ffc00011080000000003012200021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00", "hex");
@@ -676,7 +676,11 @@ function isVideo(file) {
676
676
  */
677
677
  function getAttributes(file, { attributes = null, mimeType = undefined, forceDocument = false, voiceNote = false, videoNote = false, supportsStreaming = false, thumb = null, }) {
678
678
  var _a, _b, _c, _d;
679
- const name = typeof file == "string" ? file : file.name || "unnamed";
679
+ const name = typeof file == "string"
680
+ ? file
681
+ : "name" in file
682
+ ? file.name || "unnamed"
683
+ : "unnamed";
680
684
  if (mimeType === undefined) {
681
685
  mimeType = mime_1.default.getType(name) || "application/octet-stream";
682
686
  }
package/Version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "10.0.8";
1
+ export declare const version = "10.1.1";
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 = "10.0.8";
4
+ exports.version = "10.1.1";
@@ -733,6 +733,25 @@ export declare class TelegramClient extends TelegramBaseClient {
733
733
  * @return
734
734
  */
735
735
  getParticipants(entity: EntityLike, params?: chatMethods.IterParticipantsParams): Promise<import("../Helpers").TotalList<Api.User>>;
736
+ /**
737
+ * Kicks a user from a chat.
738
+ *
739
+ * Kicking yourself (`'me'`) will result in leaving the chat.
740
+ *
741
+ * @note
742
+ * Attempting to kick someone who was banned will remove their
743
+ * restrictions (and thus unbanning them), since kicking is just
744
+ * ban + unban.
745
+ *
746
+ * @example
747
+ * // Kick some user from some chat, and deleting the service message
748
+ * const msg = await client.kickParticipant(chat, user);
749
+ * await msg.delete();
750
+ *
751
+ * // Leaving chat
752
+ * await client.kickParticipant(chat, 'me');
753
+ */
754
+ kickParticipant(entity: EntityLike, participant: EntityLike): Promise<Api.TypeMessage | Map<number, Api.Message> | (Api.Message | undefined)[] | undefined>;
736
755
  /** TODO */
737
756
  on(event: any): (f: {
738
757
  (event: any): void;
@@ -924,11 +943,11 @@ export declare class TelegramClient extends TelegramBaseClient {
924
943
  * @example
925
944
  * ```ts
926
945
  * const me = await client.getEntity("me");
927
- * console.log("My name is",utils.getDisplayName(me));
946
+ * console.log("My name is", utils.getDisplayName(me));
928
947
  *
929
948
  * const chat = await client.getInputEntity("username");
930
- * for await (const message of client.iterMessages(chat){
931
- * console.log("Message text is",message.text);
949
+ * for await (const message of client.iterMessages(chat)) {
950
+ * console.log("Message text is", message.text);
932
951
  * }
933
952
  *
934
953
  * // Note that you could have used the username directly, but it's
@@ -806,6 +806,27 @@ class TelegramClient extends telegramBaseClient_1.TelegramBaseClient {
806
806
  getParticipants(entity, params = {}) {
807
807
  return chatMethods.getParticipants(this, entity, params);
808
808
  }
809
+ /**
810
+ * Kicks a user from a chat.
811
+ *
812
+ * Kicking yourself (`'me'`) will result in leaving the chat.
813
+ *
814
+ * @note
815
+ * Attempting to kick someone who was banned will remove their
816
+ * restrictions (and thus unbanning them), since kicking is just
817
+ * ban + unban.
818
+ *
819
+ * @example
820
+ * // Kick some user from some chat, and deleting the service message
821
+ * const msg = await client.kickParticipant(chat, user);
822
+ * await msg.delete();
823
+ *
824
+ * // Leaving chat
825
+ * await client.kickParticipant(chat, 'me');
826
+ */
827
+ kickParticipant(entity, participant) {
828
+ return chatMethods.kickParticipant(this, entity, participant);
829
+ }
809
830
  //endregion
810
831
  //region updates
811
832
  /** TODO */
@@ -1051,6 +1072,8 @@ class TelegramClient extends telegramBaseClient_1.TelegramBaseClient {
1051
1072
  client: this,
1052
1073
  securityChecks: this._securityChecks,
1053
1074
  autoReconnectCallback: this._handleReconnect.bind(this),
1075
+ _exportedSenderPromises: this._exportedSenderPromises,
1076
+ reconnectRetries: this._reconnectRetries,
1054
1077
  });
1055
1078
  }
1056
1079
  const connection = new this._connection({
package/client/chats.d.ts CHANGED
@@ -36,4 +36,6 @@ export interface IterParticipantsParams {
36
36
  export declare function iterParticipants(client: TelegramClient, entity: EntityLike, { limit, offset, search, filter, showTotal }: IterParticipantsParams): _ParticipantsIter;
37
37
  /** @hidden */
38
38
  export declare function getParticipants(client: TelegramClient, entity: EntityLike, params: IterParticipantsParams): Promise<TotalList<Api.User>>;
39
+ /** @hidden */
40
+ export declare function kickParticipant(client: TelegramClient, entity: EntityLike, participant: EntityLike): Promise<Api.TypeMessage | Map<number, Api.Message> | (Api.Message | undefined)[] | undefined>;
39
41
  export {};
package/client/chats.js CHANGED
@@ -6,12 +6,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports._ParticipantsIter = void 0;
7
7
  exports.iterParticipants = iterParticipants;
8
8
  exports.getParticipants = getParticipants;
9
+ exports.kickParticipant = kickParticipant;
9
10
  const Helpers_1 = require("../Helpers");
10
11
  const requestIter_1 = require("../requestIter");
11
12
  const __1 = require("../");
12
13
  const tl_1 = require("../tl");
13
14
  const big_integer_1 = __importDefault(require("big-integer"));
14
15
  const inspect_1 = require("../inspect");
16
+ const Utils_1 = require("../Utils");
15
17
  const _MAX_PARTICIPANTS_CHUNK_SIZE = 200;
16
18
  const _MAX_ADMIN_LOG_CHUNK_SIZE = 100;
17
19
  const _MAX_PROFILE_PHOTO_CHUNK_SIZE = 100;
@@ -302,3 +304,47 @@ async function getParticipants(client, entity, params) {
302
304
  const it = client.iterParticipants(entity, params);
303
305
  return (await it.collect());
304
306
  }
307
+ /** @hidden */
308
+ async function kickParticipant(client, entity, participant) {
309
+ const peer = await client.getInputEntity(entity);
310
+ const user = await client.getInputEntity(participant);
311
+ let resp;
312
+ let request;
313
+ const type = __1.helpers._entityType(peer);
314
+ if (type === __1.helpers._EntityType.CHAT) {
315
+ request = new tl_1.Api.messages.DeleteChatUser({
316
+ chatId: (0, Helpers_1.returnBigInt)((0, Utils_1.getPeerId)(entity)),
317
+ userId: (0, Helpers_1.returnBigInt)((0, Utils_1.getPeerId)(participant)),
318
+ });
319
+ resp = await client.invoke(request);
320
+ }
321
+ else if (type === __1.helpers._EntityType.CHANNEL) {
322
+ if (user instanceof tl_1.Api.InputPeerSelf) {
323
+ request = new tl_1.Api.channels.LeaveChannel({
324
+ channel: peer,
325
+ });
326
+ resp = await client.invoke(request);
327
+ }
328
+ else {
329
+ request = new tl_1.Api.channels.EditBanned({
330
+ channel: peer,
331
+ participant: user,
332
+ bannedRights: new tl_1.Api.ChatBannedRights({
333
+ untilDate: 0,
334
+ viewMessages: true,
335
+ }),
336
+ });
337
+ resp = await client.invoke(request);
338
+ await (0, Helpers_1.sleep)(500);
339
+ await client.invoke(new tl_1.Api.channels.EditBanned({
340
+ channel: peer,
341
+ participant: user,
342
+ bannedRights: new tl_1.Api.ChatBannedRights({ untilDate: 0 }),
343
+ }));
344
+ }
345
+ }
346
+ else {
347
+ throw new Error("You must pass either a channel or a chat");
348
+ }
349
+ return client._getResponseMessage(request, resp, entity);
350
+ }
@@ -296,10 +296,10 @@ async function downloadFileV2(client, inputLocation, { outputFile = undefined, p
296
296
  _d = false;
297
297
  const chunk = _c;
298
298
  await writer.write(chunk);
299
+ downloaded = downloaded.add(chunk.length);
299
300
  if (progressCallback) {
300
301
  await progressCallback(downloaded, (0, big_integer_1.default)(fileSize || big_integer_1.default.zero));
301
302
  }
302
- downloaded = downloaded.add(chunk.length);
303
303
  }
304
304
  }
305
305
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
@@ -155,13 +155,13 @@ function _getResponseMessage(client, request, result, inputChat) {
155
155
  if (schedMessage) {
156
156
  return schedMessage;
157
157
  }
158
- client._log.warn(`No randomId in ${request} to map to. returning undefined for ${result}`);
158
+ client._log.warn(`No randomId in ${request} to map to. returning undefined for ${result} (Message was empty)`);
159
159
  return undefined;
160
160
  }
161
161
  if (!(0, Helpers_1.isArrayLike)(randomId)) {
162
162
  let msg = idToMessage.get(randomToId.get(randomId.toString()));
163
163
  if (!msg) {
164
- client._log.warn(`Request ${request.className} had missing message mapping ${result.className}`);
164
+ client._log.warn(`Request ${request.className} had missing message mapping ${result.className} (Message was empty)`);
165
165
  }
166
166
  return msg;
167
167
  }
@@ -181,7 +181,7 @@ function _getResponseMessage(client, request, result, inputChat) {
181
181
  final.push(tmp2);
182
182
  }
183
183
  if (warned) {
184
- client._log.warn(`Request ${request.className} had missing message mapping ${result.className}`);
184
+ client._log.warn(`Request ${request.className} had missing message mapping ${result.className} (Message was empty)`);
185
185
  }
186
186
  const finalToReturn = [];
187
187
  for (const rnd of randomId) {
@@ -791,7 +791,7 @@ async function getCommentData(client, entity, message) {
791
791
  peer: entity,
792
792
  msgId: __1.utils.getMessageId(message),
793
793
  }));
794
- const relevantMessage = result.messages[0];
794
+ const relevantMessage = result.messages.reduce((p, c) => (p && p.id < c.id ? p : c));
795
795
  let chat;
796
796
  for (const c of result.chats) {
797
797
  if (relevantMessage.peerId instanceof tl_1.Api.PeerChannel &&
@@ -37,11 +37,16 @@ export interface TelegramClientParams {
37
37
  */
38
38
  requestRetries?: number;
39
39
  /**
40
- * How many times the reconnection should retry, either on the initial connection or when Telegram disconnects us.<br/>
40
+ * How many times the connection should retry, either on the initial connection or when Telegram disconnects us.<br/>
41
41
  * May be set to a negative or undefined value for infinite retries, but this is not recommended, since the program can get stuck in an infinite loop.<br/>
42
42
  * defaults to 5
43
43
  */
44
44
  connectionRetries?: number;
45
+ /**
46
+ * How many times to reconnect before giving up. This happens after the initial connection is finished<br/>
47
+ * defaults to infinity
48
+ */
49
+ reconnectRetries?: number;
45
50
  /**
46
51
  * Experimental proxy to be used for the connection. (only supports MTProxies)
47
52
  */
@@ -126,6 +131,8 @@ export declare abstract class TelegramBaseClient {
126
131
  /** @hidden */
127
132
  _connectionRetries: number;
128
133
  /** @hidden */
134
+ _reconnectRetries: number;
135
+ /** @hidden */
129
136
  _retryDelay: number;
130
137
  /** @hidden */
131
138
  _timeout: number;
@@ -162,7 +169,7 @@ export declare abstract class TelegramBaseClient {
162
169
  /** @hidden */
163
170
  _ALBUMS: Map<string, [Timeout, Api.TypeUpdate[]]>;
164
171
  /** @hidden */
165
- private _exportedSenderPromises;
172
+ _exportedSenderPromises: Map<number, Promise<MTProtoSender>>;
166
173
  /** @hidden */
167
174
  private _exportedSenderReleaseTimeouts;
168
175
  /** @hidden */
@@ -48,7 +48,6 @@ const clientParamsDefault = {
48
48
  };
49
49
  class TelegramBaseClient {
50
50
  constructor(session, apiId, apiHash, clientParams) {
51
- var _a;
52
51
  /** The current gramJS version. */
53
52
  this.__version__ = __1.version;
54
53
  /** @hidden */
@@ -82,6 +81,7 @@ class TelegramBaseClient {
82
81
  this._requestRetries = clientParams.requestRetries;
83
82
  this._downloadRetries = clientParams.downloadRetries;
84
83
  this._connectionRetries = clientParams.connectionRetries;
84
+ this._reconnectRetries = clientParams.reconnectRetries;
85
85
  this._retryDelay = clientParams.retryDelay || 0;
86
86
  this._timeout = clientParams.timeout;
87
87
  this._autoReconnect = clientParams.autoReconnect;
@@ -94,7 +94,7 @@ class TelegramBaseClient {
94
94
  }
95
95
  this._connection = clientParams.connection;
96
96
  let initProxy;
97
- if ((_a = this._proxy) === null || _a === void 0 ? void 0 : _a.MTProxy) {
97
+ if (this._proxy && "MTProxy" in this._proxy) {
98
98
  this._connection = TCPMTProxy_1.ConnectionTCPMTProxyAbridged;
99
99
  initProxy = new tl_1.Api.InputClientProxy({
100
100
  address: this._proxy.ip,
@@ -118,9 +118,6 @@ class TelegramBaseClient {
118
118
  this._selfInputPeer = undefined;
119
119
  this.useWSS = clientParams.useWSS;
120
120
  this._securityChecks = !!clientParams.securityChecks;
121
- if (this.useWSS && this._proxy) {
122
- throw new Error("Cannot use SSL with proxies. You need to disable the useWSS client param in TelegramClient");
123
- }
124
121
  this._entityCache = new entityCache_1.EntityCache();
125
122
  // These will be set later
126
123
  this._config = undefined;
@@ -295,7 +292,14 @@ class TelegramBaseClient {
295
292
  }
296
293
  this._exportedSenderReleaseTimeouts.set(dcId, setTimeout(() => {
297
294
  this._exportedSenderReleaseTimeouts.delete(dcId);
298
- sender.disconnect();
295
+ if (sender._pendingState.values().length) {
296
+ console.log("sender already has some hanging states. reconnecting");
297
+ sender._reconnect();
298
+ this._borrowExportedSender(dcId, false, sender);
299
+ }
300
+ else {
301
+ sender.disconnect();
302
+ }
299
303
  }, EXPORTED_SENDER_RELEASE_TIMEOUT));
300
304
  return sender;
301
305
  }
@@ -313,6 +317,8 @@ class TelegramBaseClient {
313
317
  onConnectionBreak: this._cleanupExportedSender.bind(this),
314
318
  client: this,
315
319
  securityChecks: this._securityChecks,
320
+ _exportedSenderPromises: this._exportedSenderPromises,
321
+ reconnectRetries: this._reconnectRetries,
316
322
  });
317
323
  }
318
324
  /** @hidden */
package/client/uploads.js CHANGED
@@ -59,6 +59,7 @@ const LARGE_FILE_THRESHOLD = 10 * 1024 * 1024;
59
59
  const UPLOAD_TIMEOUT = 15 * 1000;
60
60
  const DISCONNECT_SLEEP = 1000;
61
61
  const BUFFER_SIZE_2GB = 2 ** 31;
62
+ const BUFFER_SIZE_20MB = 20 * 1024 * 1024;
62
63
  async function getFileBuffer(file, fileSize, maxBufferSize) {
63
64
  const options = {};
64
65
  if (fileSize > maxBufferSize && file instanceof CustomFile) {
@@ -78,7 +79,7 @@ async function uploadFile(client, fileParams) {
78
79
  const isLarge = size > LARGE_FILE_THRESHOLD;
79
80
  const partSize = (0, Utils_1.getAppropriatedPartSize)((0, big_integer_1.default)(size)) * KB_TO_BYTES;
80
81
  const partCount = Math.floor((size + partSize - 1) / partSize);
81
- const buffer = await getFileBuffer(file, size, fileParams.maxBufferSize || BUFFER_SIZE_2GB - 1);
82
+ const buffer = await getFileBuffer(file, size, fileParams.maxBufferSize || BUFFER_SIZE_20MB - 1);
82
83
  // Make sure a new sender can be created before starting upload
83
84
  await client.getSender(client.session.dcId);
84
85
  if (!workers || !size) {
@@ -98,7 +99,14 @@ async function uploadFile(client, fileParams) {
98
99
  end = partCount;
99
100
  }
100
101
  for (let j = i; j < end; j++) {
101
- const bytes = await buffer.slice(j * partSize, (j + 1) * partSize);
102
+ let endPart = (j + 1) * partSize;
103
+ if (endPart > size) {
104
+ endPart = size;
105
+ }
106
+ if (endPart == j * partSize) {
107
+ break;
108
+ }
109
+ const bytes = await buffer.slice(j * partSize, endPart);
102
110
  // eslint-disable-next-line no-loop-func
103
111
  sendingParts.push((async (jMemo, bytesMemo) => {
104
112
  while (true) {
package/client/users.js CHANGED
@@ -39,6 +39,9 @@ async function invoke(client, request, dcId, otherSender) {
39
39
  if (sender == undefined) {
40
40
  throw new Error("Cannot send requests while disconnected. You need to call .connect()");
41
41
  }
42
+ if (sender.userDisconnected) {
43
+ throw new Error("Cannot send requests while disconnected. Please reconnect.");
44
+ }
42
45
  await client._connectedDeferred.promise;
43
46
  await request.resolve(client, __1.utils);
44
47
  client._lastRequest = new Date().getTime();
@@ -3,7 +3,7 @@ import type { MTProtoState } from "../network/MTProtoState";
3
3
  import type { RequestState } from "../network/RequestState";
4
4
  export declare class MessagePacker {
5
5
  private _state;
6
- private _pendingStates;
6
+ _pendingStates: RequestState[];
7
7
  private _queue;
8
8
  private _ready;
9
9
  private setReady;
@@ -62,7 +62,7 @@ class MessagePacker {
62
62
  this._pendingStates.push(state);
63
63
  state
64
64
  .promise // Using finally causes triggering `unhandledrejection` event
65
- .catch(() => { })
65
+ .catch((err) => { })
66
66
  .finally(() => {
67
67
  this._pendingStates = this._pendingStates.filter((s) => s !== state);
68
68
  });
@@ -40,6 +40,9 @@ class HTMLToTelegramParser {
40
40
  }
41
41
  else if (name == "blockquote") {
42
42
  EntityType = tl_1.Api.MessageEntityBlockquote;
43
+ if (attributes.expandable !== undefined) {
44
+ args.collapsed = true;
45
+ }
43
46
  }
44
47
  else if (name == "code") {
45
48
  const pre = this._buildingEntities.get("pre");
@@ -39,7 +39,7 @@ class MarkdownParser {
39
39
  foundIndex - tempEntities[foundDelim].offset;
40
40
  entities.push(tempEntities[foundDelim]);
41
41
  }
42
- message = message.replace(foundDelim, "");
42
+ message = message.toString().replace(foundDelim, "");
43
43
  i = foundIndex;
44
44
  }
45
45
  return [message, entities];
@@ -13,7 +13,7 @@ class MarkdownV2Parser {
13
13
  // italic
14
14
  message = message.replace(/-(.*?)-/g, "<i>$1</i>");
15
15
  // pre
16
- message = message.replace(/```(.*?)```/g, "<pre>$1</pre>");
16
+ message = message.replace(/```([\s\S]*?)```/g, "<pre>$1</pre>");
17
17
  // code
18
18
  message = message.replace(/`(.*?)`/g, "<code>$1</code>");
19
19
  // Spoiler
@@ -22,6 +22,7 @@ class MarkdownV2Parser {
22
22
  message = message.replace(/(?<!\!)\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
23
23
  // Emoji
24
24
  message = message.replace(/!\[([^\]]+)\]\(tg:\/\/emoji\?id=(\d+)\)/g, '<tg-emoji emoji-id="$2">$1</tg-emoji>');
25
+ //
25
26
  return html_1.HTMLParser.parse(message);
26
27
  }
27
28
  static unparse(text, entities) {
@@ -19,9 +19,11 @@ import { RequestState } from "./RequestState";
19
19
  import { Connection, UpdateConnectionState } from "./";
20
20
  import type { TelegramClient } from "..";
21
21
  import { CancellablePromise } from "real-cancellable-promise";
22
+ import { PendingState } from "../extensions/PendingState";
22
23
  interface DEFAULT_OPTIONS {
23
24
  logger: any;
24
25
  retries: number;
26
+ reconnectRetries: number;
25
27
  delay: number;
26
28
  autoReconnect: boolean;
27
29
  connectTimeout: any;
@@ -34,10 +36,12 @@ interface DEFAULT_OPTIONS {
34
36
  client: TelegramClient;
35
37
  onConnectionBreak?: CallableFunction;
36
38
  securityChecks: boolean;
39
+ _exportedSenderPromises: Map<number, Promise<MTProtoSender>>;
37
40
  }
38
41
  export declare class MTProtoSender {
39
42
  static DEFAULT_OPTIONS: {
40
43
  logger: null;
44
+ reconnectRetries: number;
41
45
  retries: number;
42
46
  delay: number;
43
47
  autoReconnect: boolean;
@@ -54,6 +58,8 @@ export declare class MTProtoSender {
54
58
  private readonly _log;
55
59
  private _dcId;
56
60
  private readonly _retries;
61
+ private _reconnectRetries;
62
+ private _currentRetries;
57
63
  private readonly _delay;
58
64
  private _connectTimeout;
59
65
  private _autoReconnect;
@@ -71,7 +77,7 @@ export declare class MTProtoSender {
71
77
  readonly authKey: AuthKey;
72
78
  private readonly _state;
73
79
  private _sendQueue;
74
- private _pendingState;
80
+ _pendingState: PendingState;
75
81
  private readonly _pendingAck;
76
82
  private readonly _lastAcks;
77
83
  private readonly _handlers;
@@ -85,6 +91,7 @@ export declare class MTProtoSender {
85
91
  private _cancelSend;
86
92
  cancellableRecvLoopPromise?: CancellablePromise<any>;
87
93
  private _finishedConnecting;
94
+ private _exportedSenderPromises;
88
95
  /**
89
96
  * @param authKey
90
97
  * @param opts
@@ -40,6 +40,7 @@ class MTProtoSender {
40
40
  * @param opts
41
41
  */
42
42
  constructor(authKey, opts) {
43
+ this._exportedSenderPromises = new Map();
43
44
  const args = Object.assign(Object.assign({}, MTProtoSender.DEFAULT_OPTIONS), opts);
44
45
  this._finishedConnecting = false;
45
46
  this._cancelSend = false;
@@ -47,6 +48,8 @@ class MTProtoSender {
47
48
  this._log = args.logger;
48
49
  this._dcId = args.dcId;
49
50
  this._retries = args.retries;
51
+ this._currentRetries = 0;
52
+ this._reconnectRetries = args.reconnectRetries;
50
53
  this._delay = args.delay;
51
54
  this._autoReconnect = args.autoReconnect;
52
55
  this._connectTimeout = args.connectTimeout;
@@ -59,6 +62,7 @@ class MTProtoSender {
59
62
  this._onConnectionBreak = args.onConnectionBreak;
60
63
  this._securityChecks = args.securityChecks;
61
64
  this._connectMutex = new async_mutex_1.Mutex();
65
+ this._exportedSenderPromises = args._exportedSenderPromises;
62
66
  /**
63
67
  * whether we disconnected ourself or telegram did it.
64
68
  */
@@ -328,17 +332,6 @@ class MTProtoSender {
328
332
  this._log.debug(`Encrypting ${batch.length} message(s) in ${data.length} bytes for sending`);
329
333
  this._log.debug(`Sending ${batch.map((m) => m.request.className)}`);
330
334
  data = await this._state.encryptMessageData(data);
331
- try {
332
- await this._connection.send(data);
333
- }
334
- catch (e) {
335
- this._log.debug(`Connection closed while sending data ${e}`);
336
- if (this._log.canSend(Logger_1.LogLevel.DEBUG)) {
337
- console.error(e);
338
- }
339
- this._sendLoopHandle = undefined;
340
- return;
341
- }
342
335
  for (const state of batch) {
343
336
  if (!Array.isArray(state)) {
344
337
  if (state.request.classType === "request") {
@@ -353,6 +346,21 @@ class MTProtoSender {
353
346
  }
354
347
  }
355
348
  }
349
+ try {
350
+ await this._connection.send(data);
351
+ }
352
+ catch (e) {
353
+ /** when the server disconnects us we want to reconnect */
354
+ if (!this.userDisconnected) {
355
+ this._log.debug(`Connection closed while sending data ${e}`);
356
+ if (this._log.canSend(Logger_1.LogLevel.DEBUG)) {
357
+ console.error(e);
358
+ }
359
+ this.reconnect();
360
+ }
361
+ this._sendLoopHandle = undefined;
362
+ return;
363
+ }
356
364
  this._log.debug("Encrypted messages put in a queue to be sent");
357
365
  }
358
366
  this._sendLoopHandle = undefined;
@@ -366,6 +374,13 @@ class MTProtoSender {
366
374
  body = await this._connection.recv();
367
375
  }
368
376
  catch (e) {
377
+ if (this._currentRetries > this._reconnectRetries) {
378
+ for (const state of this._pendingState.values()) {
379
+ state.reject("Maximum reconnection retries reached. Aborting!");
380
+ }
381
+ this.userDisconnected = true;
382
+ return;
383
+ }
369
384
  /** when the server disconnects us we want to reconnect */
370
385
  if (!this.userDisconnected) {
371
386
  this._log.warn("Connection closed while receiving data");
@@ -443,6 +458,7 @@ class MTProtoSender {
443
458
  }
444
459
  }
445
460
  }
461
+ this._currentRetries = 0;
446
462
  }
447
463
  this._recvLoopHandle = undefined;
448
464
  }
@@ -518,7 +534,6 @@ class MTProtoSender {
518
534
  * @private
519
535
  */
520
536
  _handleRPCResult(message) {
521
- var _a;
522
537
  const result = message.obj;
523
538
  const state = this._pendingState.getAndDelete(result.reqMsgId);
524
539
  this._log.debug(`Handling RPC result for message ${result.reqMsgId}`);
@@ -553,7 +568,7 @@ class MTProtoSender {
553
568
  try {
554
569
  const reader = new extensions_1.BinaryReader(result.body);
555
570
  const read = state.request.readResult(reader);
556
- this._log.debug(`Handling RPC result ${(_a = read === null || read === void 0 ? void 0 : read.constructor) === null || _a === void 0 ? void 0 : _a.name}`);
571
+ this._log.debug(`Handling RPC result ${read === null || read === void 0 ? void 0 : read.className}`);
557
572
  state.resolve(read);
558
573
  }
559
574
  catch (err) {
@@ -756,6 +771,21 @@ class MTProtoSender {
756
771
  reconnect() {
757
772
  if (this._userConnected && !this.isReconnecting) {
758
773
  this.isReconnecting = true;
774
+ this._currentRetries++;
775
+ if (this._isMainSender) {
776
+ this._log.debug("Reconnecting all senders");
777
+ for (const promise of this._exportedSenderPromises.values()) {
778
+ promise
779
+ .then((sender) => {
780
+ if (sender && !sender._isMainSender) {
781
+ sender.reconnect();
782
+ }
783
+ })
784
+ .catch((error) => {
785
+ this._log.warn("Error getting sender to reconnect to");
786
+ });
787
+ }
788
+ }
759
789
  // we want to wait a second between each reconnect try to not flood the server with reconnects
760
790
  // in case of internal server issues.
761
791
  (0, Helpers_1.sleep)(1000).then(() => {
@@ -765,7 +795,6 @@ class MTProtoSender {
765
795
  }
766
796
  }
767
797
  async _reconnect() {
768
- this._log.debug("Closing current connection...");
769
798
  try {
770
799
  this._log.warn("[Reconnect] Closing current connection...");
771
800
  await this._disconnect();
@@ -779,6 +808,12 @@ class MTProtoSender {
779
808
  console.error(err);
780
809
  }
781
810
  }
811
+ this._log.debug(`Adding ${this._sendQueue._pendingStates.length} old request to resend`);
812
+ for (let i = 0; i < this._sendQueue._pendingStates.length; i++) {
813
+ if (this._sendQueue._pendingStates[i].msgId != undefined) {
814
+ this._pendingState.set(this._sendQueue._pendingStates[i].msgId, this._sendQueue._pendingStates[i]);
815
+ }
816
+ }
782
817
  this._sendQueue.clear();
783
818
  this._state.reset();
784
819
  const connection = this._connection;
@@ -805,6 +840,7 @@ class MTProtoSender {
805
840
  exports.MTProtoSender = MTProtoSender;
806
841
  MTProtoSender.DEFAULT_OPTIONS = {
807
842
  logger: null,
843
+ reconnectRetries: Infinity,
808
844
  retries: Infinity,
809
845
  delay: 2000,
810
846
  autoReconnect: true,