@bruch/max-client 0.1.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 (174) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +168 -0
  3. package/UPSTREAM.md +10 -0
  4. package/dist/api/account.d.ts +24 -0
  5. package/dist/api/account.d.ts.map +1 -0
  6. package/dist/api/auth.d.ts +57 -0
  7. package/dist/api/auth.d.ts.map +1 -0
  8. package/dist/api/bots.d.ts +8 -0
  9. package/dist/api/bots.d.ts.map +1 -0
  10. package/dist/api/chats.d.ts +41 -0
  11. package/dist/api/chats.d.ts.map +1 -0
  12. package/dist/api/facade.d.ts +19 -0
  13. package/dist/api/facade.d.ts.map +1 -0
  14. package/dist/api/index.d.ts +10 -0
  15. package/dist/api/index.d.ts.map +1 -0
  16. package/dist/api/messages.d.ts +35 -0
  17. package/dist/api/messages.d.ts.map +1 -0
  18. package/dist/api/response.d.ts +7 -0
  19. package/dist/api/response.d.ts.map +1 -0
  20. package/dist/api/runtime.d.ts +19 -0
  21. package/dist/api/runtime.d.ts.map +1 -0
  22. package/dist/api/uploads.d.ts +23 -0
  23. package/dist/api/uploads.d.ts.map +1 -0
  24. package/dist/api/users.d.ts +25 -0
  25. package/dist/api/users.d.ts.map +1 -0
  26. package/dist/app.d.ts +55 -0
  27. package/dist/app.d.ts.map +1 -0
  28. package/dist/auth/flows.d.ts +27 -0
  29. package/dist/auth/flows.d.ts.map +1 -0
  30. package/dist/auth/index.d.ts +3 -0
  31. package/dist/auth/index.d.ts.map +1 -0
  32. package/dist/auth/index.js +109 -0
  33. package/dist/auth/index.js.map +11 -0
  34. package/dist/auth/providers.d.ts +11 -0
  35. package/dist/auth/providers.d.ts.map +1 -0
  36. package/dist/client.d.ts +139 -0
  37. package/dist/client.d.ts.map +1 -0
  38. package/dist/config.d.ts +80 -0
  39. package/dist/config.d.ts.map +1 -0
  40. package/dist/connection/connection.d.ts +25 -0
  41. package/dist/connection/connection.d.ts.map +1 -0
  42. package/dist/connection/index.d.ts +4 -0
  43. package/dist/connection/index.d.ts.map +1 -0
  44. package/dist/connection/pending.d.ts +9 -0
  45. package/dist/connection/pending.d.ts.map +1 -0
  46. package/dist/connection/readers.d.ts +17 -0
  47. package/dist/connection/readers.d.ts.map +1 -0
  48. package/dist/dispatch/dispatcher.d.ts +16 -0
  49. package/dist/dispatch/dispatcher.d.ts.map +1 -0
  50. package/dist/dispatch/index.d.ts +3 -0
  51. package/dist/dispatch/index.d.ts.map +1 -0
  52. package/dist/dispatch/router.d.ts +64 -0
  53. package/dist/dispatch/router.d.ts.map +1 -0
  54. package/dist/errors.d.ts +24 -0
  55. package/dist/errors.d.ts.map +1 -0
  56. package/dist/files/file.d.ts +29 -0
  57. package/dist/files/file.d.ts.map +1 -0
  58. package/dist/files/index.d.ts +2 -0
  59. package/dist/files/index.d.ts.map +1 -0
  60. package/dist/files/index.js +95 -0
  61. package/dist/files/index.js.map +10 -0
  62. package/dist/formatting/index.d.ts +2 -0
  63. package/dist/formatting/index.d.ts.map +1 -0
  64. package/dist/formatting/markdown.d.ts +13 -0
  65. package/dist/formatting/markdown.d.ts.map +1 -0
  66. package/dist/index.d.ts +15 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +3967 -0
  69. package/dist/index.js.map +48 -0
  70. package/dist/logging.d.ts +10 -0
  71. package/dist/logging.d.ts.map +1 -0
  72. package/dist/protocol/compression.d.ts +8 -0
  73. package/dist/protocol/compression.d.ts.map +1 -0
  74. package/dist/protocol/enums.d.ts +175 -0
  75. package/dist/protocol/enums.d.ts.map +1 -0
  76. package/dist/protocol/index.d.ts +6 -0
  77. package/dist/protocol/index.d.ts.map +1 -0
  78. package/dist/protocol/index.js +438 -0
  79. package/dist/protocol/index.js.map +13 -0
  80. package/dist/protocol/tcp.d.ts +35 -0
  81. package/dist/protocol/tcp.d.ts.map +1 -0
  82. package/dist/protocol/types.d.ts +38 -0
  83. package/dist/protocol/types.d.ts.map +1 -0
  84. package/dist/protocol/websocket.d.ts +7 -0
  85. package/dist/protocol/websocket.d.ts.map +1 -0
  86. package/dist/session/index.d.ts +3 -0
  87. package/dist/session/index.d.ts.map +1 -0
  88. package/dist/session/index.js +179 -0
  89. package/dist/session/index.js.map +12 -0
  90. package/dist/session/store.d.ts +18 -0
  91. package/dist/session/store.d.ts.map +1 -0
  92. package/dist/session/types.d.ts +29 -0
  93. package/dist/session/types.d.ts.map +1 -0
  94. package/dist/telemetry/index.d.ts +4 -0
  95. package/dist/telemetry/index.d.ts.map +1 -0
  96. package/dist/telemetry/index.js +831 -0
  97. package/dist/telemetry/index.js.map +17 -0
  98. package/dist/telemetry/navigation.d.ts +41 -0
  99. package/dist/telemetry/navigation.d.ts.map +1 -0
  100. package/dist/telemetry/payloads.d.ts +30 -0
  101. package/dist/telemetry/payloads.d.ts.map +1 -0
  102. package/dist/telemetry/service.d.ts +53 -0
  103. package/dist/telemetry/service.d.ts.map +1 -0
  104. package/dist/transport/base.d.ts +18 -0
  105. package/dist/transport/base.d.ts.map +1 -0
  106. package/dist/transport/index.d.ts +4 -0
  107. package/dist/transport/index.d.ts.map +1 -0
  108. package/dist/transport/tcp.d.ts +28 -0
  109. package/dist/transport/tcp.d.ts.map +1 -0
  110. package/dist/transport/websocket.d.ts +14 -0
  111. package/dist/transport/websocket.d.ts.map +1 -0
  112. package/dist/types/attachments.d.ts +18 -0
  113. package/dist/types/attachments.d.ts.map +1 -0
  114. package/dist/types/index.d.ts +4 -0
  115. package/dist/types/index.d.ts.map +1 -0
  116. package/dist/types/index.js +376 -0
  117. package/dist/types/index.js.map +12 -0
  118. package/dist/types/models.d.ts +210 -0
  119. package/dist/types/models.d.ts.map +1 -0
  120. package/dist/types/utils.d.ts +10 -0
  121. package/dist/types/utils.d.ts.map +1 -0
  122. package/package.json +95 -0
  123. package/src/api/account.ts +93 -0
  124. package/src/api/auth.ts +312 -0
  125. package/src/api/bots.ts +21 -0
  126. package/src/api/chats.ts +234 -0
  127. package/src/api/facade.ts +28 -0
  128. package/src/api/index.ts +9 -0
  129. package/src/api/messages.ts +230 -0
  130. package/src/api/response.ts +24 -0
  131. package/src/api/runtime.ts +27 -0
  132. package/src/api/uploads.ts +91 -0
  133. package/src/api/users.ts +68 -0
  134. package/src/app.ts +271 -0
  135. package/src/auth/flows.ts +103 -0
  136. package/src/auth/index.ts +2 -0
  137. package/src/auth/providers.ts +30 -0
  138. package/src/client.ts +506 -0
  139. package/src/config.ts +161 -0
  140. package/src/connection/connection.ts +93 -0
  141. package/src/connection/index.ts +3 -0
  142. package/src/connection/pending.ts +49 -0
  143. package/src/connection/readers.ts +33 -0
  144. package/src/dispatch/dispatcher.ts +194 -0
  145. package/src/dispatch/index.ts +2 -0
  146. package/src/dispatch/router.ts +149 -0
  147. package/src/errors.ts +41 -0
  148. package/src/files/file.ts +92 -0
  149. package/src/files/index.ts +1 -0
  150. package/src/formatting/index.ts +1 -0
  151. package/src/formatting/markdown.ts +123 -0
  152. package/src/index.ts +15 -0
  153. package/src/logging.ts +67 -0
  154. package/src/protocol/compression.ts +69 -0
  155. package/src/protocol/enums.ts +175 -0
  156. package/src/protocol/index.ts +5 -0
  157. package/src/protocol/tcp.ts +139 -0
  158. package/src/protocol/types.ts +45 -0
  159. package/src/protocol/websocket.ts +47 -0
  160. package/src/session/index.ts +2 -0
  161. package/src/session/store.ts +154 -0
  162. package/src/session/types.ts +45 -0
  163. package/src/telemetry/index.ts +3 -0
  164. package/src/telemetry/navigation.ts +160 -0
  165. package/src/telemetry/payloads.ts +84 -0
  166. package/src/telemetry/service.ts +194 -0
  167. package/src/transport/base.ts +43 -0
  168. package/src/transport/index.ts +3 -0
  169. package/src/transport/tcp.ts +296 -0
  170. package/src/transport/websocket.ts +63 -0
  171. package/src/types/attachments.ts +24 -0
  172. package/src/types/index.ts +3 -0
  173. package/src/types/models.ts +461 -0
  174. package/src/types/utils.ts +45 -0
