Spaces:
Running
Running
| /** | |
| * sessionManager.js | |
| * | |
| * Manages GramJS bot sessions stored in MongoDB. | |
| * - First launch: creates a new session using bot token (user-mode login), | |
| * stores the session string in MongoDB. | |
| * - Next launches: loads session string from MongoDB, validates it. | |
| * - If session is expired/invalid: re-creates and stores new session string. | |
| * | |
| * For bots: uses MTProto bot login (bot token auth) so no phone/code needed. | |
| */ | |
| require('dotenv').config(); | |
| const { TelegramClient } = require('telegram'); | |
| const { StringSession } = require('telegram/sessions'); | |
| const Session = require('../db/models/session'); | |
| const API_ID = Number(process.env.API_ID); | |
| const API_HASH = process.env.API_HASH; | |
| /** | |
| * Create a brand-new GramJS client authenticated as a BOT using bot token. | |
| * Returns { client, sessionString } | |
| */ | |
| async function createBotSession(botToken, label) { | |
| console.log(`[SessionManager] Creating new GramJS session for [${label}]...`); | |
| const client = new TelegramClient(new StringSession(''), API_ID, API_HASH, { | |
| connectionRetries: 5, | |
| requestRetries: 3, | |
| retryDelay: 2000, | |
| autoReconnect: true, | |
| useIPV6: false, | |
| }); | |
| client.setLogLevel('error'); | |
| await client.start({ | |
| botAuthToken: botToken, | |
| }); | |
| const sessionString = client.session.save(); | |
| console.log(`[SessionManager] β New session created for [${label}].`); | |
| return { client, sessionString }; | |
| } | |
| /** | |
| * Load a session string from MongoDB by key. | |
| * Returns the session string or null. | |
| */ | |
| async function loadSessionString(key) { | |
| try { | |
| const doc = await Session.findOne({ key }); | |
| return doc ? doc.value : null; | |
| } catch (err) { | |
| console.error(`[SessionManager] β Failed to load session [${key}]:`, err.message); | |
| return null; | |
| } | |
| } | |
| /** | |
| * Save a session string to MongoDB by key (upsert). | |
| */ | |
| async function saveSessionString(key, value) { | |
| try { | |
| await Session.findOneAndUpdate( | |
| { key }, | |
| { value }, | |
| { upsert: true, new: true } | |
| ); | |
| console.log(`[SessionManager] πΎ Session [${key}] saved to MongoDB.`); | |
| } catch (err) { | |
| console.error(`[SessionManager] β Failed to save session [${key}]:`, err.message); | |
| } | |
| } | |
| /** | |
| * Delete a session from MongoDB by key. | |
| */ | |
| async function deleteSessionString(key) { | |
| try { | |
| await Session.deleteOne({ key }); | |
| console.log(`[SessionManager] ποΈ Session [${key}] deleted from MongoDB.`); | |
| } catch (err) { | |
| console.error(`[SessionManager] β Failed to delete session [${key}]:`, err.message); | |
| } | |
| } | |
| /** | |
| * Validate a stored session by trying to connect and calling getMe(). | |
| * Returns true if valid, false otherwise. | |
| */ | |
| async function validateSession(sessionString) { | |
| let client; | |
| try { | |
| client = new TelegramClient(new StringSession(sessionString), API_ID, API_HASH, { | |
| connectionRetries: 3, | |
| requestRetries: 2, | |
| retryDelay: 1000, | |
| autoReconnect: false, | |
| useIPV6: false, | |
| }); | |
| client.setLogLevel('none'); | |
| await client.connect(); | |
| // Try a lightweight call to verify the session is authorized | |
| await client.getMe(); | |
| await client.disconnect(); | |
| return true; | |
| } catch (err) { | |
| if (client) { | |
| try { await client.disconnect(); } catch (_) {} | |
| } | |
| const msg = err.message || ''; | |
| // AUTH_KEY_UNREGISTERED, SESSION_EXPIRED, SESSION_REVOKED, AUTH_KEY_INVALID | |
| if ( | |
| msg.includes('AUTH_KEY') || | |
| msg.includes('SESSION_EXPIRED') || | |
| msg.includes('SESSION_REVOKED') || | |
| msg.includes('UNAUTHORIZED') || | |
| msg.includes('not authorized') | |
| ) { | |
| console.warn(`[SessionManager] β οΈ Session is invalid/expired: ${msg}`); | |
| return false; | |
| } | |
| // Network/timeout errors β don't invalidate | |
| console.warn(`[SessionManager] β οΈ Session validation network error (treating as valid): ${msg}`); | |
| return true; | |
| } | |
| } | |
| /** | |
| * Main entry point. | |
| * | |
| * Gets or creates a valid GramJS client for a bot. | |
| * | |
| * @param {string} dbKey - MongoDB key, e.g. 'bot_session_str' | |
| * @param {string} botToken - Telegram bot token | |
| * @param {string} label - Human-readable label for logs | |
| * @returns {TelegramClient} - Connected GramJS client | |
| */ | |
| async function getOrCreateBotClient(dbKey, botToken, label) { | |
| let sessionString = await loadSessionString(dbKey); | |
| if (sessionString) { | |
| console.log(`[SessionManager] π Found session [${dbKey}] in MongoDB. Validating...`); | |
| const isValid = await validateSession(sessionString); | |
| if (!isValid) { | |
| console.warn(`[SessionManager] β»οΈ Session [${dbKey}] invalid. Re-creating...`); | |
| await deleteSessionString(dbKey); | |
| sessionString = null; | |
| } else { | |
| console.log(`[SessionManager] β Session [${dbKey}] is valid. Using existing session.`); | |
| } | |
| } else { | |
| console.log(`[SessionManager] π No session found for [${dbKey}]. Creating new session...`); | |
| } | |
| if (!sessionString) { | |
| const { client: newClient, sessionString: newString } = await createBotSession(botToken, label); | |
| await saveSessionString(dbKey, newString); | |
| // Re-connect with the saved string for consistency | |
| await newClient.disconnect().catch(() => {}); | |
| sessionString = newString; | |
| } | |
| // Now connect the final client | |
| const client = new TelegramClient(new StringSession(sessionString), API_ID, API_HASH, { | |
| connectionRetries: 10, | |
| requestRetries: 5, | |
| retryDelay: 2000, | |
| autoReconnect: true, | |
| useIPV6: false, | |
| }); | |
| client.setLogLevel('error'); | |
| await client.connect(); | |
| console.log(`[SessionManager] π GramJS client [${label}] is connected.`); | |
| return client; | |
| } | |
| module.exports = { | |
| getOrCreateBotClient, | |
| loadSessionString, | |
| saveSessionString, | |
| deleteSessionString, | |
| validateSession, | |
| }; | |