@melandlabs/integrations-telegram 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.
- package/LICENSE +201 -0
- package/README.md +32 -0
- package/dist/adapter.d.ts +125 -0
- package/dist/adapter.js +1670 -0
- package/dist/adapter.js.map +1 -0
- package/dist/conversation-store.d.ts +48 -0
- package/dist/conversation-store.js +147 -0
- package/dist/conversation-store.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1810 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown.d.ts +14 -0
- package/dist/markdown.js +565 -0
- package/dist/markdown.js.map +1 -0
- package/dist/state.d.ts +21 -0
- package/dist/state.js +38 -0
- package/dist/state.js.map +1 -0
- package/dist/tdata-converter.d.ts +21 -0
- package/dist/tdata-converter.js +640 -0
- package/dist/tdata-converter.js.map +1 -0
- package/dist/tdata-decrypter-index.d.ts +314 -0
- package/dist/tdata-decrypter-index.js +671 -0
- package/dist/tdata-decrypter-index.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import * as crypto from 'crypto';
|
|
3
|
+
import * as fs2 from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { AuthKey } from 'telegram/crypto/AuthKey';
|
|
7
|
+
import { StringSession } from 'telegram/sessions';
|
|
8
|
+
|
|
9
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
|
+
var __esm = (fn, res) => function __init() {
|
|
11
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
|
+
};
|
|
13
|
+
function xorBuffer(a, b) {
|
|
14
|
+
for (let i = 0; i < a.length; i++) {
|
|
15
|
+
a.writeUInt8(a.readUInt8(i) ^ b.readUInt8(i), i);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function igeCrypt(buffer, aesKey, aesIv, isEncrypt) {
|
|
19
|
+
const cipher = crypto.createDecipheriv("aes-256-ecb", aesKey, null);
|
|
20
|
+
cipher.setAutoPadding(false);
|
|
21
|
+
const result = Buffer.allocUnsafe(buffer.length);
|
|
22
|
+
let prevCipherblock;
|
|
23
|
+
let prevPlainblock;
|
|
24
|
+
{
|
|
25
|
+
prevCipherblock = aesIv.subarray(16, 32);
|
|
26
|
+
prevPlainblock = aesIv.subarray(0, 16);
|
|
27
|
+
}
|
|
28
|
+
const current = Buffer.allocUnsafe(16);
|
|
29
|
+
for (let offset = 0; offset < buffer.length; offset += 16) {
|
|
30
|
+
const chunk = buffer.subarray(offset, offset + 16);
|
|
31
|
+
chunk.copy(current);
|
|
32
|
+
xorBuffer(current, prevCipherblock);
|
|
33
|
+
const crypted = Buffer.from(cipher.update(current));
|
|
34
|
+
xorBuffer(crypted, prevPlainblock);
|
|
35
|
+
crypted.copy(result, offset);
|
|
36
|
+
prevCipherblock = crypted;
|
|
37
|
+
prevPlainblock = chunk;
|
|
38
|
+
}
|
|
39
|
+
cipher.final();
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
function createLocalKey(passcode, salt) {
|
|
43
|
+
const iterations = passcode.length > 0 ? kStrongIterationsCount : 1;
|
|
44
|
+
const password = crypto.createHash("sha512");
|
|
45
|
+
password.update(salt);
|
|
46
|
+
password.update(passcode);
|
|
47
|
+
password.update(salt);
|
|
48
|
+
return crypto.pbkdf2Sync(password.digest(), salt, iterations, 256, "sha512");
|
|
49
|
+
}
|
|
50
|
+
function decryptLocal(encryptedMsg, localKey) {
|
|
51
|
+
const msgKey = encryptedMsg.subarray(0, 16);
|
|
52
|
+
const encryptedData = encryptedMsg.subarray(16);
|
|
53
|
+
const decrypted = aesDecryptLocal(encryptedData, msgKey, localKey);
|
|
54
|
+
const sha1 = crypto.createHash("sha1");
|
|
55
|
+
sha1.update(decrypted);
|
|
56
|
+
const calculatedMsgKey = sha1.digest().subarray(0, 16);
|
|
57
|
+
if (calculatedMsgKey.compare(msgKey) !== 0) {
|
|
58
|
+
throw new CryptoException("bad decrypt key, data not decrypted - incorrect password");
|
|
59
|
+
}
|
|
60
|
+
const length = decrypted.readUInt32LE(0);
|
|
61
|
+
if (length > decrypted.length) {
|
|
62
|
+
throw new CryptoException(`corrupted data. wrong length: ${length}`);
|
|
63
|
+
}
|
|
64
|
+
return decrypted.subarray(4, 4 + length);
|
|
65
|
+
}
|
|
66
|
+
function aesDecryptLocal(encryptedData, msgKey, localKey) {
|
|
67
|
+
const { aesKey, aesIv } = prepareAesOldMtp(localKey, msgKey, false);
|
|
68
|
+
return igeCrypt(encryptedData, aesKey, aesIv);
|
|
69
|
+
}
|
|
70
|
+
function prepareAesOldMtp(localKey, msgKey, send = false) {
|
|
71
|
+
const x = send ? 0 : 8;
|
|
72
|
+
const keyPos = (pos, size) => {
|
|
73
|
+
return localKey.subarray(pos, pos + size);
|
|
74
|
+
};
|
|
75
|
+
const dataA = Buffer.concat([msgKey, keyPos(x, 32)]);
|
|
76
|
+
const dataB = Buffer.concat([keyPos(x + 32, 16), msgKey, keyPos(x + 48, 16)]);
|
|
77
|
+
const dataC = Buffer.concat([keyPos(x + 64, 32), msgKey]);
|
|
78
|
+
const dataD = Buffer.concat([msgKey, keyPos(x + 96, 32)]);
|
|
79
|
+
const sha1A = crypto.createHash("sha1").update(dataA).digest();
|
|
80
|
+
const sha1B = crypto.createHash("sha1").update(dataB).digest();
|
|
81
|
+
const sha1C = crypto.createHash("sha1").update(dataC).digest();
|
|
82
|
+
const sha1D = crypto.createHash("sha1").update(dataD).digest();
|
|
83
|
+
const aesKey = Buffer.concat([sha1A.subarray(0, 8), sha1B.subarray(8, 20), sha1C.subarray(4, 16)]);
|
|
84
|
+
const aesIv = Buffer.concat([
|
|
85
|
+
sha1A.subarray(8, 20),
|
|
86
|
+
sha1B.subarray(0, 8),
|
|
87
|
+
sha1C.subarray(16, 20),
|
|
88
|
+
sha1D.subarray(0, 8)
|
|
89
|
+
]);
|
|
90
|
+
return { aesKey, aesIv };
|
|
91
|
+
}
|
|
92
|
+
var kStrongIterationsCount, CryptoException;
|
|
93
|
+
var init_crypto = __esm({
|
|
94
|
+
"src/tdata-decrypter/crypto.ts"() {
|
|
95
|
+
kStrongIterationsCount = 1e5;
|
|
96
|
+
CryptoException = class extends Error {
|
|
97
|
+
constructor(message) {
|
|
98
|
+
super(message);
|
|
99
|
+
this.name = "CryptoException";
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
function readBytes(buffer, offset, size) {
|
|
105
|
+
if (offset + size > buffer.length) {
|
|
106
|
+
throw new Error(`Cannot read ${size} bytes at offset ${offset}, buffer length is ${buffer.length}`);
|
|
107
|
+
}
|
|
108
|
+
return buffer.subarray(offset, offset + size);
|
|
109
|
+
}
|
|
110
|
+
function readQtInteger(buffer, offset, size, signed) {
|
|
111
|
+
const value = readBytes(buffer, offset, size);
|
|
112
|
+
const hex = value.toString("hex");
|
|
113
|
+
let num = BigInt(`0x${hex}`);
|
|
114
|
+
if (signed && num >= BigInt(2) ** BigInt(size * 8 - 1)) {
|
|
115
|
+
num -= BigInt(2) ** BigInt(size * 8);
|
|
116
|
+
}
|
|
117
|
+
return Number(num);
|
|
118
|
+
}
|
|
119
|
+
function readQtInt32(buffer, offset) {
|
|
120
|
+
return readQtInteger(buffer, offset, 4, true);
|
|
121
|
+
}
|
|
122
|
+
function readQtUint64(buffer, offset) {
|
|
123
|
+
return readQtInteger(buffer, offset, 8, false);
|
|
124
|
+
}
|
|
125
|
+
function readQtByteArray(buffer, offset) {
|
|
126
|
+
const length = readQtInt32(buffer, offset);
|
|
127
|
+
let newOffset = offset + 4;
|
|
128
|
+
if (length <= 0) {
|
|
129
|
+
return { value: Buffer.alloc(0), newOffset };
|
|
130
|
+
}
|
|
131
|
+
const value = readBytes(buffer, newOffset, length);
|
|
132
|
+
newOffset += length;
|
|
133
|
+
return { value, newOffset };
|
|
134
|
+
}
|
|
135
|
+
function readQtUtf8(buffer, offset) {
|
|
136
|
+
const { value, newOffset } = readQtByteArray(buffer, offset);
|
|
137
|
+
const str = value.toString("utf16le");
|
|
138
|
+
return { value: str, newOffset };
|
|
139
|
+
}
|
|
140
|
+
var init_qt = __esm({
|
|
141
|
+
"src/tdata-decrypter/qt.ts"() {
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// src/tdata-decrypter/settings.ts
|
|
146
|
+
function readSettingsBlock(version, buffer, offset, blockId) {
|
|
147
|
+
switch (blockId) {
|
|
148
|
+
case 6 /* dbiAutoStart */:
|
|
149
|
+
case 7 /* dbiStartMinimized */:
|
|
150
|
+
case 29 /* dbiSendToMenu */:
|
|
151
|
+
case 10 /* dbiSeenTrayTooltip */:
|
|
152
|
+
case 12 /* dbiAutoUpdate */: {
|
|
153
|
+
const value = readQtInt32(buffer, offset) === 1;
|
|
154
|
+
return { value, newOffset: offset + 4 };
|
|
155
|
+
}
|
|
156
|
+
case 41 /* dbiSongVolumeOld */: {
|
|
157
|
+
const value = readQtInt32(buffer, offset) / 1e6;
|
|
158
|
+
return { value, newOffset: offset + 4 };
|
|
159
|
+
}
|
|
160
|
+
case 13 /* dbiLastUpdateCheck */:
|
|
161
|
+
case 88 /* dbiScalePercent */:
|
|
162
|
+
case 87 /* dbiPowerSaving */: {
|
|
163
|
+
const value = readQtInt32(buffer, offset);
|
|
164
|
+
return { value, newOffset: offset + 4 };
|
|
165
|
+
}
|
|
166
|
+
case 96 /* dbiFallbackProductionConfig */:
|
|
167
|
+
case 94 /* dbiApplicationSettings */: {
|
|
168
|
+
const { value, newOffset } = readQtByteArray(buffer, offset);
|
|
169
|
+
return { value, newOffset };
|
|
170
|
+
}
|
|
171
|
+
case 35 /* dbiDialogLastPath */: {
|
|
172
|
+
const { value, newOffset } = readQtUtf8(buffer, offset);
|
|
173
|
+
return { value, newOffset };
|
|
174
|
+
}
|
|
175
|
+
case 84 /* dbiThemeKey */: {
|
|
176
|
+
const day = readQtUint64(buffer, offset);
|
|
177
|
+
const night = readQtUint64(buffer, offset + 8);
|
|
178
|
+
const nightMode = readQtInt32(buffer, offset + 16) === 1;
|
|
179
|
+
return {
|
|
180
|
+
value: { day, night, nightMode },
|
|
181
|
+
newOffset: offset + 20
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
case 97 /* dbiBackgroundKey */: {
|
|
185
|
+
const day = readQtUint64(buffer, offset);
|
|
186
|
+
const night = readQtUint64(buffer, offset + 8);
|
|
187
|
+
return {
|
|
188
|
+
value: { day, night },
|
|
189
|
+
newOffset: offset + 16
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
case 85 /* dbiTileBackground */: {
|
|
193
|
+
const day = readQtInt32(buffer, offset);
|
|
194
|
+
const night = readQtInt32(buffer, offset + 4);
|
|
195
|
+
return {
|
|
196
|
+
value: { day, night },
|
|
197
|
+
newOffset: offset + 8
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
case 78 /* dbiLangPackKey */: {
|
|
201
|
+
const value = readQtUint64(buffer, offset);
|
|
202
|
+
return { value, newOffset: offset + 8 };
|
|
203
|
+
}
|
|
204
|
+
case 75 /* dbiMtpAuthorization */: {
|
|
205
|
+
const { value, newOffset } = readQtByteArray(buffer, offset);
|
|
206
|
+
return { value, newOffset };
|
|
207
|
+
}
|
|
208
|
+
default:
|
|
209
|
+
throw new Error(`Unknown block ID while reading settings: ${blockId}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function readSettingsBlocks(version, buffer) {
|
|
213
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
214
|
+
let offset = 0;
|
|
215
|
+
try {
|
|
216
|
+
while (offset < buffer.length) {
|
|
217
|
+
const blockId = readQtInt32(buffer, offset);
|
|
218
|
+
offset += 4;
|
|
219
|
+
const { value, newOffset } = readSettingsBlock(version, buffer, offset, blockId);
|
|
220
|
+
offset = newOffset;
|
|
221
|
+
blocks.set(blockId, value);
|
|
222
|
+
}
|
|
223
|
+
} catch {
|
|
224
|
+
}
|
|
225
|
+
return blocks;
|
|
226
|
+
}
|
|
227
|
+
var init_settings = __esm({
|
|
228
|
+
"src/tdata-decrypter/settings.ts"() {
|
|
229
|
+
init_qt();
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
function decryptKeyDataTdf(passcode, keyDataTdf) {
|
|
233
|
+
const stream = keyDataTdf.encryptedData;
|
|
234
|
+
let offset = 0;
|
|
235
|
+
const { value: salt, newOffset: offset1 } = readQtByteArray(stream, offset);
|
|
236
|
+
offset = offset1;
|
|
237
|
+
const { value: keyEncrypted, newOffset: offset2 } = readQtByteArray(stream, offset);
|
|
238
|
+
offset = offset2;
|
|
239
|
+
const { value: infoEncrypted, newOffset: _ } = readQtByteArray(stream, offset);
|
|
240
|
+
const passcodeKey = createLocalKey(passcode, salt);
|
|
241
|
+
const localKey = decryptLocal(keyEncrypted, passcodeKey);
|
|
242
|
+
const infoDecrypted = decryptLocal(infoEncrypted, localKey);
|
|
243
|
+
return [localKey, infoDecrypted];
|
|
244
|
+
}
|
|
245
|
+
function readKeyDataAccounts(data) {
|
|
246
|
+
let offset = 0;
|
|
247
|
+
const count = data.readInt32BE(offset);
|
|
248
|
+
offset += 4;
|
|
249
|
+
const indexes = [];
|
|
250
|
+
for (let i = 0; i < count; i++) {
|
|
251
|
+
const index = data.readInt32BE(offset);
|
|
252
|
+
offset += 4;
|
|
253
|
+
indexes.push(index);
|
|
254
|
+
}
|
|
255
|
+
const mainAccount = data.readInt32BE(offset);
|
|
256
|
+
return [indexes, mainAccount];
|
|
257
|
+
}
|
|
258
|
+
var init_storage = __esm({
|
|
259
|
+
"src/tdata-decrypter/storage.ts"() {
|
|
260
|
+
init_crypto();
|
|
261
|
+
init_qt();
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
function parseRawTdf(data) {
|
|
265
|
+
if (data.subarray(0, 4).compare(TDF_MAGIC) !== 0) {
|
|
266
|
+
throw new WrongMagicTdfParserError();
|
|
267
|
+
}
|
|
268
|
+
const version = data.readUInt32LE(4);
|
|
269
|
+
const encryptedData = data.subarray(8, data.length - 16);
|
|
270
|
+
const hashsum = data.subarray(data.length - 16);
|
|
271
|
+
const actualMd5 = crypto.createHash("md5");
|
|
272
|
+
actualMd5.update(encryptedData);
|
|
273
|
+
const lengthBuffer = Buffer.allocUnsafe(4);
|
|
274
|
+
lengthBuffer.writeUInt32LE(encryptedData.length, 0);
|
|
275
|
+
actualMd5.update(lengthBuffer);
|
|
276
|
+
const versionBuffer = Buffer.allocUnsafe(4);
|
|
277
|
+
versionBuffer.writeUInt32LE(version, 0);
|
|
278
|
+
actualMd5.update(versionBuffer);
|
|
279
|
+
actualMd5.update(TDF_MAGIC);
|
|
280
|
+
const actualMd5Digest = actualMd5.digest();
|
|
281
|
+
if (actualMd5Digest.compare(hashsum) !== 0) {
|
|
282
|
+
throw new WrongHashsumTdfParserError();
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
version,
|
|
286
|
+
encryptedData,
|
|
287
|
+
hashsum
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function readTdfFile(filepath) {
|
|
291
|
+
const candidates = [`${filepath}s`, filepath];
|
|
292
|
+
for (const candidate of candidates) {
|
|
293
|
+
try {
|
|
294
|
+
const data = fs2.readFileSync(candidate);
|
|
295
|
+
return parseRawTdf(data);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
if (error.code !== "ENOENT") {
|
|
298
|
+
throw error;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
throw new Error(`TDF file not found: ${filepath}`);
|
|
303
|
+
}
|
|
304
|
+
var TDF_MAGIC, TdfParserError, WrongMagicTdfParserError, WrongHashsumTdfParserError;
|
|
305
|
+
var init_tdf = __esm({
|
|
306
|
+
"src/tdata-decrypter/tdf.ts"() {
|
|
307
|
+
TDF_MAGIC = Buffer.from("TDF$", "ascii");
|
|
308
|
+
TdfParserError = class extends Error {
|
|
309
|
+
constructor(message) {
|
|
310
|
+
super(message);
|
|
311
|
+
this.name = "TdfParserError";
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
WrongMagicTdfParserError = class extends TdfParserError {
|
|
315
|
+
constructor(message = "Wrong magic. Not a TDF file?") {
|
|
316
|
+
super(message);
|
|
317
|
+
this.name = "WrongMagicTdfParserError";
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
WrongHashsumTdfParserError = class extends TdfParserError {
|
|
321
|
+
constructor(message = "Wrong hashsum. Corrupted file?") {
|
|
322
|
+
super(message);
|
|
323
|
+
this.name = "WrongHashsumTdfParserError";
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
function fileKeyToStr(fileKey) {
|
|
329
|
+
let result = "";
|
|
330
|
+
for (const b of fileKey) {
|
|
331
|
+
const hex = b.toString(16).toUpperCase();
|
|
332
|
+
const padded = hex.length === 1 ? `0${hex}` : hex;
|
|
333
|
+
result += padded[1] + padded[0];
|
|
334
|
+
}
|
|
335
|
+
return result;
|
|
336
|
+
}
|
|
337
|
+
function computeDataNameKey(dataName) {
|
|
338
|
+
const fileKey = crypto.createHash("md5").update(dataName, "utf8").digest().subarray(0, 8);
|
|
339
|
+
return fileKeyToStr(fileKey);
|
|
340
|
+
}
|
|
341
|
+
function composeAccountName(dataName, index) {
|
|
342
|
+
if (index > 0) {
|
|
343
|
+
return `${dataName}#${index + 1}`;
|
|
344
|
+
}
|
|
345
|
+
return dataName;
|
|
346
|
+
}
|
|
347
|
+
function readMtpAuthorization(buffer) {
|
|
348
|
+
let offset = 0;
|
|
349
|
+
const legacyUserId = readQtInt32(buffer, offset);
|
|
350
|
+
const legacyMainDcId = readQtInt32(buffer, offset + 4);
|
|
351
|
+
offset += 8;
|
|
352
|
+
let userId;
|
|
353
|
+
let mainDcId;
|
|
354
|
+
if (legacyUserId === -1 && legacyMainDcId === -1) {
|
|
355
|
+
userId = Number(readQtUint64(buffer, offset));
|
|
356
|
+
offset += 8;
|
|
357
|
+
mainDcId = readQtInt32(buffer, offset);
|
|
358
|
+
offset += 4;
|
|
359
|
+
} else {
|
|
360
|
+
userId = legacyUserId;
|
|
361
|
+
mainDcId = legacyMainDcId;
|
|
362
|
+
}
|
|
363
|
+
const count = readQtInt32(buffer, offset);
|
|
364
|
+
offset += 4;
|
|
365
|
+
const keys = /* @__PURE__ */ new Map();
|
|
366
|
+
for (let i = 0; i < count; i++) {
|
|
367
|
+
const dcId = readQtInt32(buffer, offset);
|
|
368
|
+
offset += 4;
|
|
369
|
+
const key = Buffer.from(buffer.subarray(offset, offset + 256));
|
|
370
|
+
offset += 256;
|
|
371
|
+
keys.set(dcId, key);
|
|
372
|
+
}
|
|
373
|
+
const destroyCount = readQtInt32(buffer, offset);
|
|
374
|
+
offset += 4;
|
|
375
|
+
const keysToDestroy = /* @__PURE__ */ new Map();
|
|
376
|
+
for (let i = 0; i < destroyCount; i++) {
|
|
377
|
+
const dcId = readQtInt32(buffer, offset);
|
|
378
|
+
offset += 4;
|
|
379
|
+
const key = Buffer.from(buffer.subarray(offset, offset + 256));
|
|
380
|
+
offset += 256;
|
|
381
|
+
keysToDestroy.set(dcId, key);
|
|
382
|
+
}
|
|
383
|
+
return {
|
|
384
|
+
userId,
|
|
385
|
+
currentDcId: mainDcId,
|
|
386
|
+
keys,
|
|
387
|
+
keysToDestroy
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
var AccountReader, TdataReader;
|
|
391
|
+
var init_decrypter = __esm({
|
|
392
|
+
"src/tdata-decrypter/decrypter.ts"() {
|
|
393
|
+
init_crypto();
|
|
394
|
+
init_qt();
|
|
395
|
+
init_settings();
|
|
396
|
+
init_storage();
|
|
397
|
+
init_tdf();
|
|
398
|
+
AccountReader = class {
|
|
399
|
+
basePath;
|
|
400
|
+
index;
|
|
401
|
+
accountName;
|
|
402
|
+
dataNameKey;
|
|
403
|
+
constructor(basePath, index, dataName) {
|
|
404
|
+
this.basePath = basePath;
|
|
405
|
+
this.index = index;
|
|
406
|
+
this.accountName = composeAccountName(dataName, index);
|
|
407
|
+
this.dataNameKey = computeDataNameKey(this.accountName);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Read account data using local key
|
|
411
|
+
*/
|
|
412
|
+
read(localKey) {
|
|
413
|
+
const mtpData = this.readMtpData(localKey);
|
|
414
|
+
return {
|
|
415
|
+
index: this.index,
|
|
416
|
+
mtpData
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Read MTP data
|
|
421
|
+
*/
|
|
422
|
+
readMtpData(localKey) {
|
|
423
|
+
const mtpDataFilePath = path.join(this.basePath, this.dataNameKey);
|
|
424
|
+
const tdfFile = readTdfFile(mtpDataFilePath);
|
|
425
|
+
const offset = 0;
|
|
426
|
+
const { value: encryptedData, newOffset: _ } = readQtByteArray(
|
|
427
|
+
Buffer.from(tdfFile.encryptedData),
|
|
428
|
+
offset
|
|
429
|
+
);
|
|
430
|
+
const decrypted = decryptLocal(encryptedData, localKey);
|
|
431
|
+
const blocks = readSettingsBlocks(tdfFile.version, decrypted);
|
|
432
|
+
const mtpAuth = blocks.get(75);
|
|
433
|
+
if (!mtpAuth || !(mtpAuth instanceof Buffer)) {
|
|
434
|
+
throw new Error("MTP authorization not found");
|
|
435
|
+
}
|
|
436
|
+
return readMtpAuthorization(mtpAuth);
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
TdataReader = class _TdataReader {
|
|
440
|
+
basePath;
|
|
441
|
+
dataName;
|
|
442
|
+
static DEFAULT_DATANAME = "data";
|
|
443
|
+
constructor(basePath, dataName) {
|
|
444
|
+
this.basePath = basePath;
|
|
445
|
+
this.dataName = dataName ?? _TdataReader.DEFAULT_DATANAME;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Read tdata directory
|
|
449
|
+
*/
|
|
450
|
+
read(passcode) {
|
|
451
|
+
const settings = this.readSettings();
|
|
452
|
+
const [localKey, accountIndexes] = this.readKeyData(passcode ?? "");
|
|
453
|
+
const accounts = /* @__PURE__ */ new Map();
|
|
454
|
+
for (const accountIndex of accountIndexes) {
|
|
455
|
+
const reader = new AccountReader(this.basePath, accountIndex, this.dataName);
|
|
456
|
+
const account = reader.read(localKey);
|
|
457
|
+
accounts.set(accountIndex, account);
|
|
458
|
+
}
|
|
459
|
+
return { settings, accounts };
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Read key data
|
|
463
|
+
*/
|
|
464
|
+
readKeyData(passcode) {
|
|
465
|
+
const keyDataName = `key_${this.dataName}`;
|
|
466
|
+
const keyDataPath = path.join(this.basePath, keyDataName);
|
|
467
|
+
const keyDataTdf = readTdfFile(keyDataPath);
|
|
468
|
+
const [localKey, infoDecrypted] = decryptKeyDataTdf(Buffer.from(passcode), keyDataTdf);
|
|
469
|
+
const [accountIndexes] = readKeyDataAccounts(infoDecrypted);
|
|
470
|
+
return [localKey, accountIndexes];
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Read settings
|
|
474
|
+
*/
|
|
475
|
+
readSettings() {
|
|
476
|
+
const settingsPath = path.join(this.basePath, "settings");
|
|
477
|
+
const settingsTdf = readTdfFile(settingsPath);
|
|
478
|
+
const decrypted = settingsTdf.encryptedData;
|
|
479
|
+
return readSettingsBlocks(settingsTdf.version, decrypted);
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// src/tdata-decrypter/index.ts
|
|
486
|
+
init_decrypter();
|
|
487
|
+
init_tdf();
|
|
488
|
+
init_crypto();
|
|
489
|
+
init_qt();
|
|
490
|
+
init_storage();
|
|
491
|
+
init_settings();
|
|
492
|
+
init_tdf();
|
|
493
|
+
init_crypto();
|
|
494
|
+
|
|
495
|
+
// src/tdata-converter.ts
|
|
496
|
+
var __filename$1 = fileURLToPath(import.meta.url);
|
|
497
|
+
path.dirname(__filename$1);
|
|
498
|
+
function getServerAddress(dcId) {
|
|
499
|
+
const addresses = {
|
|
500
|
+
1: "149.154.175.53",
|
|
501
|
+
2: "149.154.167.51",
|
|
502
|
+
3: "149.154.175.100",
|
|
503
|
+
4: "149.154.167.92",
|
|
504
|
+
5: "149.154.171.5"
|
|
505
|
+
};
|
|
506
|
+
return addresses[dcId] || "149.154.175.53";
|
|
507
|
+
}
|
|
508
|
+
function extractAuthKey(tdataPath) {
|
|
509
|
+
console.log("[TdataConverter] Using pure JS implementation to extract auth key...");
|
|
510
|
+
try {
|
|
511
|
+
const reader = new TdataReader(tdataPath);
|
|
512
|
+
const result = reader.read();
|
|
513
|
+
if (!result.accounts || result.accounts.size === 0) {
|
|
514
|
+
throw new Error("No accounts found in tdata. Please make sure you're logged into Telegram Desktop.");
|
|
515
|
+
}
|
|
516
|
+
const firstAccount = result.accounts.values().next().value;
|
|
517
|
+
if (!firstAccount) {
|
|
518
|
+
throw new Error("No accounts found in tdata.");
|
|
519
|
+
}
|
|
520
|
+
const { mtpData } = firstAccount;
|
|
521
|
+
const authKeyBytes = mtpData.keys.get(mtpData.currentDcId);
|
|
522
|
+
if (!authKeyBytes) {
|
|
523
|
+
throw new Error(`No auth key found for DC ${mtpData.currentDcId}`);
|
|
524
|
+
}
|
|
525
|
+
console.log("[TdataConverter] Pure JS implementation extracted:", {
|
|
526
|
+
userId: mtpData.userId,
|
|
527
|
+
dcId: mtpData.currentDcId,
|
|
528
|
+
authKeyLength: authKeyBytes.length
|
|
529
|
+
});
|
|
530
|
+
return {
|
|
531
|
+
userId: mtpData.userId,
|
|
532
|
+
dcId: mtpData.currentDcId,
|
|
533
|
+
authKey: authKeyBytes
|
|
534
|
+
};
|
|
535
|
+
} catch (error) {
|
|
536
|
+
if (error instanceof Error) {
|
|
537
|
+
throw error;
|
|
538
|
+
}
|
|
539
|
+
throw new Error("Failed to extract auth key from tdata");
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
function normalizeTdataPath(inputPath) {
|
|
543
|
+
if (inputPath.endsWith("tdata")) {
|
|
544
|
+
return inputPath;
|
|
545
|
+
}
|
|
546
|
+
const tdataSubdir = path.join(inputPath, "tdata");
|
|
547
|
+
if (fs2.existsSync(tdataSubdir) && fs2.statSync(tdataSubdir).isDirectory()) {
|
|
548
|
+
return tdataSubdir;
|
|
549
|
+
}
|
|
550
|
+
return inputPath;
|
|
551
|
+
}
|
|
552
|
+
async function convertTdataToStringSession(tdataPath) {
|
|
553
|
+
const normalizedPath = normalizeTdataPath(tdataPath);
|
|
554
|
+
console.log("[TdataConverter] Starting conversion from:", tdataPath);
|
|
555
|
+
console.log("[TdataConverter] Normalized path:", normalizedPath);
|
|
556
|
+
if (!fs2.existsSync(normalizedPath)) {
|
|
557
|
+
throw new Error(`tdata directory not found: ${normalizedPath}`);
|
|
558
|
+
}
|
|
559
|
+
try {
|
|
560
|
+
const { userId, dcId, authKey } = extractAuthKey(normalizedPath);
|
|
561
|
+
console.log("[TdataConverter] Creating GramJS StringSession...");
|
|
562
|
+
const mainAuthKey = new AuthKey();
|
|
563
|
+
await mainAuthKey.setKey(authKey);
|
|
564
|
+
const session = new StringSession("");
|
|
565
|
+
session.setDC(dcId, getServerAddress(dcId), 443);
|
|
566
|
+
session.setAuthKey(mainAuthKey);
|
|
567
|
+
const sessionString = session.save();
|
|
568
|
+
console.log("[TdataConverter] Conversion successful!");
|
|
569
|
+
return sessionString;
|
|
570
|
+
} catch (error) {
|
|
571
|
+
console.error("[TdataConverter] Conversion error:", error);
|
|
572
|
+
if (error instanceof Error) {
|
|
573
|
+
const errorMessage = error.message;
|
|
574
|
+
if (errorMessage.includes("No accounts found") || errorMessage.includes("logged into Telegram Desktop")) {
|
|
575
|
+
throw new Error(
|
|
576
|
+
"No active session found in Telegram Desktop.\n\nPlease make sure:\n\u2022 You're logged into Telegram Desktop\n\u2022 You've logged in at least once before\n\u2022 You're using the official Telegram Desktop from desktop.telegram.org"
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
throw new Error(
|
|
580
|
+
`Failed to read Telegram Desktop session: ${errorMessage}
|
|
581
|
+
|
|
582
|
+
Suggestions:
|
|
583
|
+
1. Make sure Telegram Desktop is closed
|
|
584
|
+
2. Verify you're using the official Telegram Desktop from desktop.telegram.org
|
|
585
|
+
3. Try using the QR code or phone number login method instead`
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
throw new Error(
|
|
589
|
+
"Failed to read Telegram Desktop session: Unknown error\n\nPlease try:\n1. Closing Telegram Desktop completely\n2. Using the QR code or phone number login method instead"
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
function validateTdataDirectory(tdataPath) {
|
|
594
|
+
try {
|
|
595
|
+
if (!fs2.existsSync(tdataPath)) {
|
|
596
|
+
return { valid: false, error: "Directory does not exist" };
|
|
597
|
+
}
|
|
598
|
+
if (!fs2.statSync(tdataPath).isDirectory()) {
|
|
599
|
+
return { valid: false, error: "Path is not a directory" };
|
|
600
|
+
}
|
|
601
|
+
const normalizedPath = normalizeTdataPath(tdataPath);
|
|
602
|
+
const keyDataPath = path.join(normalizedPath, "key_datas");
|
|
603
|
+
if (!fs2.existsSync(keyDataPath)) {
|
|
604
|
+
return {
|
|
605
|
+
valid: false,
|
|
606
|
+
error: `key_datas file missing.
|
|
607
|
+
|
|
608
|
+
Checked path: ${normalizedPath}
|
|
609
|
+
|
|
610
|
+
This usually means:
|
|
611
|
+
1. Telegram Desktop has never been logged in on this machine
|
|
612
|
+
2. You're using the macOS App Store version (not supported)
|
|
613
|
+
3. The tdata path is incorrect`
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
return { valid: true };
|
|
617
|
+
} catch (error) {
|
|
618
|
+
return {
|
|
619
|
+
valid: false,
|
|
620
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
function extractAccountInfo(tdataPath) {
|
|
625
|
+
try {
|
|
626
|
+
const normalizedPath = normalizeTdataPath(tdataPath);
|
|
627
|
+
const { userId, dcId } = extractAuthKey(normalizedPath);
|
|
628
|
+
return {
|
|
629
|
+
userId,
|
|
630
|
+
dcId
|
|
631
|
+
};
|
|
632
|
+
} catch (error) {
|
|
633
|
+
console.error("[TdataConverter] Failed to extract account info:", error);
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
export { convertTdataToStringSession, extractAccountInfo, validateTdataDirectory };
|
|
639
|
+
//# sourceMappingURL=tdata-converter.js.map
|
|
640
|
+
//# sourceMappingURL=tdata-converter.js.map
|