package/dist/index.js ADDED
@@ -0,0 +1,3967 @@
1
+ // @bun
2
+ // src/protocol/compression.ts
3
+ var DEFAULT_MAX_OUTPUT = 5 * 1024 * 1024;
4
+
5
+ class Lz4BlockCompression {
6
+ decompress(source, maxOutput = DEFAULT_MAX_OUTPUT) {
7
+ const output = [];
8
+ let position = 0;
9
+ while (position < source.length) {
10
+ const token = source[position++];
11
+ if (token === undefined)
12
+ break;
13
+ let literalLength = token >>> 4;
14
+ if (literalLength === 15) {
15
+ while (position < source.length) {
16
+ const byte = source[position++];
17
+ if (byte === undefined)
18
+ break;
19
+ literalLength += byte;
20
+ if (byte !== 255)
21
+ break;
22
+ }
23
+ }
24
+ if (position + literalLength > source.length) {
25
+ throw new RangeError("LZ4: literal length out of bounds");
26
+ }
27
+ for (let index = 0;index < literalLength; index += 1) {
28
+ output.push(source[position + index]);
29
+ }
30
+ position += literalLength;
31
+ this.ensureSize(output.length, maxOutput);
32
+ if (position >= source.length)
33
+ break;
34
+ if (position + 1 >= source.length)
35
+ throw new RangeError("LZ4: incomplete offset");
36
+ const offset = source[position] | source[position + 1] << 8;
37
+ position += 2;
38
+ if (offset === 0)
39
+ throw new RangeError("LZ4: zero offset");
40
+ let matchLength = (token & 15) + 4;
41
+ if ((token & 15) === 15) {
42
+ while (position < source.length) {
43
+ const byte = source[position++];
44
+ if (byte === undefined)
45
+ break;
46
+ matchLength += byte;
47
+ if (byte !== 255)
48
+ break;
49
+ }
50
+ }
51
+ const matchPosition = output.length - offset;
52
+ if (matchPosition < 0)
53
+ throw new RangeError("LZ4: match out of bounds");
54
+ for (let index = 0;index < matchLength; index += 1) {
55
+ output.push(output[matchPosition + index % offset]);
56
+ }
57
+ this.ensureSize(output.length, maxOutput);
58
+ }
59
+ return Uint8Array.from(output);
60
+ }
61
+ ensureSize(size, maximum) {
62
+ if (size > maximum)
63
+ throw new RangeError("LZ4: output too large");
64
+ }
65
+ }
66
+
67
+ class ZstdCompression {
68
+ decompress(source, maxOutput = DEFAULT_MAX_OUTPUT) {
69
+ const output = Bun.zstdDecompressSync(source);
70
+ if (output.byteLength > maxOutput)
71
+ throw new RangeError("Zstd: output too large");
72
+ return output;
73
+ }
74
+ }
75
+ // src/protocol/enums.ts
76
+ var Command = {
77
+ REQUEST: 0,
78
+ RESPONSE: 1,
79
+ EVENT: 2,
80
+ ERROR: 3
81
+ };
82
+ var Opcode = {
83
+ PING: 1,
84
+ DEBUG: 2,
85
+ RECONNECT: 3,
86
+ LOG: 5,
87
+ SESSION_INIT: 6,
88
+ PROFILE: 16,
89
+ AUTH_REQUEST: 17,
90
+ AUTH: 18,
91
+ LOGIN: 19,
92
+ LOGOUT: 20,
93
+ SYNC: 21,
94
+ CONFIG: 22,
95
+ AUTH_CONFIRM: 23,
96
+ PRESET_AVATARS: 25,
97
+ ASSETS_GET: 26,
98
+ ASSETS_UPDATE: 27,
99
+ ASSETS_GET_BY_IDS: 28,
100
+ ASSETS_ADD: 29,
101
+ SEARCH_FEEDBACK: 31,
102
+ CONTACT_INFO: 32,
103
+ CONTACT_ADD: 33,
104
+ CONTACT_UPDATE: 34,
105
+ CONTACT_PRESENCE: 35,
106
+ CONTACT_LIST: 36,
107
+ CONTACT_SEARCH: 37,
108
+ CONTACT_MUTUAL: 38,
109
+ CONTACT_PHOTOS: 39,
110
+ CONTACT_SORT: 40,
111
+ CONTACT_VERIFY: 42,
112
+ REMOVE_CONTACT_PHOTO: 43,
113
+ CONTACT_INFO_BY_PHONE: 46,
114
+ CHAT_INFO: 48,
115
+ CHAT_HISTORY: 49,
116
+ CHAT_MARK: 50,
117
+ CHAT_MEDIA: 51,
118
+ CHAT_DELETE: 52,
119
+ CHATS_LIST: 53,
120
+ CHAT_CLEAR: 54,
121
+ CHAT_UPDATE: 55,
122
+ CHAT_CHECK_LINK: 56,
123
+ CHAT_JOIN: 57,
124
+ CHAT_LEAVE: 58,
125
+ CHAT_MEMBERS: 59,
126
+ PUBLIC_SEARCH: 60,
127
+ CHAT_PERSONAL_CONFIG: 61,
128
+ CHAT_LIVESTREAM_INFO: 62,
129
+ CHAT_CREATE: 63,
130
+ MSG_SEND: 64,
131
+ MSG_TYPING: 65,
132
+ MSG_DELETE: 66,
133
+ MSG_EDIT: 67,
134
+ CHAT_SEARCH: 68,
135
+ MSG_SHARE_PREVIEW: 70,
136
+ MSG_GET: 71,
137
+ MSG_SEARCH_TOUCH: 72,
138
+ MSG_SEARCH: 73,
139
+ MSG_GET_STAT: 74,
140
+ CHAT_SUBSCRIBE: 75,
141
+ VIDEO_CHAT_START: 76,
142
+ CHAT_MEMBERS_UPDATE: 77,
143
+ VIDEO_CHAT_START_ACTIVE: 78,
144
+ VIDEO_CHAT_HISTORY: 79,
145
+ PHOTO_UPLOAD: 80,
146
+ STICKER_UPLOAD: 81,
147
+ VIDEO_UPLOAD: 82,
148
+ VIDEO_PLAY: 83,
149
+ VIDEO_CHAT_CREATE_JOIN_LINK: 84,
150
+ CHAT_PIN_SET_VISIBILITY: 86,
151
+ FILE_UPLOAD: 87,
152
+ FILE_DOWNLOAD: 88,
153
+ LINK_INFO: 89,
154
+ MSG_DELETE_RANGE: 92,
155
+ SESSIONS_INFO: 96,
156
+ SESSIONS_CLOSE: 97,
157
+ PHONE_BIND_REQUEST: 98,
158
+ PHONE_BIND_CONFIRM: 99,
159
+ AUTH_LOGIN_RESTORE_PASSWORD: 101,
160
+ GET_INBOUND_CALLS: 103,
161
+ AUTH_2FA_DETAILS: 104,
162
+ EXTERNAL_CALLBACK: 105,
163
+ AUTH_VALIDATE_PASSWORD: 107,
164
+ AUTH_VALIDATE_HINT: 108,
165
+ AUTH_VERIFY_EMAIL: 109,
166
+ AUTH_CHECK_EMAIL: 110,
167
+ AUTH_SET_2FA: 111,
168
+ AUTH_CREATE_TRACK: 112,
169
+ AUTH_CHECK_PASSWORD: 113,
170
+ AUTH_LOGIN_CHECK_PASSWORD: 115,
171
+ AUTH_LOGIN_PROFILE_DELETE: 116,
172
+ CHAT_COMPLAIN: 117,
173
+ MSG_SEND_CALLBACK: 118,
174
+ SUSPEND_BOT: 119,
175
+ LOCATION_STOP: 124,
176
+ LOCATION_SEND: 125,
177
+ LOCATION_REQUEST: 126,
178
+ GET_LAST_MENTIONS: 127,
179
+ NOTIF_MESSAGE: 128,
180
+ NOTIF_TYPING: 129,
181
+ NOTIF_MARK: 130,
182
+ NOTIF_CONTACT: 131,
183
+ NOTIF_PRESENCE: 132,
184
+ NOTIF_CONFIG: 134,
185
+ NOTIF_CHAT: 135,
186
+ NOTIF_ATTACH: 136,
187
+ NOTIF_CALL_START: 137,
188
+ NOTIF_CONTACT_SORT: 139,
189
+ NOTIF_MSG_DELETE_RANGE: 140,
190
+ NOTIF_MSG_DELETE: 142,
191
+ NOTIF_CALLBACK_ANSWER: 143,
192
+ CHAT_BOT_COMMANDS: 144,
193
+ BOT_INFO: 145,
194
+ NOTIF_LOCATION: 147,
195
+ NOTIF_LOCATION_REQUEST: 148,
196
+ NOTIF_ASSETS_UPDATE: 150,
197
+ NOTIF_DRAFT: 152,
198
+ NOTIF_DRAFT_DISCARD: 153,
199
+ NOTIF_MSG_DELAYED: 154,
200
+ NOTIF_MSG_REACTIONS_CHANGED: 155,
201
+ NOTIF_MSG_YOU_REACTED: 156,
202
+ CALLS_TOKEN: 158,
203
+ NOTIF_PROFILE: 159,
204
+ WEB_APP_INIT_DATA: 160,
205
+ COMPLAIN: 161,
206
+ COMPLAIN_REASONS_GET: 162,
207
+ VIDEO_CHAT_JOIN: 166,
208
+ DRAFT_SAVE: 176,
209
+ DRAFT_DISCARD: 177,
210
+ MSG_REACTION: 178,
211
+ MSG_CANCEL_REACTION: 179,
212
+ MSG_GET_REACTIONS: 180,
213
+ MSG_GET_DETAILED_REACTIONS: 181,
214
+ STICKER_CREATE: 193,
215
+ STICKER_SUGGEST: 194,
216
+ VIDEO_CHAT_MEMBERS: 195,
217
+ CHAT_HIDE: 196,
218
+ CHAT_SEARCH_COMMON_PARTICIPANTS: 198,
219
+ PROFILE_DELETE: 199,
220
+ PROFILE_DELETE_TIME: 200,
221
+ TRANSCRIBE_MEDIA: 202,
222
+ ORG_INFO: 256,
223
+ CHAT_REACTIONS_SETTINGS_SET: 257,
224
+ REACTIONS_SETTINGS_GET_BY_CHAT_ID: 258,
225
+ ASSETS_REMOVE: 259,
226
+ ASSETS_MOVE: 260,
227
+ ASSETS_LIST_MODIFY: 261,
228
+ FOLDERS_GET: 272,
229
+ FOLDERS_GET_BY_ID: 273,
230
+ FOLDERS_UPDATE: 274,
231
+ FOLDERS_REORDER: 275,
232
+ FOLDERS_DELETE: 276,
233
+ NOTIF_FOLDERS: 277,
234
+ GET_QR: 288,
235
+ GET_QR_STATUS: 289,
236
+ AUTH_QR_APPROVE: 290,
237
+ LOGIN_BY_QR: 291,
238
+ NOTIF_BANNERS: 292,
239
+ NOTIF_TRANSCRIPTION: 293,
240
+ CHAT_SUGGEST: 300,
241
+ AUDIO_PLAY: 301,
242
+ BANNERS_GET: 302,
243
+ MSG_DELIVERY: 303,
244
+ SEND_VOTE: 304,
245
+ VOTERS_LIST_BY_ANSWER: 305,
246
+ GET_POLL_UPDATES: 306
247
+ };
248
+ // src/protocol/tcp.ts
249
+ import { decode, encode } from "@msgpack/msgpack";
250
+ var TCP_HEADER_SIZE = 10;
251
+
252
+ class TcpPacketFramer {
253
+ pack(options) {
254
+ if (options.payloadBytes.byteLength > 16777215) {
255
+ throw new RangeError("TCP payload exceeds the 24-bit length field");
256
+ }
257
+ const result = new Uint8Array(TCP_HEADER_SIZE + options.payloadBytes.byteLength);
258
+ const view = new DataView(result.buffer);
259
+ view.setUint8(0, options.ver);
260
+ view.setUint8(1, options.cmd);
261
+ view.setUint16(2, options.seq, false);
262
+ view.setUint16(4, options.opcode, false);
263
+ const packedLength = (options.flags & 255) << 24 | options.payloadBytes.byteLength;
264
+ view.setUint32(6, packedLength >>> 0, false);
265
+ result.set(options.payloadBytes, TCP_HEADER_SIZE);
266
+ return result;
267
+ }
268
+ unpack(data) {
269
+ if (data.byteLength < TCP_HEADER_SIZE)
270
+ return null;
271
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
272
+ const packedLength = view.getUint32(6, false);
273
+ const payloadLength = packedLength & 16777215;
274
+ if (data.byteLength < TCP_HEADER_SIZE + payloadLength)
275
+ return null;
276
+ return {
277
+ header: {
278
+ ver: view.getUint8(0),
279
+ cmd: view.getUint8(1),
280
+ seq: view.getUint16(2, false),
281
+ opcode: view.getUint16(4, false),
282
+ flags: packedLength >>> 24,
283
+ payloadLength
284
+ },
285
+ payloadBytes: data.slice(TCP_HEADER_SIZE, TCP_HEADER_SIZE + payloadLength)
286
+ };
287
+ }
288
+ unpackPayloadLength(data) {
289
+ if (data.byteLength < TCP_HEADER_SIZE)
290
+ return null;
291
+ return new DataView(data.buffer, data.byteOffset, data.byteLength).getUint32(6, false) & 16777215;
292
+ }
293
+ }
294
+
295
+ class MsgpackPayloadCodec {
296
+ encode(payload) {
297
+ if (payload === null || payload === undefined)
298
+ return new Uint8Array;
299
+ return encode(payload, { useBigInt64: true, ignoreUndefined: true });
300
+ }
301
+ decode(payload) {
302
+ if (payload.byteLength === 0)
303
+ return {};
304
+ return normalizeKeys(decode(payload, { useBigInt64: true }));
305
+ }
306
+ }
307
+
308
+ class TcpPayloadDecoder {
309
+ serializer;
310
+ lz4;
311
+ zstd;
312
+ constructor(serializer = new MsgpackPayloadCodec, lz4 = new Lz4BlockCompression, zstd = new ZstdCompression) {
313
+ this.serializer = serializer;
314
+ this.lz4 = lz4;
315
+ this.zstd = zstd;
316
+ }
317
+ decode(payload, flags = 0) {
318
+ if (payload.byteLength === 0)
319
+ return {};
320
+ let decoded = payload;
321
+ if (flags === 255)
322
+ decoded = this.zstd.decompress(payload);
323
+ else if (flags > 127)
324
+ throw new RangeError(`Invalid TCP compression factor: ${flags}`);
325
+ else if (flags > 0)
326
+ decoded = this.lz4.decompress(payload);
327
+ return this.serializer.decode(decoded);
328
+ }
329
+ }
330
+
331
+ class TcpProtocol {
332
+ version = 10;
333
+ framer = new TcpPacketFramer;
334
+ serializer = new MsgpackPayloadCodec;
335
+ payloadDecoder = new TcpPayloadDecoder(this.serializer);
336
+ encode(frame) {
337
+ const payloadBytes = this.serializer.encode(frame.payload);
338
+ return this.framer.pack({
339
+ ver: this.version,
340
+ cmd: frame.cmd ?? 0,
341
+ seq: frame.seq,
342
+ opcode: frame.opcode,
343
+ flags: 0,
344
+ payloadBytes
345
+ });
346
+ }
347
+ decode(raw) {
348
+ const packet = this.framer.unpack(raw);
349
+ if (!packet)
350
+ return { opcode: 0, cmd: 0, seq: null, payload: null, raw: null };
351
+ const payload = this.payloadDecoder.decode(packet.payloadBytes, packet.header.flags);
352
+ return {
353
+ opcode: packet.header.opcode,
354
+ cmd: packet.header.cmd,
355
+ seq: packet.header.seq,
356
+ payload,
357
+ raw: payload
358
+ };
359
+ }
360
+ }
361
+ function normalizeKeys(value) {
362
+ if (Array.isArray(value))
363
+ return value.map(normalizeKeys);
364
+ if (value instanceof Map) {
365
+ const result = Object.create(null);
366
+ for (const [key, item] of value)
367
+ result[normalizeKey(key)] = normalizeKeys(item);
368
+ return result;
369
+ }
370
+ if (value !== null && typeof value === "object" && !(value instanceof Uint8Array)) {
371
+ const result = Object.create(null);
372
+ for (const [key, item] of Object.entries(value))
373
+ result[normalizeKey(key)] = normalizeKeys(item);
374
+ return result;
375
+ }
376
+ return value;
377
+ }
378
+ function normalizeKey(value) {
379
+ if (value instanceof Uint8Array)
380
+ return new TextDecoder().decode(value);
381
+ return String(value);
382
+ }
383
+ // src/protocol/websocket.ts
384
+ import { isInteger, isSafeNumber, parse, stringify } from "lossless-json";
385
+
386
+ class WsProtocol {
387
+ version = 11;
388
+ encode(frame) {
389
+ return stringify({ ...frame, cmd: frame.cmd ?? 0, ver: this.version });
390
+ }
391
+ decode(raw) {
392
+ try {
393
+ const value = parse(raw, undefined, {
394
+ parseNumber: (text) => {
395
+ if (isInteger(text) && !isSafeNumber(text))
396
+ return BigInt(text);
397
+ return Number(text);
398
+ }
399
+ });
400
+ if (!isRecord(value))
401
+ return emptyFrame();
402
+ const opcode = safeHeaderNumber(value.opcode);
403
+ const cmd = safeHeaderNumber(value.cmd ?? 0);
404
+ const seq = value.seq === null || value.seq === undefined ? null : safeHeaderNumber(value.seq);
405
+ const payload = isRecord(value.payload) ? value.payload : null;
406
+ return { opcode, cmd, seq, payload, raw: payload };
407
+ } catch {
408
+ return emptyFrame();
409
+ }
410
+ }
411
+ }
412
+ function safeHeaderNumber(value) {
413
+ const result = typeof value === "bigint" ? Number(value) : value;
414
+ if (typeof result !== "number" || !Number.isSafeInteger(result)) {
415
+ throw new TypeError("Invalid WebSocket frame header");
416
+ }
417
+ return result;
418
+ }
419
+ function isRecord(value) {
420
+ return value !== null && typeof value === "object" && !Array.isArray(value);
421
+ }
422
+ function emptyFrame() {
423
+ return { opcode: 0, cmd: 0, seq: null, payload: null, raw: null };
424
+ }
425
+ // src/types/utils.ts
426
+ function toMaxId(value, field = "id") {
427
+ if (typeof value === "bigint")
428
+ return value;
429
+ if (typeof value === "string" && /^-?\d+$/.test(value))
430
+ return BigInt(value);
431
+ if (typeof value === "number" && Number.isSafeInteger(value))
432
+ return BigInt(value);
433
+ throw new TypeError(`${field} must be a lossless integer`);
434
+ }
435
+ function optionalMaxId(value, field = "id") {
436
+ return value === null || value === undefined ? null : toMaxId(value, field);
437
+ }
438
+ function asRecord(value, field = "payload") {
439
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
440
+ throw new TypeError(`${field} must be an object`);
441
+ }
442
+ return value;
443
+ }
444
+ function optionalRecord(value) {
445
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
446
+ }
447
+ function asString(value, fallback = "") {
448
+ return typeof value === "string" ? value : fallback;
449
+ }
450
+ function asNumber(value, fallback = 0) {
451
+ if (typeof value === "bigint") {
452
+ const number = Number(value);
453
+ return Number.isSafeInteger(number) ? number : fallback;
454
+ }
455
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
456
+ }
457
+ function asBoolean(value, fallback = false) {
458
+ return typeof value === "boolean" ? value : fallback;
459
+ }
460
+ function asArray(value) {
461
+ return Array.isArray(value) ? value : [];
462
+ }
463
+
464
+ // src/types/attachments.ts
465
+ var AttachmentType = {
466
+ PHOTO: "PHOTO",
467
+ VIDEO: "VIDEO",
468
+ FILE: "FILE",
469
+ CONTACT: "CONTACT",
470
+ STICKER: "STICKER",
471
+ AUDIO: "AUDIO",
472
+ CONTROL: "CONTROL",
473
+ INLINE_KEYBOARD: "INLINE_KEYBOARD",
474
+ SHARE: "SHARE",
475
+ CALL: "CALL"
476
+ };
477
+ function parseAttachment(value) {
478
+ const raw = asRecord(value, "attachment");
479
+ return { type: asString(raw.type ?? raw._type, "UNKNOWN"), raw };
480
+ }
481
+ // src/types/models.ts
482
+ var ChatType = {
483
+ DIALOG: "DIALOG",
484
+ CHAT: "CHAT",
485
+ CHANNEL: "CHANNEL"
486
+ };
487
+
488
+ class Message {
489
+ id;
490
+ chatId;
491
+ sender;
492
+ text;
493
+ time;
494
+ type;
495
+ cid;
496
+ attaches;
497
+ stats;
498
+ status;
499
+ reactionInfo;
500
+ options;
501
+ previousMessageId;
502
+ ttl;
503
+ unread;
504
+ mark;
505
+ elements;
506
+ raw;
507
+ actions = null;
508
+ constructor(payload) {
509
+ this.id = toMaxId(payload.id, "message.id");
510
+ this.chatId = optionalMaxId(payload.chatId, "message.chatId");
511
+ this.sender = optionalMaxId(payload.sender, "message.sender");
512
+ this.text = asString(payload.text);
513
+ this.time = asNumber(payload.time);
514
+ this.type = asString(payload.type);
515
+ this.cid = optionalMaxId(payload.cid, "message.cid");
516
+ this.attaches = asArray(payload.attaches).map(parseAttachment);
517
+ this.stats = optionalRecord(payload.stats);
518
+ this.status = typeof payload.status === "string" ? payload.status : null;
519
+ this.reactionInfo = parseReactionInfo(payload.reactionInfo);
520
+ this.options = typeof payload.options === "number" || optionalRecord(payload.options) !== null ? payload.options : null;
521
+ this.previousMessageId = optionalMaxId(payload.prevMessageId, "message.prevMessageId");
522
+ this.ttl = typeof payload.ttl === "boolean" ? payload.ttl : null;
523
+ this.unread = payload.unread === null || payload.unread === undefined ? null : asNumber(payload.unread);
524
+ this.mark = payload.mark === null || payload.mark === undefined ? null : asNumber(payload.mark);
525
+ this.elements = asArray(payload.elements).map((item) => asRecord(item, "element"));
526
+ this.raw = Object.freeze({ ...payload });
527
+ }
528
+ static fromPayload(value) {
529
+ return new Message(asRecord(value, "message"));
530
+ }
531
+ bind(actions) {
532
+ this.actions = actions;
533
+ return this;
534
+ }
535
+ async reply(text, options = {}) {
536
+ return await this.requireActions().sendMessage({
537
+ ...options,
538
+ chatId: this.requireChatId(),
539
+ text,
540
+ replyTo: this.id
541
+ });
542
+ }
543
+ async answer(text, options = {}) {
544
+ return await this.requireActions().sendMessage({
545
+ ...options,
546
+ chatId: this.requireChatId(),
547
+ text
548
+ });
549
+ }
550
+ async forward(chatId, notify = true) {
551
+ return await this.requireActions().forwardMessage({
552
+ chatId,
553
+ messageId: this.id,
554
+ sourceChatId: this.requireChatId(),
555
+ notify
556
+ });
557
+ }
558
+ async edit(text, attachments) {
559
+ return await this.requireActions().editMessage({
560
+ chatId: this.requireChatId(),
561
+ messageId: this.id,
562
+ text,
563
+ ...attachments !== undefined ? { attachments } : {}
564
+ });
565
+ }
566
+ async pin(notifyPin = true) {
567
+ return await this.requireActions().pinMessage({
568
+ chatId: this.requireChatId(),
569
+ messageId: this.id,
570
+ notifyPin
571
+ });
572
+ }
573
+ async delete(forMe = false) {
574
+ return await this.requireActions().deleteMessage({
575
+ chatId: this.requireChatId(),
576
+ messageIds: [this.id],
577
+ forMe
578
+ });
579
+ }
580
+ async read() {
581
+ return await this.requireActions().readMessage(this.id, this.requireChatId());
582
+ }
583
+ async react(reaction) {
584
+ return await this.requireActions().addReaction(this.requireChatId(), this.id, reaction);
585
+ }
586
+ async unreact() {
587
+ return await this.requireActions().removeReaction(this.requireChatId(), this.id);
588
+ }
589
+ async getReactions() {
590
+ return (await this.requireActions().getReactions(this.requireChatId(), [this.id]))?.get(this.id) ?? null;
591
+ }
592
+ requireActions() {
593
+ if (!this.actions)
594
+ throw new Error("Message is not bound to a client");
595
+ return this.actions;
596
+ }
597
+ requireChatId() {
598
+ if (this.chatId === null)
599
+ throw new Error("Message has no chatId");
600
+ return this.chatId;
601
+ }
602
+ }
603
+
604
+ class Chat {
605
+ id;
606
+ type;
607
+ status;
608
+ owner;
609
+ participants;
610
+ title;
611
+ lastMessage;
612
+ pinnedMessage;
613
+ lastEventTime;
614
+ link;
615
+ description;
616
+ raw;
617
+ messageActions = null;
618
+ chatActions = null;
619
+ constructor(payload) {
620
+ this.id = toMaxId(payload.id, "chat.id");
621
+ this.type = asString(payload.type);
622
+ this.status = asString(payload.status);
623
+ this.owner = optionalMaxId(payload.owner, "chat.owner") ?? 0n;
624
+ this.participants = parseParticipants(payload.participants);
625
+ this.title = typeof payload.title === "string" ? payload.title : null;
626
+ this.lastMessage = payload.lastMessage ? Message.fromPayload(payload.lastMessage) : null;
627
+ this.pinnedMessage = payload.pinnedMessage ? Message.fromPayload(payload.pinnedMessage) : null;
628
+ this.lastEventTime = asNumber(payload.lastEventTime);
629
+ this.link = typeof payload.link === "string" ? payload.link : null;
630
+ this.description = typeof payload.description === "string" ? payload.description : null;
631
+ this.raw = Object.freeze({ ...payload });
632
+ }
633
+ static fromPayload(value) {
634
+ return new Chat(asRecord(value, "chat"));
635
+ }
636
+ bind(messageActions, chatActions) {
637
+ this.messageActions = messageActions;
638
+ this.chatActions = chatActions;
639
+ this.lastMessage?.bind(messageActions);
640
+ this.pinnedMessage?.bind(messageActions);
641
+ return this;
642
+ }
643
+ async answer(text, options = {}) {
644
+ return await this.requireMessageActions().sendMessage({ ...options, chatId: this.id, text });
645
+ }
646
+ async history(options = {}) {
647
+ return await this.requireMessageActions().fetchHistory({ ...options, chatId: this.id });
648
+ }
649
+ async getMessage(messageId) {
650
+ return (await this.requireMessageActions().getMessages(this.id, [messageId]))[0] ?? null;
651
+ }
652
+ async getMessages(messageIds) {
653
+ return await this.requireMessageActions().getMessages(this.id, messageIds);
654
+ }
655
+ async leave() {
656
+ const actions = this.requireChatActions();
657
+ if (this.type === ChatType.CHANNEL)
658
+ await actions.leaveChannel(this.id);
659
+ else if (this.type === ChatType.CHAT)
660
+ await actions.leaveGroup(this.id);
661
+ else
662
+ throw new Error("Cannot leave a dialog");
663
+ }
664
+ async delete(forMe = true) {
665
+ return await this.requireChatActions().deleteChat(this.id, forMe);
666
+ }
667
+ async invite(userIds) {
668
+ return await this.requireChatActions().inviteUsersToGroup(this.id, userIds);
669
+ }
670
+ async removeUsers(userIds) {
671
+ return await this.requireChatActions().removeUsersFromGroup(this.id, userIds);
672
+ }
673
+ requireMessageActions() {
674
+ if (!this.messageActions)
675
+ throw new Error("Chat is not bound to a client");
676
+ return this.messageActions;
677
+ }
678
+ requireChatActions() {
679
+ if (!this.chatActions)
680
+ throw new Error("Chat is not bound to a client");
681
+ return this.chatActions;
682
+ }
683
+ }
684
+
685
+ class User {
686
+ id;
687
+ names;
688
+ phone;
689
+ status;
690
+ description;
691
+ gender;
692
+ link;
693
+ webApp;
694
+ raw;
695
+ actions = null;
696
+ constructor(payload) {
697
+ this.id = toMaxId(payload.id, "user.id");
698
+ this.names = asArray(payload.names).map((item) => {
699
+ const name = asRecord(item, "name");
700
+ return {
701
+ firstName: asString(name.firstName),
702
+ lastName: typeof name.lastName === "string" ? name.lastName : null,
703
+ ...typeof name.name === "string" ? { name: name.name } : {}
704
+ };
705
+ });
706
+ this.phone = optionalMaxId(payload.phone, "user.phone");
707
+ this.status = typeof payload.status === "string" ? payload.status : null;
708
+ this.description = typeof payload.description === "string" ? payload.description : null;
709
+ this.gender = typeof payload.gender === "string" || typeof payload.gender === "number" ? payload.gender : null;
710
+ this.link = typeof payload.link === "string" ? payload.link : null;
711
+ this.webApp = typeof payload.webApp === "string" ? payload.webApp : optionalRecord(payload.webApp);
712
+ this.raw = Object.freeze({ ...payload });
713
+ }
714
+ static fromPayload(value) {
715
+ return new User(asRecord(value, "user"));
716
+ }
717
+ bind(actions) {
718
+ this.actions = actions;
719
+ return this;
720
+ }
721
+ async addContact() {
722
+ return await this.requireActions().addContact(this.id);
723
+ }
724
+ async removeContact() {
725
+ return await this.requireActions().removeContact(this.id);
726
+ }
727
+ getChatId(userId) {
728
+ return this.requireActions().getChatId(userId, this.id);
729
+ }
730
+ requireActions() {
731
+ if (!this.actions)
732
+ throw new Error("User is not bound to a client");
733
+ return this.actions;
734
+ }
735
+ }
736
+
737
+ class Profile {
738
+ contact;
739
+ profileOptions;
740
+ raw;
741
+ constructor(payload) {
742
+ this.contact = User.fromPayload(payload.contact);
743
+ const rawOptions = payload.profileOptions ?? payload.options;
744
+ this.profileOptions = Array.isArray(rawOptions) ? rawOptions.map((item) => asNumber(item)) : null;
745
+ this.raw = Object.freeze({ ...payload });
746
+ }
747
+ static fromPayload(value) {
748
+ return new Profile(asRecord(value, "profile"));
749
+ }
750
+ }
751
+ function parseReactionInfo(value) {
752
+ if (value === null || value === undefined)
753
+ return null;
754
+ const payload = asRecord(value, "reactionInfo");
755
+ return {
756
+ totalCount: asNumber(payload.totalCount),
757
+ counters: asArray(payload.counters).map((item) => {
758
+ const counter = asRecord(item, "reactionCounter");
759
+ return { count: asNumber(counter.count), reaction: asString(counter.reaction) };
760
+ }),
761
+ yourReaction: typeof payload.yourReaction === "string" ? payload.yourReaction : null
762
+ };
763
+ }
764
+ function parseReadState(value) {
765
+ const payload = asRecord(value, "readState");
766
+ return { unread: asNumber(payload.unread), mark: asNumber(payload.mark) };
767
+ }
768
+ function parseParticipants(value) {
769
+ const result = new Map;
770
+ if (value instanceof Map) {
771
+ for (const [id, role] of value)
772
+ result.set(toMaxId(id), asNumber(role));
773
+ } else if (value !== null && typeof value === "object") {
774
+ for (const [id, role] of Object.entries(value))
775
+ result.set(toMaxId(id), asNumber(role));
776
+ }
777
+ return result;
778
+ }
779
+ // src/session/store.ts
780
+ import { Database } from "bun:sqlite";
781
+ import { mkdirSync } from "fs";
782
+ import { join } from "path";
783
+
784
+ // src/logging.ts
785
+ var priorities = {
786
+ DEBUG: 10,
787
+ INFO: 20,
788
+ WARN: 30,
789
+ ERROR: 40,
790
+ SILENT: 100
791
+ };
792
+
793
+ class ConsoleLogger {
794
+ level;
795
+ constructor(level) {
796
+ this.level = level;
797
+ }
798
+ debug(message, context) {
799
+ this.write("DEBUG", message, context);
800
+ }
801
+ info(message, context) {
802
+ this.write("INFO", message, context);
803
+ }
804
+ warn(message, context) {
805
+ this.write("WARN", message, context);
806
+ }
807
+ error(message, context) {
808
+ this.write("ERROR", message, context);
809
+ }
810
+ write(level, message, context) {
811
+ if (priorities[level] < priorities[this.level])
812
+ return;
813
+ const suffix = context ? ` ${JSON.stringify(redact(context))}` : "";
814
+ const line = `[tsmax] ${level.toLowerCase()} ${message}${suffix}`;
815
+ if (level === "ERROR")
816
+ console.error(line);
817
+ else if (level === "WARN")
818
+ console.warn(line);
819
+ else
820
+ console.log(line);
821
+ }
822
+ }
823
+ var activeLogger = new ConsoleLogger("INFO");
824
+ function configureLogging(level = "INFO", logger) {
825
+ activeLogger = logger ?? new ConsoleLogger(level);
826
+ return activeLogger;
827
+ }
828
+ function getLogger() {
829
+ return activeLogger;
830
+ }
831
+ function redact(value) {
832
+ if (Array.isArray(value))
833
+ return value.map(redact);
834
+ if (value === null || typeof value !== "object")
835
+ return value;
836
+ const result = {};
837
+ for (const [key, item] of Object.entries(value)) {
838
+ result[key] = /token|password|phone|code/i.test(key) ? "[redacted]" : redact(item);
839
+ }
840
+ return result;
841
+ }
842
+
843
+ // src/session/types.ts
844
+ var DEFAULT_CONFIG_HASH = "00000000-0000000000000000-00000000-0000000000000000-0000000000000000-0-0000000000000000-00000000";
845
+ function defaultSyncState(overrides = {}) {
846
+ return {
847
+ chatsSync: overrides.chatsSync ?? -1,
848
+ contactsSync: overrides.contactsSync ?? -1,
849
+ draftsSync: overrides.draftsSync ?? -1,
850
+ presenceSync: overrides.presenceSync ?? -1,
851
+ configHash: overrides.configHash ?? DEFAULT_CONFIG_HASH
852
+ };
853
+ }
854
+ function resolveSync(saved, overrides = {}) {
855
+ return { ...saved, ...overrides };
856
+ }
857
+
858
+ // src/session/store.ts
859
+ var columns = `token, device_id, phone, mt_instance_id, chats_sync, contacts_sync,
860
+ drafts_sync, presence_sync, config_hash`;
861
+
862
+ class SqliteSessionStore {
863
+ path;
864
+ database = null;
865
+ constructor(workDir, databaseName = "session.db") {
866
+ mkdirSync(workDir, { recursive: true });
867
+ this.path = join(workDir, databaseName);
868
+ }
869
+ async saveSession(session) {
870
+ const db = this.getDatabase();
871
+ db.query(`INSERT OR REPLACE INTO sessions (${columns}) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(session.token, session.deviceId, session.phone, session.mtInstanceId, session.sync.chatsSync, session.sync.contactsSync, session.sync.draftsSync, session.sync.presenceSync, String(session.sync.configHash));
872
+ getLogger().info("session saved");
873
+ }
874
+ async updateToken(oldToken, newToken) {
875
+ this.getDatabase().query("UPDATE sessions SET token = ? WHERE token = ?").run(newToken, oldToken);
876
+ }
877
+ async loadSession() {
878
+ return this.rowToSession(this.getDatabase().query(`SELECT ${columns} FROM sessions LIMIT 1`).get());
879
+ }
880
+ async loadSessionByDeviceId(deviceId) {
881
+ return this.rowToSession(this.getDatabase().query(`SELECT ${columns} FROM sessions WHERE device_id = ?`).get(deviceId));
882
+ }
883
+ async loadSessionByPhone(phone) {
884
+ return this.rowToSession(this.getDatabase().query(`SELECT ${columns} FROM sessions WHERE phone = ?`).get(phone));
885
+ }
886
+ async deleteSession(token) {
887
+ this.getDatabase().query("DELETE FROM sessions WHERE token = ?").run(token);
888
+ }
889
+ async deleteAllSessions() {
890
+ this.getDatabase().run("DELETE FROM sessions");
891
+ }
892
+ async close() {
893
+ this.database?.close();
894
+ this.database = null;
895
+ }
896
+ getDatabase() {
897
+ if (this.database)
898
+ return this.database;
899
+ const database = new Database(this.path, { create: true, strict: true });
900
+ database.run(`CREATE TABLE IF NOT EXISTS sessions (
901
+ token TEXT NOT NULL PRIMARY KEY,
902
+ device_id TEXT NOT NULL,
903
+ phone TEXT NOT NULL,
904
+ mt_instance_id TEXT NOT NULL DEFAULT '',
905
+ chats_sync INTEGER NOT NULL DEFAULT -1,
906
+ contacts_sync INTEGER NOT NULL DEFAULT -1,
907
+ drafts_sync INTEGER NOT NULL DEFAULT -1,
908
+ presence_sync INTEGER NOT NULL DEFAULT -1,
909
+ config_hash TEXT NOT NULL DEFAULT ''
910
+ )`);
911
+ this.ensureColumns(database);
912
+ database.query("UPDATE sessions SET config_hash = ? WHERE config_hash = ''").run(DEFAULT_CONFIG_HASH);
913
+ this.database = database;
914
+ return database;
915
+ }
916
+ ensureColumns(database) {
917
+ const existing = new Set(database.query("PRAGMA table_info(sessions)").all().map((row) => row.name));
918
+ const definitions = {
919
+ mt_instance_id: "TEXT NOT NULL DEFAULT ''",
920
+ chats_sync: "INTEGER NOT NULL DEFAULT -1",
921
+ contacts_sync: "INTEGER NOT NULL DEFAULT -1",
922
+ drafts_sync: "INTEGER NOT NULL DEFAULT -1",
923
+ presence_sync: "INTEGER NOT NULL DEFAULT -1",
924
+ config_hash: "TEXT NOT NULL DEFAULT ''"
925
+ };
926
+ for (const [name, definition] of Object.entries(definitions)) {
927
+ if (!existing.has(name))
928
+ database.run(`ALTER TABLE sessions ADD COLUMN ${name} ${definition}`);
929
+ }
930
+ }
931
+ rowToSession(row) {
932
+ if (!row)
933
+ return null;
934
+ return {
935
+ token: row.token,
936
+ deviceId: row.device_id,
937
+ phone: row.phone,
938
+ mtInstanceId: row.mt_instance_id || "",
939
+ sync: defaultSyncState({
940
+ chatsSync: row.chats_sync,
941
+ contactsSync: row.contacts_sync,
942
+ draftsSync: row.drafts_sync,
943
+ presenceSync: row.presence_sync,
944
+ configHash: row.config_hash || DEFAULT_CONFIG_HASH
945
+ })
946
+ };
947
+ }
948
+ }
949
+ // src/files/file.ts
950
+ import { basename, extname } from "path";
951
+
952
+ class MaxFile {
953
+ raw;
954
+ path;
955
+ url;
956
+ name;
957
+ constructor(source) {
958
+ const sources = [source.raw, source.path, source.url].filter((value) => value !== undefined);
959
+ if (sources.length !== 1)
960
+ throw new TypeError("Exactly one of raw, path, or url must be provided");
961
+ this.raw = source.raw ?? null;
962
+ this.path = source.path ?? null;
963
+ this.url = source.url ?? null;
964
+ this.name = source.name ?? inferName(source.path, source.url);
965
+ if (!this.name)
966
+ throw new TypeError("A name is required for raw file data");
967
+ }
968
+ async read() {
969
+ if (this.raw)
970
+ return this.raw;
971
+ if (this.path)
972
+ return new Uint8Array(await Bun.file(this.path).arrayBuffer());
973
+ if (this.url) {
974
+ const response = await fetch(this.url);
975
+ if (!response.ok)
976
+ throw new Error(`Failed to download ${this.url}: ${response.status}`);
977
+ return new Uint8Array(await response.arrayBuffer());
978
+ }
979
+ throw new Error("File has no source");
980
+ }
981
+ async size() {
982
+ if (this.raw)
983
+ return this.raw.byteLength;
984
+ if (this.path)
985
+ return Bun.file(this.path).size;
986
+ if (this.url) {
987
+ const response = await fetch(this.url, { method: "HEAD" });
988
+ if (!response.ok)
989
+ throw new Error(`Failed to inspect ${this.url}: ${response.status}`);
990
+ const length = response.headers.get("content-length");
991
+ if (!length)
992
+ return (await this.read()).byteLength;
993
+ return Number(length);
994
+ }
995
+ throw new Error("File has no source");
996
+ }
997
+ async* chunks(chunkSize) {
998
+ if (!Number.isInteger(chunkSize) || chunkSize <= 0)
999
+ throw new RangeError("chunkSize must be positive");
1000
+ const data = await this.read();
1001
+ for (let offset = 0;offset < data.byteLength; offset += chunkSize) {
1002
+ yield data.slice(offset, offset + chunkSize);
1003
+ }
1004
+ }
1005
+ }
1006
+
1007
+ class File extends MaxFile {
1008
+ }
1009
+
1010
+ class Video extends MaxFile {
1011
+ }
1012
+ var allowedPhotoExtensions = new Set([".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"]);
1013
+
1014
+ class Photo extends MaxFile {
1015
+ constructor(source) {
1016
+ super(source);
1017
+ const extension = extname(this.url ? new URL(this.url).pathname : this.path ?? this.name).toLowerCase();
1018
+ if (!allowedPhotoExtensions.has(extension)) {
1019
+ throw new TypeError(`Invalid photo extension: ${extension}`);
1020
+ }
1021
+ }
1022
+ get format() {
1023
+ const extension = extname(this.url ? new URL(this.url).pathname : this.path ?? this.name).toLowerCase().slice(1);
1024
+ const normalized = extension === "jpg" ? "jpeg" : extension;
1025
+ return { extension, mimeType: `image/${normalized}` };
1026
+ }
1027
+ }
1028
+ function inferName(path, url) {
1029
+ if (path)
1030
+ return basename(path);
1031
+ if (url)
1032
+ return basename(new URL(url).pathname);
1033
+ return "";
1034
+ }
1035
+ // src/auth/providers.ts
1036
+ import QRCode from "qrcode";
1037
+ var consoleSmsCodeProvider = (phone) => {
1038
+ const code = prompt(`SMS code for ${phone}:`);
1039
+ if (!code)
1040
+ throw new Error("SMS code is required");
1041
+ return code;
1042
+ };
1043
+ var consolePasswordProvider = (hint) => {
1044
+ const password = prompt(`2FA password${hint ? ` (${hint})` : ""}:`);
1045
+ if (!password)
1046
+ throw new Error("2FA password is required");
1047
+ return password;
1048
+ };
1049
+ var consoleEmailCodeProvider = (email) => {
1050
+ const code = prompt(`Email code for ${email}:`);
1051
+ if (!code)
1052
+ throw new Error("Email code is required");
1053
+ return code;
1054
+ };
1055
+ var consoleQrHandler = async (link) => {
1056
+ console.log(await QRCode.toString(link, { type: "terminal", small: true }));
1057
+ console.log(link);
1058
+ };
1059
+
1060
+ // src/auth/flows.ts
1061
+ class SmsAuthFlow {
1062
+ codeProvider;
1063
+ passwordProvider;
1064
+ constructor(codeProvider = consoleSmsCodeProvider, passwordProvider = consolePasswordProvider) {
1065
+ this.codeProvider = codeProvider;
1066
+ this.passwordProvider = passwordProvider;
1067
+ }
1068
+ async authenticate(app) {
1069
+ const phone = app.config.phone;
1070
+ if (!phone)
1071
+ throw new Error("Phone is required for SMS authentication");
1072
+ const start = await app.api.auth.requestCode(phone);
1073
+ const token = typeof start.token === "string" ? start.token : "";
1074
+ if (!token)
1075
+ throw new Error("SMS request returned no token");
1076
+ const result = await app.api.auth.sendCode(token, await this.codeProvider(phone));
1077
+ if (result.loginToken)
1078
+ return { token: result.loginToken };
1079
+ if (result.passwordChallenge) {
1080
+ return {
1081
+ token: await this.passwordLogin(app, result.passwordChallenge.trackId, result.passwordChallenge.hint)
1082
+ };
1083
+ }
1084
+ if (result.registerToken) {
1085
+ const registration = app.config.registrationConfig;
1086
+ if (!registration)
1087
+ throw new Error("registrationConfig is required for a new account");
1088
+ return {
1089
+ token: await app.api.auth.confirmRegistration(registration.firstName, registration.lastName ?? null, result.registerToken)
1090
+ };
1091
+ }
1092
+ throw new Error("Authentication returned no login, registration, or password challenge");
1093
+ }
1094
+ async passwordLogin(app, trackId, hint) {
1095
+ for (;; ) {
1096
+ const result = await app.api.auth.checkPassword(trackId, await this.passwordProvider(hint));
1097
+ if (result.loginToken)
1098
+ return result.loginToken;
1099
+ }
1100
+ }
1101
+ }
1102
+
1103
+ class QrAuthFlow {
1104
+ qrHandler;
1105
+ passwordProvider;
1106
+ constructor(qrHandler = consoleQrHandler, passwordProvider = consolePasswordProvider) {
1107
+ this.qrHandler = qrHandler;
1108
+ this.passwordProvider = passwordProvider;
1109
+ }
1110
+ async authenticate(app) {
1111
+ const request = await app.api.auth.requestQr();
1112
+ await this.qrHandler(request.qrLink);
1113
+ while (Date.now() < request.expiresAt) {
1114
+ const status = await app.api.auth.checkQr(request.trackId);
1115
+ if (status.loginAvailable) {
1116
+ const result = await app.api.auth.confirmQr(request.trackId);
1117
+ if (result.loginToken)
1118
+ return { token: result.loginToken };
1119
+ if (result.passwordChallenge) {
1120
+ for (;; ) {
1121
+ const checked = await app.api.auth.checkPassword(result.passwordChallenge.trackId, await this.passwordProvider(result.passwordChallenge.hint));
1122
+ if (checked.loginToken)
1123
+ return { token: checked.loginToken };
1124
+ }
1125
+ }
1126
+ break;
1127
+ }
1128
+ await Bun.sleep(request.pollingInterval);
1129
+ }
1130
+ throw new Error("QR authentication expired");
1131
+ }
1132
+ }
1133
+ // src/telemetry/navigation.ts
1134
+ var Screen = {
1135
+ BACKGROUND: 1,
1136
+ CONTACTS: 100,
1137
+ CHATS: 150,
1138
+ SEARCH: 151,
1139
+ CALLS: 300,
1140
+ CHAT: 350,
1141
+ SETTINGS: 450,
1142
+ MINIAPP: 500
1143
+ };
1144
+ var mainTabParams = { source_type: 5, source_id: 1, tab_config: 2 };
1145
+ var defaultProfiles = {
1146
+ quick: {
1147
+ steps: 2,
1148
+ minPause: 35,
1149
+ maxPause: 95,
1150
+ longPauseChance: 0.05,
1151
+ minLongPause: 180,
1152
+ maxLongPause: 420,
1153
+ backChance: 0.3
1154
+ },
1155
+ browse: {
1156
+ steps: 4,
1157
+ minPause: 70,
1158
+ maxPause: 210,
1159
+ longPauseChance: 0.12,
1160
+ minLongPause: 240,
1161
+ maxLongPause: 720,
1162
+ backChance: 0.22
1163
+ },
1164
+ read: {
1165
+ steps: 3,
1166
+ minPause: 140,
1167
+ maxPause: 360,
1168
+ longPauseChance: 0.25,
1169
+ minLongPause: 420,
1170
+ maxLongPause: 1200,
1171
+ backChance: 0.18
1172
+ }
1173
+ };
1174
+ var graph = {
1175
+ [Screen.BACKGROUND]: [
1176
+ { screen: Screen.CHATS, weight: 10 },
1177
+ { screen: Screen.SETTINGS, weight: 1 }
1178
+ ],
1179
+ [Screen.CHATS]: [
1180
+ { screen: Screen.CHAT, weight: 7 },
1181
+ { screen: Screen.CONTACTS, weight: 2 },
1182
+ { screen: Screen.SEARCH, weight: 2 },
1183
+ { screen: Screen.CALLS, weight: 1 },
1184
+ { screen: Screen.SETTINGS, weight: 1 },
1185
+ { screen: Screen.CHATS, weight: 2 }
1186
+ ],
1187
+ [Screen.CHAT]: [
1188
+ { screen: Screen.CHATS, weight: 8 },
1189
+ { screen: Screen.CHAT, weight: 2 },
1190
+ { screen: Screen.SETTINGS, weight: 1 }
1191
+ ],
1192
+ [Screen.CONTACTS]: [
1193
+ { screen: Screen.CHATS, weight: 6 },
1194
+ { screen: Screen.CHAT, weight: 2 },
1195
+ { screen: Screen.SEARCH, weight: 1 }
1196
+ ],
1197
+ [Screen.SEARCH]: [
1198
+ { screen: Screen.CHATS, weight: 5 },
1199
+ { screen: Screen.CHAT, weight: 3 },
1200
+ { screen: Screen.CONTACTS, weight: 1 }
1201
+ ],
1202
+ [Screen.CALLS]: [
1203
+ { screen: Screen.CHATS, weight: 5 },
1204
+ { screen: Screen.CONTACTS, weight: 2 },
1205
+ { screen: Screen.SETTINGS, weight: 2 }
1206
+ ],
1207
+ [Screen.SETTINGS]: [
1208
+ { screen: Screen.CHATS, weight: 7 },
1209
+ { screen: Screen.CONTACTS, weight: 2 },
1210
+ { screen: Screen.CALLS, weight: 2 },
1211
+ { screen: Screen.MINIAPP, weight: 1 }
1212
+ ],
1213
+ [Screen.MINIAPP]: [
1214
+ { screen: Screen.SETTINGS, weight: 3 },
1215
+ { screen: Screen.CHATS, weight: 6 }
1216
+ ]
1217
+ };
1218
+
1219
+ class NavigationPlanner {
1220
+ random;
1221
+ currentScreen = Screen.BACKGROUND;
1222
+ history = [];
1223
+ constructor(random = Math) {
1224
+ this.random = random;
1225
+ }
1226
+ newProfile() {
1227
+ const profiles = Object.values(defaultProfiles);
1228
+ return profiles[Math.floor(this.random.random() * profiles.length)];
1229
+ }
1230
+ pause(profile) {
1231
+ const long = this.random.random() < profile.longPauseChance;
1232
+ return this.between(long ? profile.minLongPause : profile.minPause, long ? profile.maxLongPause : profile.maxPause);
1233
+ }
1234
+ nextScreen(profile) {
1235
+ if (this.history.length > 0 && this.random.random() < profile.backChance) {
1236
+ this.currentScreen = this.history.pop();
1237
+ return this.currentScreen;
1238
+ }
1239
+ const transitions = graph[this.currentScreen];
1240
+ const total = transitions.reduce((sum, item) => sum + item.weight, 0);
1241
+ let point = Math.floor(this.random.random() * total) + 1;
1242
+ let selected = transitions[transitions.length - 1];
1243
+ for (const transition of transitions) {
1244
+ point -= transition.weight;
1245
+ if (point <= 0) {
1246
+ selected = transition;
1247
+ break;
1248
+ }
1249
+ }
1250
+ if (selected.screen !== this.currentScreen) {
1251
+ this.history.push(this.currentScreen);
1252
+ if (this.history.length > 4)
1253
+ this.history.shift();
1254
+ }
1255
+ this.currentScreen = selected.screen;
1256
+ return selected.screen;
1257
+ }
1258
+ resetToBackground() {
1259
+ this.currentScreen = Screen.BACKGROUND;
1260
+ this.history.length = 0;
1261
+ }
1262
+ between(minimum, maximum) {
1263
+ return minimum + this.random.random() * (maximum - minimum);
1264
+ }
1265
+ }
1266
+ // src/telemetry/payloads.ts
1267
+ class TelemetryPayloadBuilder {
1268
+ random;
1269
+ constructor(random = Math) {
1270
+ this.random = random;
1271
+ }
1272
+ login(userId, sessionId) {
1273
+ return this.event(userId, sessionId, "PERF", "login", {
1274
+ properties: { connection_type: 2, vpn: 0, class: 2, background: 1, warm_start: 1 },
1275
+ errorType: 100
1276
+ });
1277
+ }
1278
+ navigation(options) {
1279
+ return this.event(options.userId, options.sessionId, "NAV", "GO", {
1280
+ prev_time: options.prevTime,
1281
+ screen_to: options.screenTo,
1282
+ action_id: options.actionId,
1283
+ screen_from: options.screenFrom,
1284
+ ...options.extraParams
1285
+ });
1286
+ }
1287
+ openChat(userId, sessionId) {
1288
+ const messages = this.integer(60, 240);
1289
+ const render = this.integer(50, 260);
1290
+ return this.event(userId, sessionId, "PERF", "open_chat_to_render", {
1291
+ spans: [
1292
+ { duration: messages + render, name: "open_chat_to_render" },
1293
+ { duration: messages, name: "messages_list_created" },
1294
+ { duration: render, name: "messages_render" }
1295
+ ],
1296
+ properties: { class: 2, warm: 1, flow: 1 }
1297
+ });
1298
+ }
1299
+ openChats(userId, sessionId) {
1300
+ const created = this.integer(50, 230);
1301
+ const rendered = this.integer(180, 650);
1302
+ return this.event(userId, sessionId, "PERF", "open_chats_to_render", {
1303
+ spans: [
1304
+ { duration: created + rendered, name: "open_chats_to_render" },
1305
+ { duration: created, name: "chats_tab_created" },
1306
+ { duration: rendered, name: "chat_list_render" }
1307
+ ],
1308
+ properties: { class: 2 }
1309
+ });
1310
+ }
1311
+ toPayload(events) {
1312
+ return { events };
1313
+ }
1314
+ event(userId, sessionId, type, event, params) {
1315
+ return { time: Date.now(), userId, type, event, params, sessionId };
1316
+ }
1317
+ integer(minimum, maximum) {
1318
+ return Math.floor(this.random.random() * (maximum - minimum + 1)) + minimum;
1319
+ }
1320
+ }
1321
+ // src/telemetry/service.ts
1322
+ var defaultTelemetryTiming = {
1323
+ startupDelay: [15, 90],
1324
+ sessionIdleDelay: [900, 2700],
1325
+ renderDelay: [0.15, 1.2],
1326
+ returnDelay: [12, 45],
1327
+ returnToBackgroundChance: 0.4,
1328
+ openChatsRenderChance: 0.2
1329
+ };
1330
+
1331
+ class TelemetryService {
1332
+ app;
1333
+ random;
1334
+ timing;
1335
+ planner;
1336
+ payloads;
1337
+ controller = null;
1338
+ task = null;
1339
+ actionId = 0;
1340
+ sessionId;
1341
+ lastNavTime = Date.now();
1342
+ constructor(app, random = Math, timing = defaultTelemetryTiming) {
1343
+ this.app = app;
1344
+ this.random = random;
1345
+ this.timing = timing;
1346
+ this.planner = new NavigationPlanner(random);
1347
+ this.payloads = new TelemetryPayloadBuilder(random);
1348
+ this.sessionId = app.config.device.clientSessionId;
1349
+ }
1350
+ get running() {
1351
+ return this.task !== null;
1352
+ }
1353
+ get ready() {
1354
+ return this.app.started && this.app.me !== null && this.app.connection.isOpen !== false;
1355
+ }
1356
+ start() {
1357
+ if (this.running || !this.ready)
1358
+ return;
1359
+ const controller = new AbortController;
1360
+ this.controller = controller;
1361
+ this.task = this.run(controller.signal).finally(() => {
1362
+ if (this.controller === controller) {
1363
+ this.controller = null;
1364
+ this.task = null;
1365
+ }
1366
+ });
1367
+ }
1368
+ async stop() {
1369
+ const task = this.task;
1370
+ this.controller?.abort();
1371
+ this.controller = null;
1372
+ if (task)
1373
+ await task.catch(() => {
1374
+ return;
1375
+ });
1376
+ this.task = null;
1377
+ }
1378
+ async sendEvents(events) {
1379
+ if (events.length === 0 || !this.ready)
1380
+ return;
1381
+ await this.app.invoke(Opcode.LOG, this.payloads.toPayload(events), this.app.config.requestTimeout);
1382
+ }
1383
+ sourceParams(screen) {
1384
+ if (screen === Screen.CHATS)
1385
+ return { ...mainTabParams };
1386
+ if (screen !== Screen.CHAT)
1387
+ return {};
1388
+ const chat = this.pickChat();
1389
+ return chat ? { source_type: chat.type === "DIALOG" ? 1 : 2, source_id: chat.id } : { source_type: 1, source_id: this.userId };
1390
+ }
1391
+ async run(signal) {
1392
+ try {
1393
+ await sleep(this.between(this.timing.startupDelay) * 1000, signal);
1394
+ await this.sendEvents([this.payloads.login(this.userId, this.sessionId)]);
1395
+ while (!signal.aborted) {
1396
+ this.sessionId += 1;
1397
+ await this.sendEvents(await this.collectSessionEvents(this.planner.newProfile(), signal));
1398
+ this.planner.resetToBackground();
1399
+ await sleep(this.between(this.timing.sessionIdleDelay) * 1000, signal);
1400
+ }
1401
+ } catch (error) {
1402
+ if (!signal.aborted)
1403
+ getLogger().debug("telemetry loop stopped", { error: String(error) });
1404
+ }
1405
+ }
1406
+ async collectSessionEvents(profile, signal) {
1407
+ const events = [];
1408
+ for (let step = 0;step < profile.steps; step += 1) {
1409
+ await sleep(this.planner.pause(profile) * 1000, signal);
1410
+ if (!this.ready)
1411
+ return events;
1412
+ const from = this.planner.currentScreen;
1413
+ const to = this.planner.nextScreen(profile);
1414
+ events.push(this.navigationEvent(from, to));
1415
+ if (to === Screen.CHAT) {
1416
+ await sleep(this.between(this.timing.renderDelay) * 1000, signal);
1417
+ events.push(this.payloads.openChat(this.userId, this.sessionId));
1418
+ } else if (to === Screen.CHATS && this.random.random() < this.timing.openChatsRenderChance) {
1419
+ await sleep(this.between(this.timing.renderDelay) * 1000, signal);
1420
+ events.push(this.payloads.openChats(this.userId, this.sessionId));
1421
+ }
1422
+ }
1423
+ if (this.planner.currentScreen !== Screen.BACKGROUND && this.random.random() < this.timing.returnToBackgroundChance) {
1424
+ await sleep(this.between(this.timing.returnDelay) * 1000, signal);
1425
+ const from = this.planner.currentScreen;
1426
+ this.planner.resetToBackground();
1427
+ events.push(this.navigationEvent(from, Screen.BACKGROUND));
1428
+ }
1429
+ return events;
1430
+ }
1431
+ navigationEvent(from, to) {
1432
+ this.actionId = this.actionId + 1 >>> 0;
1433
+ const event = this.payloads.navigation({
1434
+ userId: this.userId,
1435
+ sessionId: this.sessionId,
1436
+ screenFrom: from,
1437
+ screenTo: to,
1438
+ prevTime: this.lastNavTime,
1439
+ actionId: this.actionId,
1440
+ extraParams: this.sourceParams(to)
1441
+ });
1442
+ this.lastNavTime = event.time;
1443
+ return event;
1444
+ }
1445
+ get userId() {
1446
+ if (!this.app.me)
1447
+ throw new Error("Telemetry requires an authenticated profile");
1448
+ return this.app.me.contact.id;
1449
+ }
1450
+ pickChat() {
1451
+ if (this.app.chats.length === 0)
1452
+ return null;
1453
+ return this.app.chats[Math.floor(this.random.random() * this.app.chats.length)] ?? null;
1454
+ }
1455
+ between(range) {
1456
+ return range[0] + this.random.random() * (range[1] - range[0]);
1457
+ }
1458
+ }
1459
+ async function sleep(milliseconds, signal) {
1460
+ if (signal.aborted)
1461
+ throw signal.reason;
1462
+ await new Promise((resolve, reject) => {
1463
+ const timer = setTimeout(resolve, milliseconds);
1464
+ signal.addEventListener("abort", () => {
1465
+ clearTimeout(timer);
1466
+ reject(signal.reason);
1467
+ }, { once: true });
1468
+ });
1469
+ }
1470
+ // src/api/response.ts
1471
+ function payload(frame) {
1472
+ return frame.payload ?? {};
1473
+ }
1474
+ function payloadItem(frame, key) {
1475
+ return payload(frame)[key];
1476
+ }
1477
+ function requiredItem(frame, key) {
1478
+ const value = payloadItem(frame, key);
1479
+ if (value === null || value === undefined)
1480
+ throw new TypeError(`Response is missing ${key}`);
1481
+ return value;
1482
+ }
1483
+ function listItem(frame, key) {
1484
+ return asArray(payloadItem(frame, key));
1485
+ }
1486
+
1487
+ // src/api/account.ts
1488
+ class AccountApi {
1489
+ app;
1490
+ uploads;
1491
+ constructor(app, uploads) {
1492
+ this.app = app;
1493
+ this.uploads = uploads;
1494
+ }
1495
+ async requestProfilePhotoUploadUrl() {
1496
+ return asString(requiredItem(await this.app.invoke(Opcode.PHOTO_UPLOAD, { count: 1, profile: true }), "url"));
1497
+ }
1498
+ async changeProfile(options) {
1499
+ let photoToken = options.photoToken ?? null;
1500
+ if (options.photo) {
1501
+ const uploaded = await this.uploads.uploadPhoto(options.photo, true);
1502
+ if (uploaded._type === "PHOTO")
1503
+ photoToken = uploaded.photoToken;
1504
+ }
1505
+ const frame = await this.app.invoke(Opcode.PROFILE, {
1506
+ firstName: options.firstName,
1507
+ lastName: options.lastName ?? null,
1508
+ description: options.description ?? null,
1509
+ photoToken,
1510
+ avatarType: "USER_AVATAR"
1511
+ });
1512
+ this.app.me = Profile.fromPayload(requiredItem(frame, "profile"));
1513
+ return true;
1514
+ }
1515
+ async createFolder(title, chatInclude, filters = []) {
1516
+ return payload(await this.app.invoke(Opcode.FOLDERS_UPDATE, {
1517
+ id: crypto.randomUUID(),
1518
+ title,
1519
+ include: chatInclude,
1520
+ filters
1521
+ }));
1522
+ }
1523
+ async getFolders(folderSync = 0) {
1524
+ return payload(await this.app.invoke(Opcode.FOLDERS_GET, { folderSync }));
1525
+ }
1526
+ async updateFolder(folderId, title, chatInclude = [], filters = [], options = []) {
1527
+ return payload(await this.app.invoke(Opcode.FOLDERS_UPDATE, {
1528
+ id: folderId,
1529
+ title,
1530
+ include: chatInclude,
1531
+ filters,
1532
+ options
1533
+ }));
1534
+ }
1535
+ async deleteFolder(folderId) {
1536
+ return payload(await this.app.invoke(Opcode.FOLDERS_DELETE, { folderIds: [folderId] }));
1537
+ }
1538
+ async closeAllSessions() {
1539
+ if (!this.app.session)
1540
+ return false;
1541
+ const data = payload(await this.app.invoke(Opcode.SESSIONS_CLOSE, {}));
1542
+ const token = asString(data.token);
1543
+ if (!token)
1544
+ return false;
1545
+ await this.app.store.updateToken(this.app.session.token, token);
1546
+ this.app.session = { ...this.app.session, token };
1547
+ return true;
1548
+ }
1549
+ async logout() {
1550
+ await this.app.invoke(Opcode.LOGOUT, {});
1551
+ return true;
1552
+ }
1553
+ }
1554
+ // src/config.ts
1555
+ var DeviceType = {
1556
+ WEB: "WEB",
1557
+ ANDROID: "ANDROID",
1558
+ IOS: "IOS",
1559
+ DESKTOP: "DESKTOP"
1560
+ };
1561
+ var androidAgents = [
1562
+ {
1563
+ deviceType: DeviceType.ANDROID,
1564
+ osVersion: "Android 14",
1565
+ timezone: "Europe/Moscow",
1566
+ screen: "411dpi 411dpi 1080x2400",
1567
+ pushDeviceType: "GCM",
1568
+ arch: "arm64-v8a",
1569
+ locale: "ru",
1570
+ deviceName: "Pixel 8",
1571
+ deviceLocale: "ru"
1572
+ },
1573
+ {
1574
+ deviceType: DeviceType.ANDROID,
1575
+ osVersion: "Android 14",
1576
+ timezone: "Europe/Moscow",
1577
+ screen: "425dpi 425dpi 1080x2340",
1578
+ pushDeviceType: "GCM",
1579
+ arch: "arm64-v8a",
1580
+ locale: "ru",
1581
+ deviceName: "Samsung SM-S911B",
1582
+ deviceLocale: "ru"
1583
+ }
1584
+ ];
1585
+ function resolveConfig(options) {
1586
+ const extra = options.extra ?? {};
1587
+ const userAgent = extra.userAgent ?? (options.web ? createWebAgent() : createMobileAgent());
1588
+ return {
1589
+ phone: options.phone ?? null,
1590
+ workDir: options.workDir ?? ".",
1591
+ sessionName: options.sessionName ?? "session.db",
1592
+ token: extra.token ?? null,
1593
+ registrationConfig: extra.registrationConfig ?? null,
1594
+ host: extra.host ?? "api.oneme.ru",
1595
+ port: extra.port ?? 443,
1596
+ url: extra.url ?? "wss://ws-api.oneme.ru/websocket",
1597
+ useTls: extra.useTls ?? true,
1598
+ proxy: extra.proxy ?? null,
1599
+ reconnect: extra.reconnect ?? true,
1600
+ reconnectDelay: extra.reconnectDelay ?? 1,
1601
+ requestTimeout: extra.requestTimeout ?? 30,
1602
+ logLevel: extra.logLevel ?? "INFO",
1603
+ telemetry: extra.telemetry ?? true,
1604
+ ...extra.store ? { store: extra.store } : {},
1605
+ sync: extra.sync ?? defaultSyncState(),
1606
+ device: {
1607
+ deviceId: extra.deviceId ?? crypto.randomUUID(),
1608
+ mtInstanceId: extra.mtInstanceId ?? crypto.randomUUID(),
1609
+ clientSessionId: Math.floor(Math.random() * 70) + 1,
1610
+ userAgent
1611
+ }
1612
+ };
1613
+ }
1614
+ function createMobileAgent() {
1615
+ const base = androidAgents[Math.floor(Math.random() * androidAgents.length)];
1616
+ return { ...base, appVersion: "26.14.1", buildNumber: 6686 };
1617
+ }
1618
+ function createWebAgent() {
1619
+ return {
1620
+ deviceType: DeviceType.WEB,
1621
+ appVersion: "26.5.5",
1622
+ osVersion: "Linux",
1623
+ timezone: "Europe/Moscow",
1624
+ screen: "1080x1920 1.0x",
1625
+ locale: "ru",
1626
+ deviceName: "Chrome",
1627
+ deviceLocale: "ru",
1628
+ headerUserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36"
1629
+ };
1630
+ }
1631
+
1632
+ // src/api/auth.ts
1633
+ class AuthApi {
1634
+ app;
1635
+ constructor(app) {
1636
+ this.app = app;
1637
+ }
1638
+ async requestCode(phone) {
1639
+ const frame = await this.app.invoke(Opcode.AUTH_REQUEST, {
1640
+ phone,
1641
+ type: "START_AUTH",
1642
+ language: "ru"
1643
+ });
1644
+ return payload(frame);
1645
+ }
1646
+ async sendCode(token, verifyCode) {
1647
+ const frame = await this.app.invoke(Opcode.AUTH, {
1648
+ token,
1649
+ verifyCode,
1650
+ authTokenType: "CHECK_CODE"
1651
+ });
1652
+ return parseCodeResult(payload(frame));
1653
+ }
1654
+ async checkPassword(trackId, password) {
1655
+ const frame = await this.app.invoke(Opcode.AUTH_LOGIN_CHECK_PASSWORD, { trackId, password });
1656
+ const data = payload(frame);
1657
+ return { ...parseCodeResult(data), error: typeof data.error === "string" ? data.error : null };
1658
+ }
1659
+ async requestQr() {
1660
+ const data = payload(await this.app.invoke(Opcode.GET_QR, {}));
1661
+ return {
1662
+ expiresAt: asNumber(data.expiresAt),
1663
+ pollingInterval: asNumber(data.pollingInterval),
1664
+ qrLink: asString(data.qrLink),
1665
+ trackId: asString(data.trackId),
1666
+ ttl: asNumber(data.ttl)
1667
+ };
1668
+ }
1669
+ async checkQr(trackId) {
1670
+ const data = asRecord(requiredItem(await this.app.invoke(Opcode.GET_QR_STATUS, { trackId }), "status"));
1671
+ return { expiresAt: asNumber(data.expiresAt), loginAvailable: data.loginAvailable === true };
1672
+ }
1673
+ async confirmQr(trackId) {
1674
+ return parseCodeResult(payload(await this.app.invoke(Opcode.LOGIN_BY_QR, { trackId })));
1675
+ }
1676
+ async confirmRegistration(firstName, lastName, token) {
1677
+ const data = payload(await this.app.invoke(Opcode.AUTH_CONFIRM, {
1678
+ firstName,
1679
+ lastName,
1680
+ token,
1681
+ tokenType: "REGISTER"
1682
+ }));
1683
+ return asString(data.token);
1684
+ }
1685
+ async handshake(deviceId) {
1686
+ const { userAgent, mtInstanceId, clientSessionId } = this.app.config.device;
1687
+ if (userAgent.deviceType === DeviceType.WEB) {
1688
+ await this.app.invoke(Opcode.SESSION_INIT, {
1689
+ userAgent: webUserAgent(userAgent),
1690
+ deviceId
1691
+ });
1692
+ } else {
1693
+ await this.app.invoke(Opcode.SESSION_INIT, {
1694
+ mt_instanceid: mtInstanceId,
1695
+ userAgent,
1696
+ clientSessionId,
1697
+ deviceId
1698
+ });
1699
+ }
1700
+ }
1701
+ async login() {
1702
+ const session = this.app.session;
1703
+ if (!session)
1704
+ throw new Error("No session available for login");
1705
+ const sync = resolveSync(session.sync, this.app.config.sync);
1706
+ const userAgent = this.app.config.device.userAgent;
1707
+ const loginPayload = userAgent.deviceType === DeviceType.WEB ? {
1708
+ token: session.token,
1709
+ chatsCount: 40,
1710
+ interactive: true,
1711
+ chatsSync: sync.chatsSync,
1712
+ contactsSync: sync.contactsSync,
1713
+ presenceSync: sync.presenceSync,
1714
+ draftsSync: sync.draftsSync
1715
+ } : {
1716
+ userAgent,
1717
+ token: session.token,
1718
+ chatsSync: sync.chatsSync,
1719
+ contactsSync: sync.contactsSync,
1720
+ draftsSync: sync.draftsSync,
1721
+ presenceSync: sync.presenceSync,
1722
+ interactive: true,
1723
+ exp: { chatsCountGroups: Uint8Array.from([10, 50]) },
1724
+ configHash: sync.configHash
1725
+ };
1726
+ const result = parseLogin(payload(await this.app.invoke(Opcode.LOGIN, loginPayload)), this.app);
1727
+ const updatedSync = defaultSyncState({
1728
+ chatsSync: result.time ?? session.sync.chatsSync,
1729
+ contactsSync: result.time ?? session.sync.contactsSync,
1730
+ draftsSync: result.time ?? session.sync.draftsSync,
1731
+ presenceSync: result.time ?? session.sync.presenceSync,
1732
+ configHash: result.configHash ?? session.sync.configHash
1733
+ });
1734
+ this.app.session = {
1735
+ ...session,
1736
+ mtInstanceId: this.app.config.device.mtInstanceId,
1737
+ sync: updatedSync
1738
+ };
1739
+ await this.app.store.saveSession(this.app.session);
1740
+ return result;
1741
+ }
1742
+ async createTrack() {
1743
+ const data = payload(await this.app.invoke(Opcode.AUTH_CREATE_TRACK, { type: 0 }));
1744
+ const trackId = asString(data.trackId);
1745
+ if (!trackId)
1746
+ throw new Error("Failed to create auth track");
1747
+ return trackId;
1748
+ }
1749
+ async setTwoFactor(options) {
1750
+ const trackId = await this.createTrack();
1751
+ await this.app.invoke(Opcode.AUTH_VALIDATE_PASSWORD, { trackId, password: options.password });
1752
+ const capabilities = [1];
1753
+ if (options.email !== undefined) {
1754
+ await this.app.invoke(Opcode.AUTH_VERIFY_EMAIL, { trackId, email: options.email });
1755
+ if (!options.emailCodeProvider)
1756
+ throw new Error("emailCodeProvider is required");
1757
+ const verifyCode = await options.emailCodeProvider(options.email);
1758
+ await this.app.invoke(Opcode.AUTH_CHECK_EMAIL, { trackId, verifyCode });
1759
+ capabilities.push(3);
1760
+ }
1761
+ if (options.hint !== undefined) {
1762
+ await this.app.invoke(Opcode.AUTH_VALIDATE_HINT, { trackId, hint: options.hint });
1763
+ capabilities.push(2);
1764
+ }
1765
+ await this.app.invoke(Opcode.AUTH_SET_2FA, {
1766
+ trackId,
1767
+ password: options.password,
1768
+ hint: options.hint ?? null,
1769
+ expectedCapabilities: capabilities
1770
+ });
1771
+ return true;
1772
+ }
1773
+ async removeTwoFactor(password) {
1774
+ const trackId = await this.createTrack();
1775
+ await this.app.invoke(Opcode.AUTH_CHECK_PASSWORD, { trackId, password });
1776
+ await this.app.invoke(Opcode.AUTH_SET_2FA, {
1777
+ trackId,
1778
+ remove2fa: true,
1779
+ expectedCapabilities: [4]
1780
+ });
1781
+ return true;
1782
+ }
1783
+ async changePassword(oldPassword, newPassword) {
1784
+ const trackId = await this.createTrack();
1785
+ await this.app.invoke(Opcode.AUTH_CHECK_PASSWORD, { trackId, password: oldPassword });
1786
+ await this.app.invoke(Opcode.AUTH_VALIDATE_PASSWORD, { trackId, password: newPassword });
1787
+ await this.app.invoke(Opcode.AUTH_SET_2FA, {
1788
+ trackId,
1789
+ password: newPassword,
1790
+ hint: null,
1791
+ expectedCapabilities: [5]
1792
+ });
1793
+ return true;
1794
+ }
1795
+ async authorizeQrLogin(qrLink) {
1796
+ await this.app.invoke(Opcode.AUTH_QR_APPROVE, { qrLink });
1797
+ return true;
1798
+ }
1799
+ async checkTwoFactor() {
1800
+ return this.app.me?.profileOptions?.includes(2) ?? false;
1801
+ }
1802
+ }
1803
+ function parseCodeResult(data) {
1804
+ const attrs = data.tokenAttrs ? asRecord(data.tokenAttrs) : {};
1805
+ const login = attrs.LOGIN ? asRecord(attrs.LOGIN) : null;
1806
+ const register = attrs.REGISTER ? asRecord(attrs.REGISTER) : null;
1807
+ const challenge = data.passwordChallenge ? asRecord(data.passwordChallenge) : null;
1808
+ return {
1809
+ loginToken: login ? asString(login.token) || null : null,
1810
+ registerToken: register ? asString(register.token) || null : null,
1811
+ passwordChallenge: challenge ? {
1812
+ trackId: asString(challenge.trackId),
1813
+ hint: typeof challenge.hint === "string" ? challenge.hint : null
1814
+ } : null
1815
+ };
1816
+ }
1817
+ function parseLogin(data, app) {
1818
+ const profile = Profile.fromPayload(data.profile);
1819
+ app.bindUser(profile.contact);
1820
+ const chats = asArray(data.chats).map((item) => app.bindChat(Chat.fromPayload(item)));
1821
+ const contacts = asArray(data.contacts).map((item) => item ? app.bindUser(User.fromPayload(item)) : null);
1822
+ const messages = new Map;
1823
+ if (data.messages && typeof data.messages === "object") {
1824
+ for (const [chatId, items] of Object.entries(data.messages)) {
1825
+ messages.set(toMaxId(chatId), asArray(items).map((item) => app.bindMessage(Message.fromPayload(item))));
1826
+ }
1827
+ }
1828
+ const config = data.config ? asRecord(data.config) : null;
1829
+ return {
1830
+ chats,
1831
+ profile,
1832
+ messages,
1833
+ contacts,
1834
+ token: typeof data.token === "string" ? data.token : null,
1835
+ time: data.time === null || data.time === undefined ? null : asNumber(data.time),
1836
+ configHash: typeof config?.hash === "string" || typeof config?.hash === "number" ? config.hash : null
1837
+ };
1838
+ }
1839
+ function webUserAgent(agent) {
1840
+ const {
1841
+ deviceType,
1842
+ locale,
1843
+ deviceLocale,
1844
+ osVersion,
1845
+ deviceName,
1846
+ headerUserAgent,
1847
+ appVersion,
1848
+ screen,
1849
+ timezone
1850
+ } = agent;
1851
+ return {
1852
+ deviceType,
1853
+ locale,
1854
+ deviceLocale,
1855
+ osVersion,
1856
+ deviceName,
1857
+ appVersion,
1858
+ screen,
1859
+ timezone,
1860
+ ...headerUserAgent ? { headerUserAgent } : {}
1861
+ };
1862
+ }
1863
+ // src/api/bots.ts
1864
+ class BotsApi {
1865
+ app;
1866
+ constructor(app) {
1867
+ this.app = app;
1868
+ }
1869
+ async getBotInitData(botId, chatId, startParam) {
1870
+ return payload(await this.app.invoke(Opcode.WEB_APP_INIT_DATA, {
1871
+ botId,
1872
+ chatId: chatId ?? null,
1873
+ startParam: startParam ?? null
1874
+ }));
1875
+ }
1876
+ }
1877
+ // src/errors.ts
1878
+ class TsMaxError extends Error {
1879
+ constructor(message, options) {
1880
+ super(message, options);
1881
+ this.name = new.target.name;
1882
+ }
1883
+ }
1884
+
1885
+ class UploadError extends TsMaxError {
1886
+ }
1887
+
1888
+ class ConnectionError extends TsMaxError {
1889
+ }
1890
+
1891
+ class TimeoutError extends TsMaxError {
1892
+ }
1893
+
1894
+ class ApiError extends TsMaxError {
1895
+ opcode;
1896
+ payload;
1897
+ constructor(opcode, options = {}) {
1898
+ const parts = [];
1899
+ if (options.localizedMessage)
1900
+ parts.push(options.localizedMessage);
1901
+ if (options.message && options.message !== options.localizedMessage)
1902
+ parts.push(options.message);
1903
+ if (options.title)
1904
+ parts.push(`(${options.title})`);
1905
+ if (options.error)
1906
+ parts.push(`[${options.error}]`);
1907
+ super(parts.join(" ") || "API request failed");
1908
+ this.opcode = opcode;
1909
+ this.payload = options.payload ?? {};
1910
+ this.error = options.error ?? null;
1911
+ this.title = options.title ?? null;
1912
+ this.localizedMessage = options.localizedMessage ?? null;
1913
+ }
1914
+ error;
1915
+ title;
1916
+ localizedMessage;
1917
+ }
1918
+
1919
+ // src/api/chats.ts
1920
+ class ChatApi {
1921
+ app;
1922
+ cache = new Map;
1923
+ constructor(app) {
1924
+ this.app = app;
1925
+ }
1926
+ seed(chats) {
1927
+ for (const chat of chats)
1928
+ this.cache.set(chat.id, this.app.bindChat(chat));
1929
+ }
1930
+ async createGroup(name, participantIds = [], notify = true) {
1931
+ const frame = await this.app.invoke(Opcode.MSG_SEND, {
1932
+ message: {
1933
+ cid: BigInt(Date.now()),
1934
+ attaches: [
1935
+ {
1936
+ _type: "CONTROL",
1937
+ event: "new",
1938
+ chatType: "CHAT",
1939
+ title: name,
1940
+ userIds: participantIds
1941
+ }
1942
+ ]
1943
+ },
1944
+ notify
1945
+ });
1946
+ const chatRaw = payloadItem(frame, "chat");
1947
+ if (!chatRaw)
1948
+ return null;
1949
+ const chat = this.cacheChat(Chat.fromPayload(chatRaw));
1950
+ const data = payload(frame);
1951
+ const message = this.app.bindMessage(Message.fromPayload(data.message ?? data));
1952
+ return [chat, message];
1953
+ }
1954
+ async inviteUsersToGroup(chatId, userIds, showHistory = true) {
1955
+ return await this.memberAction(chatId, userIds, "add", showHistory);
1956
+ }
1957
+ async inviteUsersToChannel(chatId, userIds, showHistory = true) {
1958
+ return await this.inviteUsersToGroup(chatId, userIds, showHistory);
1959
+ }
1960
+ async removeUsersFromGroup(chatId, userIds, cleanMsgPeriod = 0) {
1961
+ const frame = await this.app.invoke(Opcode.CHAT_MEMBERS_UPDATE, {
1962
+ chatId,
1963
+ userIds,
1964
+ operation: "remove",
1965
+ cleanMsgPeriod
1966
+ });
1967
+ return this.optionalChat(payloadItem(frame, "chat"));
1968
+ }
1969
+ async changeGroupSettings(chatId, settings) {
1970
+ const options = {};
1971
+ const keys = [
1972
+ ["allCanPinMessage", "ALL_CAN_PIN_MESSAGE"],
1973
+ ["onlyOwnerCanChangeIconTitle", "ONLY_OWNER_CAN_CHANGE_ICON_TITLE"],
1974
+ ["onlyAdminCanAddMember", "ONLY_ADMIN_CAN_ADD_MEMBER"],
1975
+ ["onlyAdminCanCall", "ONLY_ADMIN_CAN_CALL"],
1976
+ ["membersCanSeePrivateLink", "MEMBERS_CAN_SEE_PRIVATE_LINK"]
1977
+ ];
1978
+ for (const [source, target] of keys) {
1979
+ const value = settings[source];
1980
+ if (typeof value === "boolean")
1981
+ options[target] = value;
1982
+ }
1983
+ const frame = await this.app.invoke(Opcode.CHAT_UPDATE, { chatId, options });
1984
+ this.optionalChat(payloadItem(frame, "chat"));
1985
+ }
1986
+ async changeGroupProfile(chatId, name, description = null) {
1987
+ const frame = await this.app.invoke(Opcode.CHAT_UPDATE, { chatId, theme: name, description });
1988
+ this.optionalChat(payloadItem(frame, "chat"));
1989
+ }
1990
+ async joinGroup(link) {
1991
+ const processed = processJoinLink(link);
1992
+ if (!processed)
1993
+ throw new TypeError("Invalid group link");
1994
+ return await this.join(processed);
1995
+ }
1996
+ async joinChannel(link) {
1997
+ return await this.join(processJoinLink(link) ?? link);
1998
+ }
1999
+ async resolveGroupByLink(link) {
2000
+ const processed = processJoinLink(link);
2001
+ if (!processed)
2002
+ throw new TypeError("Invalid group link");
2003
+ const frame = await this.app.invoke(Opcode.LINK_INFO, { link: processed });
2004
+ return this.optionalChat(payloadItem(frame, "chat"));
2005
+ }
2006
+ async reworkInviteLink(chatId) {
2007
+ const frame = await this.app.invoke(Opcode.CHAT_UPDATE, { revokePrivateLink: true, chatId });
2008
+ const chat = this.optionalChat(payloadItem(frame, "chat"));
2009
+ if (!chat)
2010
+ throw new TsMaxError("Chat not found in response");
2011
+ return chat;
2012
+ }
2013
+ async getChats(chatIds) {
2014
+ const missing = chatIds.filter((id) => !this.cache.has(id));
2015
+ if (missing.length > 0) {
2016
+ const frame = await this.app.invoke(Opcode.CHAT_INFO, { chatIds: missing });
2017
+ for (const raw of listItem(frame, "chats"))
2018
+ this.cacheChat(Chat.fromPayload(raw));
2019
+ }
2020
+ return chatIds.flatMap((id) => {
2021
+ const chat = this.cache.get(id);
2022
+ return chat ? [chat] : [];
2023
+ });
2024
+ }
2025
+ async getChat(chatId) {
2026
+ const chat = (await this.getChats([chatId]))[0];
2027
+ if (!chat)
2028
+ throw new TsMaxError("Chat not found in response");
2029
+ return chat;
2030
+ }
2031
+ async fetchChats(marker = Date.now()) {
2032
+ const frame = await this.app.invoke(Opcode.CHATS_LIST, { marker });
2033
+ return listItem(frame, "chats").map((raw) => this.cacheChat(Chat.fromPayload(raw)));
2034
+ }
2035
+ async leaveGroup(chatId) {
2036
+ await this.app.invoke(Opcode.CHAT_LEAVE, { chatId });
2037
+ this.cache.delete(chatId);
2038
+ }
2039
+ async leaveChannel(chatId) {
2040
+ await this.leaveGroup(chatId);
2041
+ }
2042
+ async deleteChat(chatId, forMe = true, lastEventTime = Date.now()) {
2043
+ await this.app.invoke(Opcode.CHAT_DELETE, { chatId, lastEventTime, forAll: !forMe });
2044
+ this.cache.delete(chatId);
2045
+ return true;
2046
+ }
2047
+ async getJoinRequests(chatId, count = 100) {
2048
+ return listItem(await this.app.invoke(Opcode.CHAT_MEMBERS, { chatId, type: "JOIN_REQUEST", count }), "members").map((item) => asRecord(item));
2049
+ }
2050
+ async confirmJoinRequests(chatId, userIds, showHistory = true) {
2051
+ return await this.memberAction(chatId, userIds, "add", showHistory, "JOIN_REQUEST");
2052
+ }
2053
+ async confirmJoinRequest(chatId, userId, showHistory = true) {
2054
+ return await this.confirmJoinRequests(chatId, [userId], showHistory);
2055
+ }
2056
+ async declineJoinRequests(chatId, userIds) {
2057
+ return await this.memberAction(chatId, userIds, "remove", null, "JOIN_REQUEST");
2058
+ }
2059
+ async declineJoinRequest(chatId, userId) {
2060
+ return await this.declineJoinRequests(chatId, [userId]);
2061
+ }
2062
+ async join(link) {
2063
+ const frame = await this.app.invoke(Opcode.CHAT_JOIN, { link });
2064
+ const chat = this.optionalChat(payloadItem(frame, "chat"));
2065
+ if (!chat)
2066
+ throw new TsMaxError("Chat not found in response");
2067
+ return chat;
2068
+ }
2069
+ async memberAction(chatId, userIds, operation, showHistory, type) {
2070
+ const frame = await this.app.invoke(Opcode.CHAT_MEMBERS_UPDATE, {
2071
+ chatId,
2072
+ userIds,
2073
+ operation,
2074
+ showHistory,
2075
+ ...type ? { type } : {}
2076
+ });
2077
+ return this.optionalChat(payloadItem(frame, "chat"));
2078
+ }
2079
+ optionalChat(raw) {
2080
+ return raw ? this.cacheChat(Chat.fromPayload(raw)) : null;
2081
+ }
2082
+ cacheChat(chat) {
2083
+ const bound = this.app.bindChat(chat);
2084
+ this.cache.set(bound.id, bound);
2085
+ return bound;
2086
+ }
2087
+ }
2088
+ function processJoinLink(link) {
2089
+ const index = link.indexOf("join/");
2090
+ return index >= 0 ? link.slice(index) : null;
2091
+ }
2092
+ // src/formatting/markdown.ts
2093
+ var markers = new Map([
2094
+ ["```", "CODE"],
2095
+ ["**", "STRONG"],
2096
+ ["__", "UNDERLINE"],
2097
+ ["~~", "STRIKETHROUGH"],
2098
+ ["`", "MONOSPACED"],
2099
+ ["_", "EMPHASIZED"],
2100
+ ["*", "EMPHASIZED"]
2101
+ ]);
2102
+ function formatMarkdown(text) {
2103
+ let clean = "";
2104
+ let index = 0;
2105
+ let cleanPosition = 0;
2106
+ let lineStart = true;
2107
+ const active = new Map;
2108
+ const elements = [];
2109
+ while (index < text.length) {
2110
+ const link = parseLink(text, index);
2111
+ if (link) {
2112
+ const length = utf16Length(link.label);
2113
+ elements.push({ type: "LINK", from: cleanPosition, length, attributes: { url: link.url } });
2114
+ clean += link.label;
2115
+ cleanPosition += length;
2116
+ index = link.next;
2117
+ lineStart = false;
2118
+ continue;
2119
+ }
2120
+ const current = text[index];
2121
+ if (lineStart && (current === "#" || current === ">")) {
2122
+ const result = parseLineEntity(text, index, current === "#" ? "HEADING" : "QUOTE");
2123
+ if (result) {
2124
+ clean += result.value;
2125
+ const length = utf16Length(result.value);
2126
+ elements.push({ type: result.type, from: cleanPosition, length });
2127
+ cleanPosition += length;
2128
+ index = result.next;
2129
+ lineStart = false;
2130
+ continue;
2131
+ }
2132
+ }
2133
+ let handled = false;
2134
+ for (const [marker, type] of markers) {
2135
+ if (!text.startsWith(marker, index))
2136
+ continue;
2137
+ const open = active.get(marker);
2138
+ if (open === undefined) {
2139
+ const closing = text.indexOf(marker, index + marker.length);
2140
+ const newline = text.indexOf(`
2141
+ `, index + marker.length);
2142
+ if (closing <= index + marker.length || marker !== "```" && newline !== -1 && closing > newline) {
2143
+ clean += marker;
2144
+ cleanPosition += utf16Length(marker);
2145
+ } else {
2146
+ active.set(marker, cleanPosition);
2147
+ }
2148
+ } else {
2149
+ const length = cleanPosition - open;
2150
+ if (length > 0)
2151
+ elements.push({ type, from: open, length });
2152
+ active.delete(marker);
2153
+ }
2154
+ index += marker.length;
2155
+ handled = true;
2156
+ break;
2157
+ }
2158
+ if (handled) {
2159
+ lineStart = false;
2160
+ continue;
2161
+ }
2162
+ clean += current;
2163
+ cleanPosition += utf16Length(current);
2164
+ index += 1;
2165
+ lineStart = current === `
2166
+ `;
2167
+ }
2168
+ return { text: clean, elements };
2169
+ }
2170
+ function parseLink(text, index) {
2171
+ if (text[index] !== "[")
2172
+ return null;
2173
+ const labelEnd = text.indexOf("]", index + 1);
2174
+ if (labelEnd < 0 || text[labelEnd + 1] !== "(")
2175
+ return null;
2176
+ const urlEnd = text.indexOf(")", labelEnd + 2);
2177
+ if (urlEnd < 0)
2178
+ return null;
2179
+ const label = text.slice(index + 1, labelEnd);
2180
+ const url = text.slice(labelEnd + 2, urlEnd);
2181
+ return label && url ? { label, url, next: urlEnd + 1 } : null;
2182
+ }
2183
+ function parseLineEntity(text, index, type) {
2184
+ let contentStart = index + 1;
2185
+ if (type === "HEADING") {
2186
+ while (text[contentStart] === "#")
2187
+ contentStart += 1;
2188
+ if (text[contentStart] !== " ")
2189
+ return null;
2190
+ }
2191
+ if (text[contentStart] === " ")
2192
+ contentStart += 1;
2193
+ const newline = text.indexOf(`
2194
+ `, contentStart);
2195
+ const next = newline < 0 ? text.length : newline;
2196
+ const value = text.slice(contentStart, next);
2197
+ return value ? { type, value, next } : null;
2198
+ }
2199
+ function utf16Length(value) {
2200
+ return value.length;
2201
+ }
2202
+ // src/api/messages.ts
2203
+ class MessageApi {
2204
+ app;
2205
+ uploads;
2206
+ previousCid = Date.now();
2207
+ constructor(app, uploads) {
2208
+ this.app = app;
2209
+ this.uploads = uploads;
2210
+ }
2211
+ async sendMessage(options) {
2212
+ const formatted = formatMarkdown(options.text);
2213
+ const attaches = await this.uploadAttachments(options.attachments);
2214
+ const link = options.replyTo ? { type: "REPLY", messageId: options.replyTo } : undefined;
2215
+ const frame = await this.app.invoke(Opcode.MSG_SEND, {
2216
+ chatId: options.chatId,
2217
+ message: {
2218
+ text: formatted.text,
2219
+ cid: this.nextCid(),
2220
+ elements: formatted.elements,
2221
+ attaches,
2222
+ ...link ? { link } : {}
2223
+ },
2224
+ notify: options.notify ?? true
2225
+ });
2226
+ return this.parseMessage(payload(frame));
2227
+ }
2228
+ async forwardMessage(options) {
2229
+ const frame = await this.app.invoke(Opcode.MSG_SEND, {
2230
+ chatId: options.chatId,
2231
+ message: {
2232
+ cid: -this.nextCid(),
2233
+ link: {
2234
+ type: "FORWARD",
2235
+ messageId: options.messageId.toString(),
2236
+ chatId: options.sourceChatId ?? options.chatId
2237
+ },
2238
+ attaches: []
2239
+ },
2240
+ notify: options.notify ?? true
2241
+ });
2242
+ return this.parseMessage(payload(frame));
2243
+ }
2244
+ async getMessages(chatId, messageIds) {
2245
+ const frame = await this.app.invoke(Opcode.MSG_GET, { chatId, messageIds });
2246
+ return listItem(frame, "messages").map((item) => {
2247
+ const message = this.app.bindMessage(Message.fromPayload(item));
2248
+ message.chatId ??= chatId;
2249
+ return message;
2250
+ });
2251
+ }
2252
+ async getMessage(chatId, messageId) {
2253
+ return (await this.getMessages(chatId, [messageId]))[0] ?? null;
2254
+ }
2255
+ async editMessage(options) {
2256
+ const formatted = formatMarkdown(options.text);
2257
+ const frame = await this.app.invoke(Opcode.MSG_EDIT, {
2258
+ chatId: options.chatId,
2259
+ messageId: options.messageId,
2260
+ text: formatted.text,
2261
+ elements: formatted.elements,
2262
+ attachments: await this.uploadAttachments(options.attachments)
2263
+ });
2264
+ const message = this.app.bindMessage(Message.fromPayload(requiredItem(frame, "message")));
2265
+ message.chatId ??= options.chatId;
2266
+ return message;
2267
+ }
2268
+ async fetchHistory(options) {
2269
+ const frame = await this.app.invoke(Opcode.CHAT_HISTORY, {
2270
+ chatId: options.chatId,
2271
+ forward: options.forward ?? 0,
2272
+ backward: options.backward ?? 40,
2273
+ backwardTime: options.backwardTime ?? 0,
2274
+ forwardTime: options.forwardTime ?? 0,
2275
+ from: options.from ?? Date.now(),
2276
+ itemType: options.itemType ?? "REGULAR",
2277
+ getChat: options.getChat ?? false,
2278
+ getMessages: options.getMessages ?? true,
2279
+ interactive: options.interactive ?? false
2280
+ });
2281
+ const result = listItem(frame, "messages").map((item) => this.app.bindMessage(Message.fromPayload(item)));
2282
+ return result.length > 0 ? result : null;
2283
+ }
2284
+ async deleteMessage(options) {
2285
+ await this.app.invoke(Opcode.MSG_DELETE, options);
2286
+ return true;
2287
+ }
2288
+ async pinMessage(options) {
2289
+ await this.app.invoke(Opcode.CHAT_UPDATE, {
2290
+ chatId: options.chatId,
2291
+ notifyPin: options.notifyPin,
2292
+ pinMessageId: options.messageId
2293
+ });
2294
+ return true;
2295
+ }
2296
+ async readMessage(messageId, chatId) {
2297
+ const frame = await this.app.invoke(Opcode.CHAT_MARK, {
2298
+ type: "READ_MESSAGE",
2299
+ chatId,
2300
+ messageId,
2301
+ mark: Date.now()
2302
+ });
2303
+ return parseReadState(payload(frame));
2304
+ }
2305
+ async addReaction(chatId, messageId, reaction) {
2306
+ const frame = await this.app.invoke(Opcode.MSG_REACTION, {
2307
+ chatId,
2308
+ messageId: messageId.toString(),
2309
+ reaction: { id: reaction }
2310
+ });
2311
+ return parseReactionInfo(payloadItem(frame, "reactionInfo"));
2312
+ }
2313
+ async removeReaction(chatId, messageId) {
2314
+ const frame = await this.app.invoke(Opcode.MSG_CANCEL_REACTION, {
2315
+ chatId,
2316
+ messageId: messageId.toString()
2317
+ });
2318
+ return parseReactionInfo(payloadItem(frame, "reactionInfo"));
2319
+ }
2320
+ async getReactions(chatId, messageIds) {
2321
+ const frame = await this.app.invoke(Opcode.MSG_GET_REACTIONS, {
2322
+ chatId,
2323
+ messageIds: messageIds.map(String)
2324
+ });
2325
+ const raw = payloadItem(frame, "messagesReactions");
2326
+ if (!raw)
2327
+ return null;
2328
+ const result = new Map;
2329
+ for (const [id, info] of Object.entries(asRecord(raw))) {
2330
+ const parsed = parseReactionInfo(info);
2331
+ if (parsed)
2332
+ result.set(toMaxId(id), parsed);
2333
+ }
2334
+ return result;
2335
+ }
2336
+ async getVideoById(chatId, messageId, videoId) {
2337
+ const frame = await this.app.invoke(Opcode.VIDEO_PLAY, {
2338
+ chatId,
2339
+ messageId: messageId.toString(),
2340
+ videoId
2341
+ });
2342
+ return frame.payload;
2343
+ }
2344
+ async getFileById(chatId, messageId, fileId) {
2345
+ const frame = await this.app.invoke(Opcode.FILE_DOWNLOAD, {
2346
+ chatId,
2347
+ messageId: messageId.toString(),
2348
+ fileId
2349
+ });
2350
+ return frame.payload;
2351
+ }
2352
+ nextCid() {
2353
+ const next = Math.max(Date.now(), this.previousCid + 1);
2354
+ this.previousCid = next;
2355
+ return BigInt(next);
2356
+ }
2357
+ async uploadAttachments(attachments2) {
2358
+ const result = [];
2359
+ for (const attachment of attachments2 ?? [])
2360
+ result.push(await this.uploads.upload(attachment));
2361
+ return result;
2362
+ }
2363
+ parseMessage(value) {
2364
+ if (!value)
2365
+ return null;
2366
+ const data = asRecord(value);
2367
+ const candidate = data.message ?? value;
2368
+ return this.app.bindMessage(Message.fromPayload(candidate));
2369
+ }
2370
+ }
2371
+
2372
+ // src/api/uploads.ts
2373
+ class UploadApi {
2374
+ app;
2375
+ constructor(app) {
2376
+ this.app = app;
2377
+ }
2378
+ async upload(attachment) {
2379
+ if (attachment instanceof Photo)
2380
+ return await this.uploadPhoto(attachment);
2381
+ if (attachment instanceof Video)
2382
+ return await this.uploadVideo(attachment);
2383
+ if (attachment instanceof File)
2384
+ return await this.uploadFile(attachment);
2385
+ throw new UploadError("Unsupported attachment");
2386
+ }
2387
+ async uploadPhoto(photo, profile = false) {
2388
+ const response = payload(await this.app.invoke(Opcode.PHOTO_UPLOAD, { count: 1, profile }));
2389
+ const url = asString(response.url);
2390
+ if (!url)
2391
+ throw new UploadError("No photo upload URL received");
2392
+ const photoId = new URL(url).searchParams.get("photoIds");
2393
+ if (!photoId)
2394
+ throw new UploadError("Photo upload URL has no photoIds");
2395
+ const form = new FormData;
2396
+ const bytes = await photo.read();
2397
+ form.append("file", new Blob([bytes], { type: photo.format.mimeType }), `image.${photo.format.extension}`);
2398
+ const upload = await fetch(url, { method: "POST", body: form });
2399
+ if (!upload.ok)
2400
+ throw new UploadError(`Photo upload failed with status ${upload.status}`);
2401
+ const result = asRecord(await upload.json(), "photo upload response");
2402
+ const photos = asRecord(result.photos, "photos");
2403
+ const item = asRecord(photos[photoId], "photo result");
2404
+ const token = asString(item.token);
2405
+ if (!token)
2406
+ throw new UploadError("Photo upload response has no token");
2407
+ return { _type: "PHOTO", photoToken: token };
2408
+ }
2409
+ async uploadVideo(video) {
2410
+ const response = payload(await this.app.invoke(Opcode.VIDEO_UPLOAD, { count: 1, profile: false }));
2411
+ const info = asRecord(asArray(response.info)[0], "video upload info");
2412
+ const videoId = asNumber(info.videoId);
2413
+ const token = asString(info.token);
2414
+ const waiter = this.app.createAttachmentWaiter?.("videoId", videoId, 60);
2415
+ try {
2416
+ await this.postBinary(asString(info.url), video, true);
2417
+ await waiter?.promise;
2418
+ return { _type: "VIDEO", videoId, token };
2419
+ } finally {
2420
+ waiter?.cancel();
2421
+ }
2422
+ }
2423
+ async uploadFile(file2) {
2424
+ const response = payload(await this.app.invoke(Opcode.FILE_UPLOAD, { count: 1, profile: false }));
2425
+ const info = asRecord(asArray(response.info)[0], "file upload info");
2426
+ const fileId = asNumber(info.fileId);
2427
+ const waiter = this.app.createAttachmentWaiter?.("fileId", fileId, 60);
2428
+ try {
2429
+ await this.postBinary(asString(info.url), file2, false);
2430
+ await waiter?.promise;
2431
+ return { _type: "FILE", fileId };
2432
+ } finally {
2433
+ waiter?.cancel();
2434
+ }
2435
+ }
2436
+ async postBinary(url, file2, contentRange) {
2437
+ if (!url)
2438
+ throw new UploadError("No upload URL received");
2439
+ const bytes = await file2.read();
2440
+ const headers = {
2441
+ "Content-Disposition": `attachment; filename=${encodeURIComponent(file2.name)}`,
2442
+ "Content-Length": String(bytes.byteLength)
2443
+ };
2444
+ if (contentRange || bytes.byteLength > 0)
2445
+ headers["Content-Range"] = `0-${bytes.byteLength - 1}/${bytes.byteLength}`;
2446
+ const response = await fetch(url, { method: "POST", headers, body: bytes });
2447
+ if (!response.ok)
2448
+ throw new UploadError(`Upload failed with status ${response.status}`);
2449
+ }
2450
+ }
2451
+
2452
+ // src/api/users.ts
2453
+ class UserApi {
2454
+ app;
2455
+ cache = new Map;
2456
+ constructor(app) {
2457
+ this.app = app;
2458
+ }
2459
+ seed(users) {
2460
+ for (const user of users)
2461
+ if (user)
2462
+ this.cacheUser(user);
2463
+ }
2464
+ getCachedUser(userId) {
2465
+ return this.cache.get(userId) ?? null;
2466
+ }
2467
+ async getUsers(userIds) {
2468
+ const missing = userIds.filter((id) => !this.cache.has(id));
2469
+ if (missing.length > 0)
2470
+ for (const user of await this.fetchUsers(missing))
2471
+ this.cache.set(user.id, user);
2472
+ return userIds.flatMap((id) => this.cache.has(id) ? [this.cache.get(id)] : []);
2473
+ }
2474
+ async getUser(userId) {
2475
+ return (await this.getUsers([userId]))[0] ?? null;
2476
+ }
2477
+ async fetchUsers(userIds) {
2478
+ const frame = await this.app.invoke(Opcode.CONTACT_INFO, { contactIds: userIds });
2479
+ return listItem(frame, "contacts").map((raw) => this.cacheUser(User.fromPayload(raw)));
2480
+ }
2481
+ async searchByPhone(phone) {
2482
+ const frame = await this.app.invoke(Opcode.CONTACT_INFO_BY_PHONE, { phone });
2483
+ return this.cacheUser(User.fromPayload(requiredItem(frame, "contact")));
2484
+ }
2485
+ async getSessions() {
2486
+ return listItem(await this.app.invoke(Opcode.SESSIONS_INFO, {}), "sessions").map((item) => asRecord(item));
2487
+ }
2488
+ async addContact(contactId) {
2489
+ const frame = await this.app.invoke(Opcode.CONTACT_UPDATE, { contactId, action: "add" });
2490
+ return this.cacheUser(User.fromPayload(requiredItem(frame, "contact")));
2491
+ }
2492
+ async removeContact(contactId) {
2493
+ await this.app.invoke(Opcode.CONTACT_UPDATE, { contactId, action: "remove" });
2494
+ this.cache.delete(contactId);
2495
+ return true;
2496
+ }
2497
+ async importContacts(contacts) {
2498
+ const contactList = Object.fromEntries(contacts.map((item) => [item.phone, { firstName: item.firstName }]));
2499
+ const frame = await this.app.invoke(Opcode.SYNC, { contactList });
2500
+ return listItem(frame, "contacts").map((raw) => this.cacheUser(User.fromPayload(raw)));
2501
+ }
2502
+ getChatId(firstUserId, secondUserId) {
2503
+ return firstUserId ^ secondUserId;
2504
+ }
2505
+ cacheUser(user) {
2506
+ const bound = this.app.bindUser(user);
2507
+ this.cache.set(bound.id, bound);
2508
+ return bound;
2509
+ }
2510
+ }
2511
+
2512
+ // src/api/facade.ts
2513
+ class ApiFacade {
2514
+ auth;
2515
+ uploads;
2516
+ messages;
2517
+ chats;
2518
+ users;
2519
+ account;
2520
+ bots;
2521
+ constructor(app) {
2522
+ this.auth = new AuthApi(app);
2523
+ this.uploads = new UploadApi(app);
2524
+ this.messages = new MessageApi(app, this.uploads);
2525
+ this.chats = new ChatApi(app);
2526
+ this.users = new UserApi(app);
2527
+ this.account = new AccountApi(app, this.uploads);
2528
+ this.bots = new BotsApi(app);
2529
+ }
2530
+ }
2531
+ // src/dispatch/router.ts
2532
+ var EventType = {
2533
+ MESSAGE_NEW: "messageNew",
2534
+ MESSAGE_EDIT: "messageEdit",
2535
+ MESSAGE_DELETE: "messageDelete",
2536
+ MESSAGE_READ: "messageRead",
2537
+ TYPING: "typing",
2538
+ PRESENCE: "presence",
2539
+ REACTION_UPDATE: "reactionUpdate",
2540
+ CHAT_UPDATE: "chatUpdate",
2541
+ RAW: "raw"
2542
+ };
2543
+
2544
+ class Router {
2545
+ children = [];
2546
+ handlers = new Map;
2547
+ startHandlers = [];
2548
+ errorHandlers = [];
2549
+ disconnectHandlers = [];
2550
+ includeRouter(router) {
2551
+ this.children.push(router);
2552
+ return () => {
2553
+ const index = this.children.indexOf(router);
2554
+ if (index >= 0)
2555
+ this.children.splice(index, 1);
2556
+ };
2557
+ }
2558
+ on(event, handler, ...filters) {
2559
+ const entry = { handler, filters };
2560
+ const entries = this.handlers.get(event) ?? [];
2561
+ entries.push(entry);
2562
+ this.handlers.set(event, entries);
2563
+ return () => {
2564
+ const index = entries.indexOf(entry);
2565
+ if (index >= 0)
2566
+ entries.splice(index, 1);
2567
+ };
2568
+ }
2569
+ onStart(handler) {
2570
+ this.startHandlers.push(handler);
2571
+ return () => remove(this.startHandlers, handler);
2572
+ }
2573
+ onError(handler) {
2574
+ this.errorHandlers.push(handler);
2575
+ return () => remove(this.errorHandlers, handler);
2576
+ }
2577
+ onDisconnect(handler) {
2578
+ this.disconnectHandlers.push(handler);
2579
+ return () => remove(this.disconnectHandlers, handler);
2580
+ }
2581
+ onMessage(handler, ...filters) {
2582
+ return this.on("messageNew", handler, ...filters);
2583
+ }
2584
+ onMessageEdit(handler, ...filters) {
2585
+ return this.on("messageEdit", handler, ...filters);
2586
+ }
2587
+ onMessageDelete(handler, ...filters) {
2588
+ return this.on("messageDelete", handler, ...filters);
2589
+ }
2590
+ onMessageRead(handler, ...filters) {
2591
+ return this.on("messageRead", handler, ...filters);
2592
+ }
2593
+ onTyping(handler, ...filters) {
2594
+ return this.on("typing", handler, ...filters);
2595
+ }
2596
+ onPresence(handler, ...filters) {
2597
+ return this.on("presence", handler, ...filters);
2598
+ }
2599
+ onReactionUpdate(handler, ...filters) {
2600
+ return this.on("reactionUpdate", handler, ...filters);
2601
+ }
2602
+ onChatUpdate(handler, ...filters) {
2603
+ return this.on("chatUpdate", handler, ...filters);
2604
+ }
2605
+ onRaw(handler, ...filters) {
2606
+ return this.on("raw", handler, ...filters);
2607
+ }
2608
+ }
2609
+ function remove(values, value) {
2610
+ const index = values.indexOf(value);
2611
+ if (index >= 0)
2612
+ values.splice(index, 1);
2613
+ }
2614
+
2615
+ // src/dispatch/dispatcher.ts
2616
+ class Dispatcher {
2617
+ rootRouter;
2618
+ client = null;
2619
+ constructor(rootRouter) {
2620
+ this.rootRouter = rootRouter;
2621
+ }
2622
+ bindClient(client) {
2623
+ this.client = client;
2624
+ }
2625
+ async emitStart() {
2626
+ const client = this.requireClient();
2627
+ for (const router of this.routers()) {
2628
+ for (const handler of router.startHandlers) {
2629
+ try {
2630
+ await handler(client);
2631
+ } catch (error) {
2632
+ await this.emitError(error, "start", null, router);
2633
+ }
2634
+ }
2635
+ }
2636
+ }
2637
+ async emitDisconnect(error, reconnect, delay) {
2638
+ for (const router of this.routers()) {
2639
+ for (const handler of router.disconnectHandlers) {
2640
+ try {
2641
+ await handler(error, reconnect, delay);
2642
+ } catch {}
2643
+ }
2644
+ }
2645
+ }
2646
+ async dispatch(frame) {
2647
+ await this.dispatchEvent("raw", frame);
2648
+ if (frame.cmd !== Command.REQUEST || !frame.payload)
2649
+ return;
2650
+ const resolved = resolveEvent(frame);
2651
+ if (resolved)
2652
+ await this.dispatchEvent(resolved.type, resolved.event);
2653
+ }
2654
+ async dispatchEvent(type, event) {
2655
+ const client = this.requireClient();
2656
+ for (const router of this.routers()) {
2657
+ const entries = router.handlers.get(type) ?? [];
2658
+ for (const rawEntry of entries) {
2659
+ const entry = rawEntry;
2660
+ try {
2661
+ let matches = true;
2662
+ for (const filter of entry.filters) {
2663
+ if (!await filter(event)) {
2664
+ matches = false;
2665
+ break;
2666
+ }
2667
+ }
2668
+ if (matches)
2669
+ await entry.handler(event, client);
2670
+ } catch (error) {
2671
+ await this.emitError(error, type, event, router);
2672
+ }
2673
+ }
2674
+ }
2675
+ }
2676
+ async emitError(error, eventType, event, source) {
2677
+ const failure = error instanceof Error ? error : new Error(String(error));
2678
+ const client = this.requireClient();
2679
+ let handled = false;
2680
+ for (const router of this.routers()) {
2681
+ for (const handler of router.errorHandlers) {
2682
+ handled = true;
2683
+ await handler(failure, { client, eventType, event, router: source });
2684
+ }
2685
+ }
2686
+ if (!handled)
2687
+ throw failure;
2688
+ }
2689
+ *routers(router = this.rootRouter) {
2690
+ yield router;
2691
+ for (const child of router.children)
2692
+ yield* this.routers(child);
2693
+ }
2694
+ requireClient() {
2695
+ if (!this.client)
2696
+ throw new Error("Dispatcher has no bound client");
2697
+ return this.client;
2698
+ }
2699
+ }
2700
+ function resolveEvent(frame) {
2701
+ const payload2 = frame.payload;
2702
+ if (frame.opcode === Opcode.NOTIF_MESSAGE || frame.opcode === Opcode.MSG_EDIT) {
2703
+ const messagePayload = payload2.message ?? payload2;
2704
+ const message = Message.fromPayload(messagePayload);
2705
+ if (message.status === "REMOVED")
2706
+ return { type: EventType.MESSAGE_DELETE, event: deleteFromMessage(message, payload2) };
2707
+ return {
2708
+ type: message.status === "EDITED" ? EventType.MESSAGE_EDIT : EventType.MESSAGE_NEW,
2709
+ event: message
2710
+ };
2711
+ }
2712
+ if (frame.opcode === Opcode.NOTIF_CHAT) {
2713
+ return { type: EventType.CHAT_UPDATE, event: Chat.fromPayload(payload2.chat ?? payload2) };
2714
+ }
2715
+ if (frame.opcode === Opcode.NOTIF_MSG_DELETE) {
2716
+ const chat = payload2.chat ? asRecord(payload2.chat) : null;
2717
+ const event = {
2718
+ chatId: toMaxId(payload2.chatId ?? chat?.id, "delete.chatId"),
2719
+ messageIds: asArray(payload2.messageIds).map((id) => toMaxId(id)),
2720
+ chat: chat ? Chat.fromPayload(chat) : null,
2721
+ ttl: asBoolean(payload2.ttl)
2722
+ };
2723
+ return { type: EventType.MESSAGE_DELETE, event };
2724
+ }
2725
+ if (frame.opcode === Opcode.NOTIF_TYPING) {
2726
+ const event = {
2727
+ chatId: toMaxId(payload2.chatId),
2728
+ userId: toMaxId(payload2.userId),
2729
+ typing: asBoolean(payload2.typing, true)
2730
+ };
2731
+ return { type: EventType.TYPING, event };
2732
+ }
2733
+ if (frame.opcode === Opcode.NOTIF_MARK) {
2734
+ const event = {
2735
+ chatId: toMaxId(payload2.chatId),
2736
+ userId: optionalMaxId(payload2.userId),
2737
+ mark: asNumber(payload2.mark)
2738
+ };
2739
+ return { type: EventType.MESSAGE_READ, event };
2740
+ }
2741
+ if (frame.opcode === Opcode.NOTIF_PRESENCE) {
2742
+ const presence = payload2.presence ? asRecord(payload2.presence) : payload2;
2743
+ const event = {
2744
+ userId: toMaxId(payload2.userId),
2745
+ online: asBoolean(presence.online ?? presence.status === "ONLINE"),
2746
+ lastSeen: presence.lastSeen === undefined ? null : asNumber(presence.lastSeen),
2747
+ raw: presence
2748
+ };
2749
+ return { type: EventType.PRESENCE, event };
2750
+ }
2751
+ if (frame.opcode === Opcode.NOTIF_MSG_REACTIONS_CHANGED) {
2752
+ const event = {
2753
+ chatId: toMaxId(payload2.chatId),
2754
+ messageId: toMaxId(payload2.messageId),
2755
+ reactionInfo: parseReactionInfo({
2756
+ totalCount: payload2.totalCount,
2757
+ counters: payload2.counters,
2758
+ yourReaction: payload2.yourReaction
2759
+ })
2760
+ };
2761
+ return { type: EventType.REACTION_UPDATE, event };
2762
+ }
2763
+ return null;
2764
+ }
2765
+ function deleteFromMessage(message, payload2) {
2766
+ if (message.chatId === null)
2767
+ throw new TypeError("Removed message has no chatId");
2768
+ return {
2769
+ chatId: message.chatId,
2770
+ messageIds: [message.id],
2771
+ message,
2772
+ ttl: asBoolean(payload2.ttl)
2773
+ };
2774
+ }
2775
+ // src/app.ts
2776
+ class App {
2777
+ connection;
2778
+ config;
2779
+ authFlow;
2780
+ dispatcher;
2781
+ api;
2782
+ telemetry;
2783
+ store;
2784
+ session = null;
2785
+ me = null;
2786
+ chats = [];
2787
+ contacts = [];
2788
+ messages = new Map;
2789
+ started = false;
2790
+ pingController = null;
2791
+ attachmentWaiters = new Map;
2792
+ constructor(connection, config, authFlow, rootRouter) {
2793
+ this.connection = connection;
2794
+ this.config = config;
2795
+ this.authFlow = authFlow;
2796
+ this.dispatcher = new Dispatcher(rootRouter);
2797
+ this.store = config.store ?? new SqliteSessionStore(config.workDir, config.sessionName);
2798
+ this.api = new ApiFacade(this);
2799
+ this.telemetry = new TelemetryService(this);
2800
+ this.connection.onEvent = async (frame) => {
2801
+ this.resolveAttachmentWaiters(frame);
2802
+ await this.dispatcher.dispatch(frame);
2803
+ };
2804
+ this.connection.onClose = (error) => this.connectionLost(error);
2805
+ }
2806
+ bindClient(client) {
2807
+ this.dispatcher.bindClient(client);
2808
+ }
2809
+ async start() {
2810
+ if (this.started)
2811
+ return;
2812
+ let saved = await this.store.loadSession();
2813
+ if (saved?.mtInstanceId)
2814
+ this.config.device.mtInstanceId = saved.mtInstanceId;
2815
+ await this.connection.open();
2816
+ await this.api.auth.handshake(saved?.deviceId ?? this.config.device.deviceId);
2817
+ this.startPing();
2818
+ if (!saved) {
2819
+ const token = this.config.token ?? (await this.authFlow.authenticate(this)).token;
2820
+ if (!token)
2821
+ throw new Error("Authentication failed: no token returned");
2822
+ saved = {
2823
+ token,
2824
+ deviceId: this.config.device.deviceId,
2825
+ phone: this.config.phone ?? "",
2826
+ mtInstanceId: this.config.device.mtInstanceId,
2827
+ sync: defaultSyncState()
2828
+ };
2829
+ await this.store.saveSession(saved);
2830
+ }
2831
+ this.session = saved;
2832
+ const login = await this.api.auth.login();
2833
+ let activeSession = this.session ?? saved;
2834
+ if (login.token && login.token !== activeSession.token) {
2835
+ await this.store.updateToken(activeSession.token, login.token);
2836
+ activeSession = { ...activeSession, token: login.token };
2837
+ }
2838
+ this.session = activeSession;
2839
+ this.me = login.profile;
2840
+ this.chats = login.chats;
2841
+ this.contacts = login.contacts;
2842
+ this.messages = login.messages;
2843
+ this.api.chats.seed(this.chats);
2844
+ this.api.users.seed([...this.contacts, this.me.contact]);
2845
+ this.bindUser(this.me.contact);
2846
+ this.started = true;
2847
+ if (this.config.telemetry)
2848
+ this.telemetry.start();
2849
+ getLogger().info("client started", {
2850
+ profileId: this.me.contact.id.toString(),
2851
+ chats: this.chats.length
2852
+ });
2853
+ }
2854
+ async close() {
2855
+ this.pingController?.abort();
2856
+ this.pingController = null;
2857
+ await this.telemetry.stop();
2858
+ for (const entries of this.attachmentWaiters.values()) {
2859
+ for (const entry of entries)
2860
+ entry.reject(new Error("Client closed before upload processing completed"));
2861
+ }
2862
+ this.attachmentWaiters.clear();
2863
+ await this.connection.close();
2864
+ await this.store.close();
2865
+ this.started = false;
2866
+ }
2867
+ async invoke(opcode, payload2 = {}, timeout) {
2868
+ const frame = {
2869
+ ver: this.connection.protocol.version,
2870
+ opcode,
2871
+ cmd: Command.REQUEST,
2872
+ seq: this.connection.nextSequence(),
2873
+ payload: payload2
2874
+ };
2875
+ const response = await this.connection.request(frame, timeout ?? this.config.requestTimeout);
2876
+ if (response.cmd === Command.ERROR)
2877
+ throw buildApiError(response);
2878
+ return response;
2879
+ }
2880
+ bindMessage(message) {
2881
+ return message.bind(this.api.messages);
2882
+ }
2883
+ bindChat(chat) {
2884
+ return chat.bind(this.api.messages, this.api.chats);
2885
+ }
2886
+ bindUser(user) {
2887
+ return user.bind(this.api.users);
2888
+ }
2889
+ createAttachmentWaiter(field, id, timeoutSeconds = 60) {
2890
+ const key = `${field}:${id}`;
2891
+ const entries = this.attachmentWaiters.get(key) ?? new Set;
2892
+ let active = true;
2893
+ let resolvePromise = () => {
2894
+ return;
2895
+ };
2896
+ let rejectPromise = () => {
2897
+ return;
2898
+ };
2899
+ const promise = new Promise((resolve, reject) => {
2900
+ resolvePromise = resolve;
2901
+ rejectPromise = reject;
2902
+ });
2903
+ const entry = {
2904
+ resolve: () => {
2905
+ if (!active)
2906
+ return;
2907
+ active = false;
2908
+ clearTimeout(timer);
2909
+ entries.delete(entry);
2910
+ resolvePromise();
2911
+ },
2912
+ reject: (error) => {
2913
+ if (!active)
2914
+ return;
2915
+ active = false;
2916
+ clearTimeout(timer);
2917
+ entries.delete(entry);
2918
+ rejectPromise(error);
2919
+ }
2920
+ };
2921
+ entries.add(entry);
2922
+ this.attachmentWaiters.set(key, entries);
2923
+ const timer = setTimeout(() => entry.reject(new Error(`Timed out waiting for ${field}=${id} processing`)), timeoutSeconds * 1000);
2924
+ return {
2925
+ promise,
2926
+ cancel: () => {
2927
+ if (!active)
2928
+ return;
2929
+ active = false;
2930
+ clearTimeout(timer);
2931
+ entries.delete(entry);
2932
+ if (entries.size === 0)
2933
+ this.attachmentWaiters.delete(key);
2934
+ promise.catch(() => {
2935
+ return;
2936
+ });
2937
+ }
2938
+ };
2939
+ }
2940
+ startPing() {
2941
+ const controller = new AbortController;
2942
+ this.pingController = controller;
2943
+ this.pingLoop(controller.signal);
2944
+ }
2945
+ async pingLoop(signal) {
2946
+ try {
2947
+ while (!signal.aborted) {
2948
+ await this.invoke(Opcode.PING, { interactive: true });
2949
+ await sleep2(30000, signal);
2950
+ }
2951
+ } catch (error) {
2952
+ if (!signal.aborted) {
2953
+ getLogger().warn("ping failed", { error: String(error) });
2954
+ await this.connection.close();
2955
+ }
2956
+ }
2957
+ }
2958
+ connectionLost(error) {
2959
+ this.started = false;
2960
+ this.pingController?.abort();
2961
+ if (error)
2962
+ getLogger().warn("connection lost", { error: error.message });
2963
+ }
2964
+ resolveAttachmentWaiters(frame) {
2965
+ if (frame.opcode !== Opcode.NOTIF_ATTACH || !frame.payload)
2966
+ return;
2967
+ for (const field of ["fileId", "videoId"]) {
2968
+ const value = frame.payload[field];
2969
+ if (typeof value !== "number" || !Number.isSafeInteger(value))
2970
+ continue;
2971
+ const key = `${field}:${value}`;
2972
+ const entries = this.attachmentWaiters.get(key);
2973
+ if (!entries)
2974
+ continue;
2975
+ this.attachmentWaiters.delete(key);
2976
+ for (const entry of entries)
2977
+ entry.resolve();
2978
+ }
2979
+ }
2980
+ }
2981
+ function buildApiError(frame) {
2982
+ const data = frame.payload ?? {};
2983
+ return new ApiError(frame.opcode, {
2984
+ error: typeof data.error === "string" ? data.error : null,
2985
+ title: typeof data.title === "string" ? data.title : null,
2986
+ message: typeof data.message === "string" ? data.message : null,
2987
+ localizedMessage: typeof data.localizedMessage === "string" ? data.localizedMessage : null,
2988
+ payload: data
2989
+ });
2990
+ }
2991
+ async function sleep2(milliseconds, signal) {
2992
+ await new Promise((resolve, reject) => {
2993
+ const timer = setTimeout(resolve, milliseconds);
2994
+ signal.addEventListener("abort", () => {
2995
+ clearTimeout(timer);
2996
+ reject(signal.reason);
2997
+ }, { once: true });
2998
+ });
2999
+ }
3000
+
3001
+ // src/connection/pending.ts
3002
+ class PendingRequests {
3003
+ requests = new Map;
3004
+ create(sequence, timeoutSeconds) {
3005
+ if (this.requests.has(sequence))
3006
+ throw new Error(`Duplicate request sequence: ${sequence}`);
3007
+ return new Promise((resolve, reject) => {
3008
+ const request = { resolve, reject, timer: null };
3009
+ if (timeoutSeconds !== undefined) {
3010
+ request.timer = setTimeout(() => {
3011
+ this.requests.delete(sequence);
3012
+ reject(new TimeoutError(`Request ${sequence} timed out`));
3013
+ }, timeoutSeconds * 1000);
3014
+ }
3015
+ this.requests.set(sequence, request);
3016
+ });
3017
+ }
3018
+ resolve(sequence, frame) {
3019
+ const request = this.requests.get(sequence);
3020
+ if (!request)
3021
+ return false;
3022
+ this.requests.delete(sequence);
3023
+ if (request.timer)
3024
+ clearTimeout(request.timer);
3025
+ request.resolve(frame);
3026
+ return true;
3027
+ }
3028
+ discard(sequence) {
3029
+ const request = this.requests.get(sequence);
3030
+ if (request?.timer)
3031
+ clearTimeout(request.timer);
3032
+ this.requests.delete(sequence);
3033
+ }
3034
+ cancelAll(reason = new ConnectionError("Connection closed")) {
3035
+ for (const request of this.requests.values()) {
3036
+ if (request.timer)
3037
+ clearTimeout(request.timer);
3038
+ request.reject(reason);
3039
+ }
3040
+ this.requests.clear();
3041
+ }
3042
+ }
3043
+
3044
+ // src/connection/connection.ts
3045
+ class ConnectionManager {
3046
+ reader;
3047
+ transport;
3048
+ protocol;
3049
+ requests = new PendingRequests;
3050
+ onEvent = null;
3051
+ onClose = null;
3052
+ sequence = -1;
3053
+ receiveTask = null;
3054
+ abortController = null;
3055
+ constructor(reader, transport, protocol) {
3056
+ this.reader = reader;
3057
+ this.transport = transport;
3058
+ this.protocol = protocol;
3059
+ }
3060
+ get isOpen() {
3061
+ return this.transport.connected;
3062
+ }
3063
+ async open() {
3064
+ if (this.isOpen)
3065
+ return;
3066
+ await this.transport.connect();
3067
+ this.abortController = new AbortController;
3068
+ this.receiveTask = this.receiveLoop(this.abortController.signal);
3069
+ }
3070
+ async close() {
3071
+ this.abortController?.abort();
3072
+ this.abortController = null;
3073
+ this.requests.cancelAll();
3074
+ await this.transport.close();
3075
+ try {
3076
+ await this.receiveTask;
3077
+ } catch {}
3078
+ this.receiveTask = null;
3079
+ }
3080
+ async send(frame) {
3081
+ if (!this.isOpen)
3082
+ throw new ConnectionError("Connection is not open");
3083
+ await this.transport.send(this.protocol.encode(frame));
3084
+ }
3085
+ async request(frame, timeoutSeconds) {
3086
+ const response = this.requests.create(frame.seq, timeoutSeconds);
3087
+ try {
3088
+ await this.send(frame);
3089
+ return await response;
3090
+ } catch (error) {
3091
+ this.requests.discard(frame.seq);
3092
+ throw error;
3093
+ }
3094
+ }
3095
+ async waitClosed() {
3096
+ await this.receiveTask;
3097
+ }
3098
+ nextSequence() {
3099
+ this.sequence = this.sequence + 1 & 65535;
3100
+ return this.sequence;
3101
+ }
3102
+ async receiveLoop(signal) {
3103
+ try {
3104
+ while (!signal.aborted) {
3105
+ const raw = await this.reader.read();
3106
+ const frame = this.protocol.decode(raw);
3107
+ if ((frame.cmd === Command.RESPONSE || frame.cmd === Command.ERROR) && frame.seq !== null) {
3108
+ this.requests.resolve(frame.seq, frame);
3109
+ }
3110
+ if (this.onEvent)
3111
+ Promise.resolve(this.onEvent(frame));
3112
+ }
3113
+ } catch (error) {
3114
+ if (signal.aborted)
3115
+ return;
3116
+ const failure = error instanceof Error ? error : new ConnectionError(String(error));
3117
+ this.requests.cancelAll(failure);
3118
+ this.onClose?.(failure);
3119
+ throw failure;
3120
+ }
3121
+ }
3122
+ }
3123
+ // src/connection/readers.ts
3124
+ class TcpReader {
3125
+ transport;
3126
+ framer;
3127
+ constructor(transport, framer = new TcpPacketFramer) {
3128
+ this.transport = transport;
3129
+ this.framer = framer;
3130
+ }
3131
+ async read() {
3132
+ const header = await this.transport.recv(TCP_HEADER_SIZE);
3133
+ const payloadLength = this.framer.unpackPayloadLength(header);
3134
+ if (payloadLength === null)
3135
+ throw new ConnectionError("Incomplete TCP header");
3136
+ if (payloadLength === 0)
3137
+ return header;
3138
+ const payload2 = await this.transport.recv(payloadLength);
3139
+ const result = new Uint8Array(header.byteLength + payload2.byteLength);
3140
+ result.set(header);
3141
+ result.set(payload2, header.byteLength);
3142
+ return result;
3143
+ }
3144
+ }
3145
+
3146
+ class WsReader {
3147
+ transport;
3148
+ constructor(transport) {
3149
+ this.transport = transport;
3150
+ }
3151
+ async read() {
3152
+ return await this.transport.recv();
3153
+ }
3154
+ }
3155
+ // src/transport/base.ts
3156
+ class AsyncQueue {
3157
+ values = [];
3158
+ waiters = [];
3159
+ failure = null;
3160
+ push(value) {
3161
+ const waiter = this.waiters.shift();
3162
+ if (waiter)
3163
+ waiter.resolve(value);
3164
+ else
3165
+ this.values.push(value);
3166
+ }
3167
+ async shift() {
3168
+ const value = this.values.shift();
3169
+ if (value !== undefined)
3170
+ return value;
3171
+ if (this.failure)
3172
+ throw this.failure;
3173
+ return await new Promise((resolve, reject) => this.waiters.push({ resolve, reject }));
3174
+ }
3175
+ fail(reason) {
3176
+ this.failure = reason;
3177
+ for (const waiter of this.waiters.splice(0))
3178
+ waiter.reject(reason);
3179
+ }
3180
+ clear() {
3181
+ this.values.length = 0;
3182
+ }
3183
+ reset() {
3184
+ this.clear();
3185
+ this.failure = null;
3186
+ }
3187
+ }
3188
+ // src/transport/tcp.ts
3189
+ var defaultConnector = async (options) => await Bun.connect(options);
3190
+
3191
+ class TcpTransport {
3192
+ host;
3193
+ port;
3194
+ useTls;
3195
+ proxy;
3196
+ connector;
3197
+ socket = null;
3198
+ chunks = new AsyncQueue;
3199
+ buffered = new Uint8Array;
3200
+ constructor(host, port, useTls = true, proxy = null, connector = defaultConnector) {
3201
+ this.host = host;
3202
+ this.port = port;
3203
+ this.useTls = useTls;
3204
+ this.proxy = proxy;
3205
+ this.connector = connector;
3206
+ }
3207
+ get connected() {
3208
+ return this.socket !== null && this.socket.readyState === 1;
3209
+ }
3210
+ async connect() {
3211
+ if (this.socket)
3212
+ return;
3213
+ this.chunks.reset();
3214
+ const endpoint = this.proxy ? new URL(this.proxy) : null;
3215
+ let resolveOpen = () => {
3216
+ return;
3217
+ };
3218
+ let rejectOpen = () => {
3219
+ return;
3220
+ };
3221
+ let opened = false;
3222
+ const open = new Promise((resolve, reject) => {
3223
+ resolveOpen = resolve;
3224
+ rejectOpen = reject;
3225
+ });
3226
+ const handlers = this.handlers();
3227
+ try {
3228
+ this.socket = await this.connector({
3229
+ hostname: endpoint?.hostname ?? this.host,
3230
+ port: endpoint ? Number(endpoint.port || defaultProxyPort(endpoint.protocol)) : this.port,
3231
+ tls: endpoint?.protocol === "https:" ? { serverName: endpoint.hostname } : !endpoint && this.useTls ? { serverName: this.host } : false,
3232
+ socket: {
3233
+ ...handlers,
3234
+ open: () => {
3235
+ opened = true;
3236
+ resolveOpen();
3237
+ },
3238
+ close: (socket, error) => {
3239
+ handlers.close?.(socket, error);
3240
+ if (!opened)
3241
+ rejectOpen(error ?? new ConnectionError("Connection closed while opening"));
3242
+ },
3243
+ error: (socket, error) => {
3244
+ handlers.error?.(socket, error);
3245
+ if (!opened)
3246
+ rejectOpen(error);
3247
+ },
3248
+ connectError: (socket, error) => {
3249
+ handlers.connectError?.(socket, error);
3250
+ if (!opened)
3251
+ rejectOpen(error);
3252
+ },
3253
+ timeout: (socket) => {
3254
+ handlers.timeout?.(socket);
3255
+ if (!opened)
3256
+ rejectOpen(new ConnectionError("Connection timed out while opening"));
3257
+ }
3258
+ }
3259
+ });
3260
+ await open;
3261
+ } catch (error) {
3262
+ this.socket = null;
3263
+ throw error;
3264
+ }
3265
+ if (endpoint) {
3266
+ await this.openTunnel(endpoint);
3267
+ if (this.useTls)
3268
+ await this.upgradeTls();
3269
+ }
3270
+ }
3271
+ async close() {
3272
+ this.socket?.end();
3273
+ this.socket = null;
3274
+ this.buffered = new Uint8Array;
3275
+ this.chunks.reset();
3276
+ }
3277
+ async send(data) {
3278
+ if (!this.socket || !this.connected)
3279
+ throw new ConnectionError("Transport is not connected");
3280
+ await this.sendRaw(data);
3281
+ }
3282
+ async recv(length) {
3283
+ if (!this.connected)
3284
+ throw new ConnectionError("Transport is not connected");
3285
+ if (length === undefined)
3286
+ return await this.chunks.shift();
3287
+ while (this.buffered.byteLength < length) {
3288
+ this.buffered = concatBytes(this.buffered, await this.chunks.shift());
3289
+ }
3290
+ const result = this.buffered.slice(0, length);
3291
+ this.buffered = this.buffered.slice(length);
3292
+ return result;
3293
+ }
3294
+ handlers() {
3295
+ return {
3296
+ data: (_socket, data) => this.chunks.push(Uint8Array.from(data)),
3297
+ close: (_socket, error) => {
3298
+ this.socket = null;
3299
+ this.chunks.fail(error ?? new ConnectionError("Connection closed"));
3300
+ },
3301
+ end: () => this.chunks.fail(new ConnectionError("Connection closed by server")),
3302
+ error: (_socket, error) => this.chunks.fail(error),
3303
+ connectError: (_socket, error) => this.chunks.fail(error),
3304
+ timeout: () => this.chunks.fail(new ConnectionError("Connection timed out"))
3305
+ };
3306
+ }
3307
+ async openTunnel(proxy) {
3308
+ if (proxy.protocol === "http:" || proxy.protocol === "https:") {
3309
+ await this.httpConnect(proxy);
3310
+ return;
3311
+ }
3312
+ if (proxy.protocol === "socks5:" || proxy.protocol === "socks5h:") {
3313
+ await this.socks5Connect(proxy);
3314
+ return;
3315
+ }
3316
+ if (proxy.protocol === "socks4:" || proxy.protocol === "socks4a:") {
3317
+ await this.socks4Connect(proxy);
3318
+ return;
3319
+ }
3320
+ throw new TypeError(`Unsupported proxy protocol: ${proxy.protocol}`);
3321
+ }
3322
+ async httpConnect(proxy) {
3323
+ const headers = [
3324
+ `CONNECT ${this.host}:${this.port} HTTP/1.1`,
3325
+ `Host: ${this.host}:${this.port}`
3326
+ ];
3327
+ if (proxy.username || proxy.password) {
3328
+ headers.push(`Proxy-Authorization: Basic ${Buffer.from(`${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`).toString("base64")}`);
3329
+ }
3330
+ await this.sendRaw(new TextEncoder().encode(`${headers.join(`\r
3331
+ `)}\r
3332
+ \r
3333
+ `));
3334
+ const response = new TextDecoder().decode(await this.readUntil(Uint8Array.from([13, 10, 13, 10])));
3335
+ const status = Number(/^HTTP\/\d(?:\.\d)?\s+(\d{3})/.exec(response)?.[1]);
3336
+ if (status !== 200)
3337
+ throw new ConnectionError(`HTTP proxy CONNECT failed with status ${status || "unknown"}`);
3338
+ }
3339
+ async socks5Connect(proxy) {
3340
+ const authenticated = Boolean(proxy.username || proxy.password);
3341
+ await this.sendRaw(Uint8Array.from(authenticated ? [5, 2, 0, 2] : [5, 1, 0]));
3342
+ const greeting = await this.recv(2);
3343
+ if (greeting[0] !== 5 || greeting[1] === 255)
3344
+ throw new ConnectionError("SOCKS5 negotiation failed");
3345
+ if (greeting[1] === 2) {
3346
+ const username = new TextEncoder().encode(decodeURIComponent(proxy.username));
3347
+ const password = new TextEncoder().encode(decodeURIComponent(proxy.password));
3348
+ if (username.length > 255 || password.length > 255)
3349
+ throw new TypeError("SOCKS5 credentials are too long");
3350
+ await this.sendRaw(Uint8Array.from([1, username.length, ...username, password.length, ...password]));
3351
+ const auth2 = await this.recv(2);
3352
+ if (auth2[1] !== 0)
3353
+ throw new ConnectionError("SOCKS5 authentication failed");
3354
+ } else if (greeting[1] !== 0) {
3355
+ throw new ConnectionError(`Unsupported SOCKS5 auth method: ${greeting[1]}`);
3356
+ }
3357
+ const hostname = new TextEncoder().encode(this.host);
3358
+ if (hostname.length > 255)
3359
+ throw new TypeError("SOCKS5 hostname is too long");
3360
+ await this.sendRaw(Uint8Array.from([
3361
+ 5,
3362
+ 1,
3363
+ 0,
3364
+ 3,
3365
+ hostname.length,
3366
+ ...hostname,
3367
+ this.port >>> 8 & 255,
3368
+ this.port & 255
3369
+ ]));
3370
+ const head = await this.recv(4);
3371
+ if (head[0] !== 5 || head[1] !== 0)
3372
+ throw new ConnectionError(`SOCKS5 CONNECT failed: ${head[1]}`);
3373
+ const addressLength = head[3] === 1 ? 4 : head[3] === 4 ? 16 : (await this.recv(1))[0];
3374
+ if (addressLength === undefined)
3375
+ throw new ConnectionError("Invalid SOCKS5 response");
3376
+ await this.recv(addressLength + 2);
3377
+ }
3378
+ async socks4Connect(proxy) {
3379
+ const user = new TextEncoder().encode(decodeURIComponent(proxy.username));
3380
+ const host = new TextEncoder().encode(this.host);
3381
+ const request = Uint8Array.from([
3382
+ 4,
3383
+ 1,
3384
+ this.port >>> 8 & 255,
3385
+ this.port & 255,
3386
+ 0,
3387
+ 0,
3388
+ 0,
3389
+ 1,
3390
+ ...user,
3391
+ 0,
3392
+ ...host,
3393
+ 0
3394
+ ]);
3395
+ await this.sendRaw(request);
3396
+ const response = await this.recv(8);
3397
+ if (response[1] !== 90)
3398
+ throw new ConnectionError(`SOCKS4 CONNECT failed: ${response[1]}`);
3399
+ }
3400
+ async upgradeTls() {
3401
+ const socket = this.socket;
3402
+ if (!socket)
3403
+ throw new ConnectionError("Proxy socket closed before TLS upgrade");
3404
+ await new Promise((resolve, reject) => {
3405
+ const handlers = this.handlers();
3406
+ const [, tls] = socket.upgradeTLS({
3407
+ tls: { serverName: this.host },
3408
+ socket: {
3409
+ ...handlers,
3410
+ open: () => resolve(),
3411
+ error: (_socket, error) => {
3412
+ handlers.error?.(_socket, error);
3413
+ reject(error);
3414
+ }
3415
+ }
3416
+ });
3417
+ this.socket = tls;
3418
+ });
3419
+ }
3420
+ async sendRaw(data) {
3421
+ const socket = this.socket;
3422
+ if (!socket || socket.readyState <= 0)
3423
+ throw new ConnectionError("Socket is not connected");
3424
+ let offset = 0;
3425
+ while (offset < data.byteLength) {
3426
+ const written = socket.write(data, offset, data.byteLength - offset);
3427
+ if (written < 0)
3428
+ throw new ConnectionError("Socket closed while writing");
3429
+ if (written === 0) {
3430
+ await Bun.sleep(0);
3431
+ continue;
3432
+ }
3433
+ offset += written;
3434
+ }
3435
+ socket.flush();
3436
+ }
3437
+ async readUntil(delimiter) {
3438
+ let data = this.buffered;
3439
+ for (;; ) {
3440
+ const index = findBytes(data, delimiter);
3441
+ if (index >= 0) {
3442
+ const end = index + delimiter.length;
3443
+ const result = data.slice(0, end);
3444
+ this.buffered = data.slice(end);
3445
+ return result;
3446
+ }
3447
+ if (data.byteLength > 64 * 1024)
3448
+ throw new ConnectionError("Proxy response header is too large");
3449
+ data = concatBytes(data, await this.chunks.shift());
3450
+ }
3451
+ }
3452
+ }
3453
+ function concatBytes(left, right) {
3454
+ const result = new Uint8Array(left.byteLength + right.byteLength);
3455
+ result.set(left);
3456
+ result.set(right, left.byteLength);
3457
+ return result;
3458
+ }
3459
+ function defaultProxyPort(protocol) {
3460
+ if (protocol === "https:")
3461
+ return 443;
3462
+ if (protocol === "http:")
3463
+ return 8080;
3464
+ return 1080;
3465
+ }
3466
+ function findBytes(source, target) {
3467
+ outer:
3468
+ for (let index = 0;index <= source.length - target.length; index += 1) {
3469
+ for (let offset = 0;offset < target.length; offset += 1) {
3470
+ if (source[index + offset] !== target[offset])
3471
+ continue outer;
3472
+ }
3473
+ return index;
3474
+ }
3475
+ return -1;
3476
+ }
3477
+ // src/transport/websocket.ts
3478
+ class WebSocketTransport {
3479
+ url;
3480
+ proxy;
3481
+ socket = null;
3482
+ messages = new AsyncQueue;
3483
+ constructor(url, proxy = null) {
3484
+ this.url = url;
3485
+ this.proxy = proxy;
3486
+ }
3487
+ get connected() {
3488
+ return this.socket?.readyState === WebSocket.OPEN;
3489
+ }
3490
+ async connect() {
3491
+ if (this.socket)
3492
+ return;
3493
+ const socket = new WebSocket(this.url, {
3494
+ headers: { Origin: "https://web.max.ru" },
3495
+ ...this.proxy ? { proxy: this.proxy } : {}
3496
+ });
3497
+ this.socket = socket;
3498
+ await new Promise((resolve, reject) => {
3499
+ socket.addEventListener("open", () => resolve(), { once: true });
3500
+ socket.addEventListener("error", () => reject(new ConnectionError("WebSocket connection failed")), {
3501
+ once: true
3502
+ });
3503
+ });
3504
+ socket.addEventListener("message", (event) => {
3505
+ if (typeof event.data === "string")
3506
+ this.messages.push(event.data);
3507
+ else
3508
+ this.messages.fail(new TypeError("Expected a text WebSocket frame"));
3509
+ });
3510
+ socket.addEventListener("close", () => {
3511
+ this.socket = null;
3512
+ this.messages.fail(new ConnectionError("WebSocket closed"));
3513
+ });
3514
+ socket.addEventListener("error", () => this.messages.fail(new ConnectionError("WebSocket failed")));
3515
+ }
3516
+ async close() {
3517
+ const socket = this.socket;
3518
+ this.socket = null;
3519
+ if (socket && socket.readyState < WebSocket.CLOSING)
3520
+ socket.close();
3521
+ this.messages.clear();
3522
+ }
3523
+ async send(data) {
3524
+ if (!this.socket || !this.connected)
3525
+ throw new ConnectionError("Transport is not connected");
3526
+ this.socket.send(data);
3527
+ }
3528
+ async recv() {
3529
+ if (!this.connected)
3530
+ throw new ConnectionError("Transport is not connected");
3531
+ return await this.messages.shift();
3532
+ }
3533
+ }
3534
+ // src/client.ts
3535
+ class BaseClient {
3536
+ router = new Router;
3537
+ app;
3538
+ config;
3539
+ stopping = false;
3540
+ constructor(config, _authFlow, app) {
3541
+ this.config = config;
3542
+ configureLogging(config.logLevel);
3543
+ this.app = app;
3544
+ this.app.bindClient(this);
3545
+ }
3546
+ get me() {
3547
+ return this.app.me;
3548
+ }
3549
+ get chats() {
3550
+ return this.app.chats;
3551
+ }
3552
+ get contacts() {
3553
+ return this.app.contacts;
3554
+ }
3555
+ get messages() {
3556
+ return this.app.messages;
3557
+ }
3558
+ get session() {
3559
+ return this.app.session;
3560
+ }
3561
+ get api() {
3562
+ return this.app.api;
3563
+ }
3564
+ get account() {
3565
+ return this.api.account;
3566
+ }
3567
+ get auth() {
3568
+ return this.api.auth;
3569
+ }
3570
+ get bots() {
3571
+ return this.api.bots;
3572
+ }
3573
+ get chatApi() {
3574
+ return this.api.chats;
3575
+ }
3576
+ get users() {
3577
+ return this.api.users;
3578
+ }
3579
+ async start() {
3580
+ this.stopping = false;
3581
+ for (;; ) {
3582
+ try {
3583
+ await this.app.start();
3584
+ await this.app.dispatcher.emitStart();
3585
+ await this.app.connection.waitClosed();
3586
+ await this.app.close();
3587
+ return;
3588
+ } catch (error) {
3589
+ await this.app.close();
3590
+ if (this.stopping)
3591
+ return;
3592
+ const failure = error instanceof Error ? error : new Error(String(error));
3593
+ await this.app.dispatcher.emitDisconnect(failure, this.config.reconnect, this.config.reconnectDelay);
3594
+ if (!this.config.reconnect)
3595
+ throw failure;
3596
+ await Bun.sleep(this.config.reconnectDelay * 1000);
3597
+ this.app = this.rebuildApp();
3598
+ this.app.bindClient(this);
3599
+ }
3600
+ }
3601
+ }
3602
+ async stop() {
3603
+ this.stopping = true;
3604
+ await this.app.close();
3605
+ }
3606
+ async close() {
3607
+ await this.stop();
3608
+ }
3609
+ async relogin(start = true) {
3610
+ if (!this.app.session)
3611
+ throw new Error("Cannot relogin before session is loaded");
3612
+ await this.app.store.deleteSession(this.app.session.token);
3613
+ await this.app.close();
3614
+ this.config.token = null;
3615
+ this.app = this.rebuildApp();
3616
+ this.app.bindClient(this);
3617
+ if (start)
3618
+ await this.start();
3619
+ }
3620
+ includeRouter(router2) {
3621
+ return this.router.includeRouter(router2);
3622
+ }
3623
+ onStart(handler) {
3624
+ return this.router.onStart(handler);
3625
+ }
3626
+ onError(handler) {
3627
+ return this.router.onError(handler);
3628
+ }
3629
+ onDisconnect(handler) {
3630
+ return this.router.onDisconnect(handler);
3631
+ }
3632
+ onMessage(handler, ...filters) {
3633
+ return this.router.onMessage(handler, ...filters);
3634
+ }
3635
+ onMessageEdit(handler, ...filters) {
3636
+ return this.router.onMessageEdit(handler, ...filters);
3637
+ }
3638
+ onMessageDelete(handler, ...filters) {
3639
+ return this.router.onMessageDelete(handler, ...filters);
3640
+ }
3641
+ onMessageRead(handler, ...filters) {
3642
+ return this.router.onMessageRead(handler, ...filters);
3643
+ }
3644
+ onTyping(handler, ...filters) {
3645
+ return this.router.onTyping(handler, ...filters);
3646
+ }
3647
+ onPresence(handler, ...filters) {
3648
+ return this.router.onPresence(handler, ...filters);
3649
+ }
3650
+ onReactionUpdate(handler, ...filters) {
3651
+ return this.router.onReactionUpdate(handler, ...filters);
3652
+ }
3653
+ onChatUpdate(handler, ...filters) {
3654
+ return this.router.onChatUpdate(handler, ...filters);
3655
+ }
3656
+ onRaw(handler, ...filters) {
3657
+ return this.router.onRaw(handler, ...filters);
3658
+ }
3659
+ async sendMessage(options) {
3660
+ return await this.api.messages.sendMessage(options);
3661
+ }
3662
+ async forwardMessage(options) {
3663
+ return await this.api.messages.forwardMessage(options);
3664
+ }
3665
+ async getMessages(chatId, messageIds) {
3666
+ return await this.api.messages.getMessages(chatId, messageIds);
3667
+ }
3668
+ async getMessage(chatId, messageId) {
3669
+ return await this.api.messages.getMessage(chatId, messageId);
3670
+ }
3671
+ async editMessage(options) {
3672
+ return await this.api.messages.editMessage(options);
3673
+ }
3674
+ async fetchHistory(options) {
3675
+ return await this.api.messages.fetchHistory(options);
3676
+ }
3677
+ async deleteMessage(options) {
3678
+ return await this.api.messages.deleteMessage(options);
3679
+ }
3680
+ async pinMessage(options) {
3681
+ return await this.api.messages.pinMessage(options);
3682
+ }
3683
+ async readMessage(messageId, chatId) {
3684
+ return await this.api.messages.readMessage(messageId, chatId);
3685
+ }
3686
+ async addReaction(chatId, messageId, reaction) {
3687
+ return await this.api.messages.addReaction(chatId, messageId, reaction);
3688
+ }
3689
+ async removeReaction(chatId, messageId) {
3690
+ return await this.api.messages.removeReaction(chatId, messageId);
3691
+ }
3692
+ async getReactions(chatId, messageIds) {
3693
+ return await this.api.messages.getReactions(chatId, messageIds);
3694
+ }
3695
+ async getVideoById(chatId, messageId, videoId) {
3696
+ return await this.api.messages.getVideoById(chatId, messageId, videoId);
3697
+ }
3698
+ async getFileById(chatId, messageId, fileId) {
3699
+ return await this.api.messages.getFileById(chatId, messageId, fileId);
3700
+ }
3701
+ async createGroup(name, participantIds = [], notify = true) {
3702
+ return await this.api.chats.createGroup(name, participantIds, notify);
3703
+ }
3704
+ async inviteUsersToGroup(chatId, userIds, showHistory = true) {
3705
+ return await this.api.chats.inviteUsersToGroup(chatId, userIds, showHistory);
3706
+ }
3707
+ async inviteUsersToChannel(chatId, userIds, showHistory = true) {
3708
+ return await this.api.chats.inviteUsersToChannel(chatId, userIds, showHistory);
3709
+ }
3710
+ async removeUsersFromGroup(chatId, userIds) {
3711
+ return await this.api.chats.removeUsersFromGroup(chatId, userIds);
3712
+ }
3713
+ async getChats(chatIds) {
3714
+ return await this.api.chats.getChats(chatIds);
3715
+ }
3716
+ async getChat(chatId) {
3717
+ return await this.api.chats.getChat(chatId);
3718
+ }
3719
+ async fetchChats(marker) {
3720
+ return await this.api.chats.fetchChats(marker);
3721
+ }
3722
+ async joinGroup(link) {
3723
+ return await this.api.chats.joinGroup(link);
3724
+ }
3725
+ async joinChannel(link) {
3726
+ return await this.api.chats.joinChannel(link);
3727
+ }
3728
+ async leaveGroup(chatId) {
3729
+ await this.api.chats.leaveGroup(chatId);
3730
+ }
3731
+ async leaveChannel(chatId) {
3732
+ await this.api.chats.leaveChannel(chatId);
3733
+ }
3734
+ async deleteChat(chatId, forMe = true) {
3735
+ return await this.api.chats.deleteChat(chatId, forMe);
3736
+ }
3737
+ async changeGroupSettings(chatId, settings) {
3738
+ await this.api.chats.changeGroupSettings(chatId, settings);
3739
+ }
3740
+ async changeGroupProfile(chatId, name, description = null) {
3741
+ await this.api.chats.changeGroupProfile(chatId, name, description);
3742
+ }
3743
+ async resolveGroupByLink(link) {
3744
+ return await this.api.chats.resolveGroupByLink(link);
3745
+ }
3746
+ async reworkInviteLink(chatId) {
3747
+ return await this.api.chats.reworkInviteLink(chatId);
3748
+ }
3749
+ async getJoinRequests(chatId, count = 100) {
3750
+ return await this.api.chats.getJoinRequests(chatId, count);
3751
+ }
3752
+ async confirmJoinRequests(chatId, userIds, showHistory = true) {
3753
+ return await this.api.chats.confirmJoinRequests(chatId, userIds, showHistory);
3754
+ }
3755
+ async confirmJoinRequest(chatId, userId, showHistory = true) {
3756
+ return await this.api.chats.confirmJoinRequest(chatId, userId, showHistory);
3757
+ }
3758
+ async declineJoinRequests(chatId, userIds) {
3759
+ return await this.api.chats.declineJoinRequests(chatId, userIds);
3760
+ }
3761
+ async declineJoinRequest(chatId, userId) {
3762
+ return await this.api.chats.declineJoinRequest(chatId, userId);
3763
+ }
3764
+ getCachedUser(userId) {
3765
+ return this.api.users.getCachedUser(userId);
3766
+ }
3767
+ async getUsers(userIds) {
3768
+ return await this.api.users.getUsers(userIds);
3769
+ }
3770
+ async getUser(userId) {
3771
+ return await this.api.users.getUser(userId);
3772
+ }
3773
+ async fetchUsers(userIds) {
3774
+ return await this.api.users.fetchUsers(userIds);
3775
+ }
3776
+ async searchByPhone(phone) {
3777
+ return await this.api.users.searchByPhone(phone);
3778
+ }
3779
+ async addContact(contactId) {
3780
+ return await this.api.users.addContact(contactId);
3781
+ }
3782
+ async removeContact(contactId) {
3783
+ return await this.api.users.removeContact(contactId);
3784
+ }
3785
+ async importContacts(contacts) {
3786
+ return await this.api.users.importContacts(contacts);
3787
+ }
3788
+ getChatId(firstUserId, secondUserId) {
3789
+ return this.api.users.getChatId(firstUserId, secondUserId);
3790
+ }
3791
+ async setTwoFactor(options) {
3792
+ return await this.auth.setTwoFactor(options);
3793
+ }
3794
+ async removeTwoFactor(password) {
3795
+ return await this.auth.removeTwoFactor(password);
3796
+ }
3797
+ async changePassword(oldPassword, newPassword) {
3798
+ return await this.auth.changePassword(oldPassword, newPassword);
3799
+ }
3800
+ async authorizeQrLogin(qrLink) {
3801
+ return await this.auth.authorizeQrLogin(qrLink);
3802
+ }
3803
+ async checkTwoFactor() {
3804
+ return await this.auth.checkTwoFactor();
3805
+ }
3806
+ async changeProfile(options) {
3807
+ return await this.account.changeProfile(options);
3808
+ }
3809
+ async requestProfilePhotoUploadUrl() {
3810
+ return await this.account.requestProfilePhotoUploadUrl();
3811
+ }
3812
+ async createFolder(title, chatInclude, filters = []) {
3813
+ return await this.account.createFolder(title, chatInclude, filters);
3814
+ }
3815
+ async getFolders(folderSync = 0) {
3816
+ return await this.account.getFolders(folderSync);
3817
+ }
3818
+ async updateFolder(folderId, title, chatInclude = [], filters = [], options = []) {
3819
+ return await this.account.updateFolder(folderId, title, chatInclude, filters, options);
3820
+ }
3821
+ async deleteFolder(folderId) {
3822
+ return await this.account.deleteFolder(folderId);
3823
+ }
3824
+ async closeAllSessions() {
3825
+ return await this.account.closeAllSessions();
3826
+ }
3827
+ async logout() {
3828
+ return await this.account.logout();
3829
+ }
3830
+ async getSessions() {
3831
+ return await this.users.getSessions();
3832
+ }
3833
+ async getBotInitData(botId, chatId, startParam) {
3834
+ return await this.bots.getBotInitData(botId, chatId, startParam);
3835
+ }
3836
+ }
3837
+
3838
+ class Client extends BaseClient {
3839
+ authFlow;
3840
+ constructor(options) {
3841
+ const config = resolveConfig({
3842
+ phone: options.phone,
3843
+ ...options.workDir !== undefined ? { workDir: options.workDir } : {},
3844
+ ...options.sessionName !== undefined ? { sessionName: options.sessionName } : {},
3845
+ ...options.extraConfig !== undefined ? { extra: options.extraConfig } : {}
3846
+ });
3847
+ const authFlow = options.authFlow ?? new SmsAuthFlow(options.smsCodeProvider, options.passwordProvider);
3848
+ const transport = new TcpTransport(config.host, config.port, config.useTls, config.proxy);
3849
+ const connection2 = new ConnectionManager(new TcpReader(transport), transport, new TcpProtocol);
3850
+ const placeholder = new Router;
3851
+ const app = new App(connection2, config, authFlow, placeholder);
3852
+ super(config, authFlow, app);
3853
+ this.authFlow = authFlow;
3854
+ app.dispatcher.rootRouter.children.push(this.router);
3855
+ }
3856
+ rebuildApp() {
3857
+ const transport = new TcpTransport(this.config.host, this.config.port, this.config.useTls, this.config.proxy);
3858
+ const connection2 = new ConnectionManager(new TcpReader(transport), transport, new TcpProtocol);
3859
+ return new App(connection2, this.config, this.authFlow, this.router);
3860
+ }
3861
+ }
3862
+
3863
+ class WebClient extends BaseClient {
3864
+ authFlow;
3865
+ constructor(options = {}) {
3866
+ const config = resolveConfig({
3867
+ phone: null,
3868
+ ...options.workDir !== undefined ? { workDir: options.workDir } : {},
3869
+ ...options.sessionName !== undefined ? { sessionName: options.sessionName } : {},
3870
+ web: true,
3871
+ ...options.extraConfig !== undefined ? { extra: options.extraConfig } : {}
3872
+ });
3873
+ const authFlow = options.authFlow ?? new QrAuthFlow(options.qrHandler, options.passwordProvider);
3874
+ const transport = new WebSocketTransport(config.url, config.proxy);
3875
+ const connection2 = new ConnectionManager(new WsReader(transport), transport, new WsProtocol);
3876
+ const placeholder = new Router;
3877
+ const app = new App(connection2, config, authFlow, placeholder);
3878
+ super(config, authFlow, app);
3879
+ this.authFlow = authFlow;
3880
+ app.dispatcher.rootRouter.children.push(this.router);
3881
+ }
3882
+ rebuildApp() {
3883
+ const transport = new WebSocketTransport(this.config.url, this.config.proxy);
3884
+ const connection2 = new ConnectionManager(new WsReader(transport), transport, new WsProtocol);
3885
+ return new App(connection2, this.config, this.authFlow, this.router);
3886
+ }
3887
+ }
3888
+
3889
+ // src/index.ts
3890
+ var VERSION = "0.1.0";
3891
+ export {
3892
+ toMaxId,
3893
+ resolveSync,
3894
+ resolveConfig,
3895
+ parseReadState,
3896
+ parseReactionInfo,
3897
+ parseAttachment,
3898
+ optionalRecord,
3899
+ optionalMaxId,
3900
+ mainTabParams,
3901
+ getLogger,
3902
+ formatMarkdown,
3903
+ defaultTelemetryTiming,
3904
+ defaultSyncState,
3905
+ defaultProfiles,
3906
+ consoleSmsCodeProvider,
3907
+ consoleQrHandler,
3908
+ consolePasswordProvider,
3909
+ consoleEmailCodeProvider,
3910
+ configureLogging,
3911
+ asString,
3912
+ asRecord,
3913
+ asNumber,
3914
+ asBoolean,
3915
+ asArray,
3916
+ ZstdCompression,
3917
+ WsProtocol,
3918
+ WebClient,
3919
+ Video,
3920
+ VERSION,
3921
+ UserApi,
3922
+ User,
3923
+ UploadError,
3924
+ UploadApi,
3925
+ TsMaxError,
3926
+ TimeoutError,
3927
+ TelemetryService,
3928
+ TelemetryPayloadBuilder,
3929
+ TcpProtocol,
3930
+ TcpPayloadDecoder,
3931
+ TcpPacketFramer,
3932
+ TCP_HEADER_SIZE,
3933
+ SqliteSessionStore,
3934
+ SmsAuthFlow,
3935
+ Screen,
3936
+ Router,
3937
+ QrAuthFlow,
3938
+ Profile,
3939
+ Photo,
3940
+ Opcode,
3941
+ NavigationPlanner,
3942
+ MsgpackPayloadCodec,
3943
+ MessageApi,
3944
+ Message,
3945
+ MaxFile,
3946
+ Lz4BlockCompression,
3947
+ File,
3948
+ EventType,
3949
+ Dispatcher,
3950
+ DeviceType,
3951
+ DEFAULT_CONFIG_HASH,
3952
+ ConnectionError,
3953
+ Command,
3954
+ Client,
3955
+ ChatType,
3956
+ ChatApi,
3957
+ Chat,
3958
+ BotsApi,
3959
+ BaseClient,
3960
+ AuthApi,
3961
+ AttachmentType,
3962
+ ApiFacade,
3963
+ ApiError,
3964
+ AccountApi
3965
+ };
3966
+
3967
+ //# debugId=DCB99DD4AB754F0E64756E2164756E21