const { Telegraf, Markup, session } = require('telegraf'); const fs = require('fs').promises; const path = require('path'); const axios = require('axios'); const { exec } = require('child_process'); const util = require('util'); const cron = require('node-cron'); const express = require('express'); require('dotenv').config(); const execAsync = util.promisify(exec); class TelegramOTPBot { constructor() { // Load config dari environment variables this.botToken = process.env.BOT_TOKEN; this.adminIds = process.env.ADMIN_IDS ? process.env.ADMIN_IDS.split(',').map(id => id.trim()) : []; this.adminUsernames = process.env.ADMIN_USERNAMES ? process.env.ADMIN_USERNAMES.split(',').map(u => u.trim()) : []; this.groupChatId = process.env.GROUP_CHAT_ID || ''; this.webEmail = process.env.WEB_EMAIL || ''; this.webPassword = process.env.WEB_PASSWORD || ''; this.sendToGroup = process.env.SEND_TO_GROUP === 'true' || process.env.SEND_TO_GROUP === '1'; this.apiBaseURL = 'https://x.mnitnetwork.com'; this.dataDir = path.join(__dirname, 'data'); this.rangesFile = path.join(this.dataDir, 'ranges.json'); this.usersFile = path.join(this.dataDir, 'users.json'); this.sessionFile = path.join(this.dataDir, 'session.json'); this.activeMonitors = new Map(); this.bot = null; this.waitingForRangeInput = new Map(); this.waitingForCustomRange = new Map(); this.waitingForBroadcast = new Map(); this.waitingForCheckNumber = new Map(); this.sharedSession = null; this.monitorIntervals = new Map(); this.lastCheckedIds = new Set(); this.globalMonitorInterval = null; this.consoleMonitorInterval = null; this.lastConsoleId = 0; this.consoleCheckCount = 0; this.checkInterval = process.env.CHECK_INTERVAL ? parseInt(process.env.CHECK_INTERVAL) : 5000; this.timeoutMinutes = process.env.TIMEOUT_MINUTES ? parseInt(process.env.TIMEOUT_MINUTES) : 30; this.isProcessing = new Set(); this.userRangeRequests = new Map(); this.userOTPHistory = new Map(); this.consoleMonitorEnabled = process.env.AUTO_START_CONSOLE_MONITOR === 'true' || process.env.AUTO_START_CONSOLE_MONITOR === '1'; this.globalMonitorEnabled = process.env.AUTO_START_GLOBAL_MONITOR === 'true' || process.env.AUTO_START_GLOBAL_MONITOR === '1'; this.isGroupChatIdValid = false; this.groupChatInfo = null; this.isBulkProcessing = false; this.bulkRequests = new Map(); this.lastGetNumMessages = new Map(); this.userButtonMessages = new Map(); this.consoleRangeCounts = new Map(); this.otpGroupSent = new Set(); this.consoleCheckInterval = 5000; this.enablePing = process.env.ENABLE_PING === 'true' || process.env.ENABLE_PING === '1'; this.requiredGroups = process.env.REQUIRED_GROUPS ? JSON.parse(process.env.REQUIRED_GROUPS) : []; // Express setup this.expressApp = null; this.server = null; this.expressPort = process.env.PORT || process.env.EXPRESS_PORT || 7860; this.isBotRunning = false; this.domain = process.env.DOMAIN || `https://fourstore-tele.hf.space`; this.authToken = process.env.AUTH_TOKEN || null; // Validasi config penting if (!this.botToken) { console.error('āŒ BOT_TOKEN is required in .env file'); process.exit(1); } } async initialize() { await this.ensureDataDir(); await this.loadData(); this.bot = new Telegraf(this.botToken); this.setupMiddlewares(); this.setupCommands(); this.setupCallbacks(); this.setupMessageHandler(); this.bot.catch((err, ctx) => { console.error(`[ERROR] ${err.message}`); }); await this.bot.launch(); console.log('āœ… Bot started successfully'); console.log(`šŸ¤– Bot username: @${(await this.bot.telegram.getMe()).username}`); this.isBotRunning = true; await this.checkAndValidateGroupChatId(); this.setupCronJobs(); await this.checkAndStartMonitoring(); // Start Express server this.startExpressServer(); } startExpressServer() { this.expressApp = express(); // Middleware untuk logging this.expressApp.use((req, res, next) => { console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`); next(); }); // Middleware untuk auth jika ada if (this.authToken) { this.expressApp.use((req, res, next) => { const token = req.headers['authorization'] || req.query.token; if (token === `Bearer ${this.authToken}` || token === this.authToken) { next(); } else { res.status(401).json({ error: 'Unauthorized' }); } }); } // Route untuk status bot this.expressApp.get('/', (req, res) => { const status = { bot: this.isBotRunning ? 'ONLINE' : 'OFFLINE', timestamp: new Date().toISOString(), session: this.sharedSession ? { username: this.sharedSession.username, balance: this.sharedSession.balance, expiry: this.sharedSession.expiry } : null, monitors: Array.from(this.activeMonitors.values()).reduce((sum, arr) => sum + arr.length, 0), users: Object.keys(this.users || {}).length, ranges: Object.keys(this.ranges || {}).length, expressPort: this.expressPort, domain: this.domain, environment: process.env.NODE_ENV || 'development' }; res.json(status); }); // Route untuk halaman HTML sederhana this.expressApp.get('/status', (req, res) => { const botStatus = this.isBotRunning ? '🟢 ONLINE' : 'šŸ”“ OFFLINE'; const sessionStatus = this.sharedSession ? '🟢 ACTIVE' : 'šŸ”“ NO SESSION'; const expiry = this.sharedSession ? new Date(this.sharedSession.expiry).toLocaleString() : 'N/A'; const balance = this.sharedSession ? this.sharedSession.balance : 'N/A'; const botUsername = this.bot ? this.bot.botInfo?.username : 'N/A'; const html = ` Telegram OTP Bot - ${this.domain}

šŸ¤– Telegram OTP Bot

Bot Status: ${botStatus}
Bot Username: @${botUsername}
Session Status: ${sessionStatus}
Account: ${this.sharedSession ? this.sharedSession.username : 'N/A'}
Balance: ${balance}
Session Expires: ${expiry}
${Object.keys(this.users || {}).length} Total Users
${Array.from(this.activeMonitors.values()).reduce((sum, arr) => sum + arr.length, 0)} Active Monitors
${Object.keys(this.ranges || {}).length} Available Ranges
${this.expressPort} API Port
šŸ¤– Bot Info:
@${botUsername}
${this.adminIds.length} Admin(s)
🌐 Domain: ${this.domain}
šŸš€ Environment: ${process.env.NODE_ENV || 'development'}
Last updated: ${new Date().toLocaleString()}
Auto-refresh every 10 seconds
`; res.send(html); }); // Route untuk health check this.expressApp.get('/health', (req, res) => { const healthStatus = { status: this.isBotRunning ? 'healthy' : 'unhealthy', bot: this.isBotRunning, timestamp: new Date().toISOString(), domain: this.domain, uptime: process.uptime() }; res.json(healthStatus); }); // Route untuk info lengkap (JSON) this.expressApp.get('/api/info', (req, res) => { const info = { bot: { running: this.isBotRunning, username: this.bot ? this.bot.botInfo?.username : 'N/A', id: this.bot ? this.bot.botInfo?.id : 'N/A' }, session: this.sharedSession ? { username: this.sharedSession.username, email: this.sharedSession.email, balance: this.sharedSession.balance, expiry: this.sharedSession.expiry, remaining_minutes: this.sharedSession.expiry ? Math.floor((new Date(this.sharedSession.expiry) - new Date()) / 1000 / 60) : 0 } : null, statistics: { total_users: Object.keys(this.users || {}).length, active_monitors: Array.from(this.activeMonitors.values()).reduce((sum, arr) => sum + arr.length, 0), total_ranges: Object.keys(this.ranges || {}).length, console_checks: this.consoleCheckCount, total_requests: Object.values(this.users || {}).reduce((sum, user) => sum + (user.requests || 0), 0), success_requests: Object.values(this.users || {}).reduce((sum, user) => sum + (user.success || 0), 0), failed_requests: Object.values(this.users || {}).reduce((sum, user) => sum + (user.failed || 0), 0) }, monitoring: { console_monitor: this.consoleMonitorInterval ? 'active' : 'inactive', global_monitor: this.globalMonitorInterval ? 'active' : 'inactive', check_interval: this.checkInterval, timeout_minutes: this.timeoutMinutes }, config: { send_to_group: this.sendToGroup, enable_ping: this.enablePing, required_groups_count: this.requiredGroups.length, admin_count: this.adminIds.length }, server: { express_port: this.expressPort, domain: this.domain, uptime: process.uptime(), memory_usage: process.memoryUsage(), environment: process.env.NODE_ENV || 'development' } }; res.json(info); }); // Route untuk webhook jika diperlukan this.expressApp.post('/webhook', express.json(), (req, res) => { console.log('Webhook received:', req.body); res.json({ received: true }); }); // Route untuk ping (jika enable_ping true) if (this.enablePing) { this.expressApp.get('/ping', (req, res) => { res.json({ pong: new Date().toISOString(), bot: this.isBotRunning, uptime: process.uptime() }); }); } // 404 handler this.expressApp.use((req, res) => { res.status(404).json({ error: 'Not Found', available_routes: ['/', '/status', '/health', '/api/info', '/webhook'].concat(this.enablePing ? ['/ping'] : []) }); }); // Error handler this.expressApp.use((err, req, res, next) => { console.error('Express error:', err); res.status(500).json({ error: 'Internal Server Error' }); }); // Start server this.server = this.expressApp.listen(this.expressPort, '0.0.0.0', () => { console.log(`āœ… Express server running on port ${this.expressPort}`); console.log(`šŸ“Š Status page: ${this.domain}/status`); console.log(`šŸ”§ API endpoint: ${this.domain}/api/info`); console.log(`ā¤ļø Health check: ${this.domain}/health`); if (this.enablePing) { console.log(`šŸ“ Ping endpoint: ${this.domain}/ping`); } console.log(`🌐 Environment: ${process.env.NODE_ENV || 'development'}`); }); // Handle server errors this.server.on('error', (error) => { console.error('Express server error:', error); }); } setupCronJobs() { cron.schedule('0 0 * * *', async () => { console.log('Running auto session refresh (cron)...'); await this.autoRunTestJS(); }); cron.schedule('*/15 * * * *', async () => { console.log('Checking session status...'); if (!this.sharedSession) { console.log('No session found, running test.js...'); await this.autoRunTestJS(); } else { const expiry = new Date(this.sharedSession.expiry); const now = new Date(); if (expiry <= now) { console.log('Session expired, running test.js...'); await this.autoRunTestJS(); } } }); cron.schedule('0 */1 * * *', async () => { console.log('Clearing user group check cache...'); }); console.log('Cron jobs scheduled'); } async ensureDataDir() { try { await fs.mkdir(this.dataDir, { recursive: true }); } catch (error) {} } async loadData() { try { this.ranges = await this.loadJSON(this.rangesFile) || {}; this.users = await this.loadJSON(this.usersFile) || {}; const sessionData = await this.loadJSON(this.sessionFile); if (sessionData) { this.sharedSession = sessionData; console.log('āœ… Session loaded'); } else { console.log('āš ļø No session data found'); } } catch (error) { console.log('Error loading data:', error.message); this.ranges = {}; this.users = {}; } } async loadJSON(file) { try { const data = await fs.readFile(file, 'utf8'); return JSON.parse(data); } catch { return null; } } async saveJSON(file, data) { try { await fs.writeFile(file, JSON.stringify(data, null, 2)); } catch (error) {} } setupMiddlewares() { this.bot.use(session()); this.bot.use(async (ctx, next) => { ctx.isAdmin = this.adminIds.includes(ctx.from.id.toString()); await next(); }); } setupCommands() { this.bot.start(async (ctx) => { try { if (ctx.chat.type !== 'private') return; await this.showMainMenu(ctx); } catch (error) { console.error('Start command error:', error); } }); this.bot.command('admin', async (ctx) => { try { if (ctx.chat.type !== 'private') return; if (!ctx.isAdmin) { await ctx.reply('āŒ Admin only'); return; } const keyboard = Markup.inlineKeyboard([ [Markup.button.callback('āž• Add Range', 'add_range'), Markup.button.callback('šŸ“‹ List Ranges', 'list_ranges')], [Markup.button.callback('šŸ‘„ Users', 'show_users'), Markup.button.callback('šŸ“Š Stats', 'show_stats')], [Markup.button.callback('šŸ“¢ Broadcast', 'broadcast'), Markup.button.callback('šŸ” Session Info', 'session_info')], [Markup.button.callback('šŸ”„ Run test.js', 'run_test_js'), Markup.button.callback('šŸ”„ Reload', 'reload_session')], [Markup.button.callback('šŸ“± Console Monitor', 'toggle_console_monitor'), Markup.button.callback('āš™ļø Settings', 'admin_settings')], [Markup.button.callback('šŸ‘„ Check Group ID', 'check_group_id')], [Markup.button.callback('ā„¹ļø Group Info', 'group_info')] ]); await ctx.reply('šŸ› ļø *Admin Panel*', { parse_mode: 'Markdown', ...keyboard }); } catch (error) {} }); this.bot.command('getid', async (ctx) => { try { const chatId = ctx.chat.id; const chatType = ctx.chat.type; const chatTitle = ctx.chat.title || ctx.chat.first_name || 'Private Chat'; await ctx.reply( `šŸ’¬ *Chat Information*\n\nšŸ“ Chat ID: \`${chatId}\`\nšŸ“Š Type: ${chatType}\nšŸ·ļø Title: ${chatTitle}`, { parse_mode: 'Markdown' } ); if (chatType === 'group' || chatType === 'supergroup') { this.groupChatId = chatId.toString(); await ctx.reply( `āœ… *Group Chat ID Updated!*\n\nšŸ†” New Group ID: \`${chatId}\`\nšŸ·ļø Title: ${chatTitle}`, { parse_mode: 'Markdown' } ); await this.checkAndValidateGroupChatId(); } } catch (error) { await ctx.reply(`āŒ Error: ${error.message}`); } }); this.bot.command('get', async (ctx) => { try { if (ctx.chat.type !== 'private') return; await this.showRangeSelection(ctx); } catch (error) {} }); this.bot.command('bulk', async (ctx) => { try { if (ctx.chat.type !== 'private') return; await this.startBulkRequest(ctx); } catch (error) {} }); this.bot.command('status', async (ctx) => { try { if (ctx.chat.type !== 'private') return; await this.showServiceStatus(ctx); } catch (error) {} }); this.bot.command('check', async (ctx) => { try { if (ctx.chat.type !== 'private') return; await this.startCheckNumber(ctx); } catch (error) {} }); this.bot.command('cancel', async (ctx) => { try { if (ctx.chat.type !== 'private') return; await this.cancelAllMonitors(ctx); } catch (error) {} }); this.bot.command('help', async (ctx) => { try { if (ctx.chat.type !== 'private') return; const helpMsg = `šŸ“š *Help Menu*\n\n` + `/start - Start bot\n` + `/get - Get single number\n` + `/bulk - Get multiple numbers\n` + `/status - Check status\n` + `/check - Check number status\n` + `/cancel - Cancel all monitors\n` + `/help - Show help\n` + `${ctx.isAdmin ? '/admin - Admin panel\n' : ''}` + `\nšŸ“ž Contact: ${this.getAdminContact()}`; await ctx.reply(helpMsg, { parse_mode: 'Markdown' }); } catch (error) {} }); this.bot.command('run_test', async (ctx) => { try { if (ctx.chat.type !== 'private') return; if (!ctx.isAdmin) { await ctx.reply('āŒ Admin only'); return; } await this.runTestJS(ctx); } catch (error) {} }); this.bot.command('reload', async (ctx) => { try { if (ctx.chat.type !== 'private') return; if (!ctx.isAdmin) { await ctx.reply('āŒ Admin only'); return; } await this.reloadSession(ctx); } catch (error) {} }); this.bot.command('checkgroup', async (ctx) => { try { if (ctx.chat.type !== 'private') return; if (!ctx.isAdmin) { await ctx.reply('āŒ Admin only'); return; } await this.checkGroupIdCommand(ctx); } catch (error) {} }); } setupCallbacks() { this.bot.action('check_group_id', async (ctx) => { try { if (!ctx.isAdmin) { await ctx.answerCbQuery('āŒ Admin only'); return; } await ctx.answerCbQuery('Checking group...'); await this.checkGroupIdCommand(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('group_info', async (ctx) => { try { if (!ctx.isAdmin) { await ctx.answerCbQuery('āŒ Admin only'); return; } await ctx.answerCbQuery('Getting group info...'); await this.showGroupInfo(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('check_console_now', async (ctx) => { try { if (!ctx.isAdmin) { await ctx.answerCbQuery('āŒ Admin only'); return; } await ctx.answerCbQuery('Checking console...'); const result = await this.checkConsoleData(); if (result) { await ctx.reply( `šŸ“Š *Console Check Result*\n\nšŸ“ Total Logs: ${result.totalLogs}\nšŸ“± WhatsApp Logs: ${result.whatsappCount}\nšŸ“˜ Facebook Logs: ${result.facebookCount}\nšŸ†• New Facebook Logs: ${result.newFacebookCount}\nšŸ†• New WhatsApp Logs: ${result.newWhatsappCount}`, { parse_mode: 'Markdown' } ); } else { await ctx.reply('āŒ Console check failed'); } } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action(/^range_(.+)$/, async (ctx) => { try { await this.selectRangeHandler(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('custom_range', async (ctx) => { try { await this.startCustomRange(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('check_number', async (ctx) => { try { await this.startCheckNumber(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('cancel_monitor', async (ctx) => { try { await this.cancelAllMonitorsFromButton(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('add_range', async (ctx) => { try { if (!ctx.isAdmin) { await ctx.answerCbQuery('āŒ Admin only'); return; } await this.startAddRange(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('list_ranges', async (ctx) => { try { if (!ctx.isAdmin) { await ctx.answerCbQuery('āŒ Admin only'); return; } await this.listRangesHandler(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('show_users', async (ctx) => { try { if (!ctx.isAdmin) { await ctx.answerCbQuery('āŒ Admin only'); return; } await this.showUsersHandler(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('show_stats', async (ctx) => { try { if (!ctx.isAdmin) { await ctx.answerCbQuery('āŒ Admin only'); return; } await this.showStatsHandler(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('broadcast', async (ctx) => { try { if (!ctx.isAdmin) { await ctx.answerCbQuery('āŒ Admin only'); return; } await this.startBroadcast(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('session_info', async (ctx) => { try { if (!ctx.isAdmin) { await ctx.answerCbQuery('āŒ Admin only'); return; } await this.showSessionInfo(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('run_test_js', async (ctx) => { try { if (!ctx.isAdmin) { await ctx.answerCbQuery('āŒ Admin only'); return; } await this.runTestJS(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('reload_session', async (ctx) => { try { if (!ctx.isAdmin) { await ctx.answerCbQuery('āŒ Admin only'); return; } await this.reloadSession(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('toggle_console_monitor', async (ctx) => { try { if (!ctx.isAdmin) { await ctx.answerCbQuery('āŒ Admin only'); return; } await this.toggleConsoleMonitor(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('admin_settings', async (ctx) => { try { if (!ctx.isAdmin) { await ctx.answerCbQuery('āŒ Admin only'); return; } await this.showAdminSettings(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action(/^delete_range_(.+)$/, async (ctx) => { try { if (!ctx.isAdmin) return; await this.deleteRangeHandler(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action(/^toggle_range_(.+)$/, async (ctx) => { try { if (!ctx.isAdmin) return; await this.toggleRangeHandler(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action(/^change_num_(\d+)_(.+)$/, async (ctx) => { try { await this.handleChangeNumber(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action(/^bulk_range_(.+)$/, async (ctx) => { try { await this.startBulkFromRange(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('change_num_1', async (ctx) => { try { await this.handleChangeSingleNum(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); this.bot.action('change_num_3', async (ctx) => { try { await this.handleChangeThreeNums(ctx); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } }); } async showMainMenu(ctx) { const userId = ctx.from.id.toString(); if (!this.users[userId]) { this.users[userId] = { id: userId, username: ctx.from.username || ctx.from.first_name, joinDate: new Date().toISOString(), requests: 0, success: 0, failed: 0, blocked: false }; await this.saveJSON(this.usersFile, this.users); } const keyboard = Markup.keyboard([ ['šŸ“± Get Number', 'šŸ“¦ Get Bulk'], ['šŸ“Š Status', 'šŸ” Check Number'], ['āŒ Cancel Monitor', 'šŸ‘Øā€šŸ’» Contact Admin'] ]).resize(); if (!this.sharedSession) { await ctx.reply( `šŸ“› *No Active Session*\n\nBot sedang mencoba refresh session otomatis...\n\nContact: ${this.getAdminContact()}`, { parse_mode: 'Markdown', ...keyboard } ); await this.autoRunTestJS(); return; } const expiry = new Date(this.sharedSession.expiry); const now = new Date(); if (expiry <= now) { await ctx.reply( `šŸ“› *Session Expired*\n\nBot sedang refresh session otomatis...\n\nContact: ${this.getAdminContact()}`, { parse_mode: 'Markdown', ...keyboard } ); await this.autoRunTestJS(); return; } await ctx.reply( `šŸ‘‹ *Welcome ${ctx.from.first_name}!*\n\nšŸ“Š *Account:* ${this.sharedSession.username}\nšŸ’° *Balance:* ${this.sharedSession.balance}\nā° *Expires:* ${new Date(this.sharedSession.expiry).toLocaleString()}\n\nšŸš€ *Ready to get OTP numbers!*`, { parse_mode: 'Markdown', ...keyboard } ); } async checkAndValidateGroupChatId() { if (!this.groupChatId) { this.isGroupChatIdValid = false; return; } try { const chat = await this.bot.telegram.getChat(this.groupChatId); this.groupChatInfo = chat; this.isGroupChatIdValid = true; } catch (error) { this.isGroupChatIdValid = false; } return this.isGroupChatIdValid; } async checkAndStartMonitoring() { if (this.sharedSession) { const expiry = new Date(this.sharedSession.expiry); const now = new Date(); if (expiry > now) { this.startAutoMonitors(); } else { await this.autoRunTestJS(); } } else { await this.autoRunTestJS(); } } startAutoMonitors() { if (this.globalMonitorEnabled) { this.startGlobalMonitoring(); } if (this.consoleMonitorEnabled) { this.startConsoleMonitoring(); } } async autoRunTestJS() { try { const testJsPath = path.join(__dirname, 'test.js'); try { await fs.access(testJsPath); } catch (error) { return; } for (const adminId of this.adminIds) { try { await this.bot.telegram.sendMessage( adminId, `šŸ”„ *Auto Session Refresh*\n\nRunning test.js...`, { parse_mode: 'Markdown' } ); } catch (error) {} } await execAsync('node test.js', { cwd: __dirname }); await this.loadData(); if (this.sharedSession) { if (this.globalMonitorInterval) { clearInterval(this.globalMonitorInterval); this.globalMonitorInterval = null; } if (this.consoleMonitorInterval) { clearInterval(this.consoleMonitorInterval); this.consoleMonitorInterval = null; } this.startAutoMonitors(); for (const adminId of this.adminIds) { try { await this.bot.telegram.sendMessage( adminId, `āœ… *Session Auto-Refreshed*\n\nAccount: ${this.sharedSession.username}\nBalance: ${this.sharedSession.balance}\nExpires: ${new Date(this.sharedSession.expiry).toLocaleString()}`, { parse_mode: 'Markdown' } ); } catch (error) {} } } } catch (error) {} } async runTestJS(ctx) { try { const loadingMsg = await ctx.reply('šŸ”„ Running test.js...'); const testJsPath = path.join(__dirname, 'test.js'); try { await fs.access(testJsPath); } catch (error) { await ctx.telegram.editMessageText( ctx.chat.id, loadingMsg.message_id, null, 'āŒ test.js not found' ); return; } await execAsync('node test.js', { cwd: __dirname }); await this.loadData(); if (this.sharedSession) { await ctx.telegram.editMessageText( ctx.chat.id, loadingMsg.message_id, null, `āœ… *test.js executed!*\n\nšŸ‘¤ Account: ${this.sharedSession.username}\nšŸ’° Balance: ${this.sharedSession.balance}\nā° Expires: ${new Date(this.sharedSession.expiry).toLocaleString()}`, { parse_mode: 'Markdown' } ); if (this.globalMonitorInterval) { clearInterval(this.globalMonitorInterval); this.globalMonitorInterval = null; } if (this.consoleMonitorInterval) { clearInterval(this.consoleMonitorInterval); this.consoleMonitorInterval = null; } this.startAutoMonitors(); } else { await ctx.telegram.editMessageText( ctx.chat.id, loadingMsg.message_id, null, 'āŒ No session saved' ); } } catch (error) { await ctx.reply(`āŒ Error: ${error.message}`); } } setupMessageHandler() { this.bot.on('text', async (ctx) => { try { if (ctx.chat.type !== 'private') return; const userId = ctx.from.id.toString(); const text = ctx.message.text.trim(); if (this.waitingForRangeInput.has(userId)) { this.waitingForRangeInput.delete(userId); await this.processRangeInput(ctx, text); return; } if (this.waitingForCustomRange.has(userId)) { this.waitingForCustomRange.delete(userId); await this.processCustomRange(ctx, text); return; } if (this.waitingForCheckNumber.has(userId)) { this.waitingForCheckNumber.delete(userId); await this.processCheckNumber(ctx, text); return; } if (this.waitingForBroadcast.has(userId)) { this.waitingForBroadcast.delete(userId); await this.processBroadcast(ctx, text); return; } if (text === 'šŸ“± Get Number') { await this.showRangeSelection(ctx); } else if (text === 'šŸ“¦ Get Bulk') { await this.startBulkRequest(ctx); } else if (text === 'šŸ“Š Status') { await this.showServiceStatus(ctx); } else if (text === 'šŸ” Check Number') { await this.startCheckNumber(ctx); } else if (text === 'āŒ Cancel Monitor') { await this.cancelAllMonitors(ctx); } else if (text === 'šŸ‘Øā€šŸ’» Contact Admin') { await ctx.reply(`šŸ“ž *Contact Admin:*\n${this.getAdminContact()}`, { parse_mode: 'Markdown' }); } else if (/^\+?\d{10,15}$/.test(text.replace(/\s/g, ''))) { await this.processCheckNumber(ctx, text); } else if (/^\d{10,15}$/.test(text.replace(/[Xx]/g, '0')) && text.includes('X')) { await this.processDirectRange(ctx, text); } } catch (error) { console.error('Message handler error:', error); } }); this.bot.on('message', async (ctx) => { try { if (ctx.message.text && ctx.message.text.includes('/start console_')) { const match = ctx.message.text.match(/\/start console_(.+)/); if (match) { await this.handleStartWithConsoleRange(ctx); } } } catch (error) {} }); } async handleStartWithConsoleRange(ctx) { try { const userId = ctx.from.id.toString(); const text = ctx.message.text; const match = text.match(/\/start console_(.+)/); if (!match) return; const range = match[1].trim().toUpperCase(); if (!range.includes('X')) { await ctx.reply('āŒ Range must contain X'); return; } const xCount = (range.match(/X/g) || []).length; if (xCount < 3 || xCount > 4) { await ctx.reply(`āŒ Range must have 3-4 X (currently: ${xCount}X)`); return; } if (range.length < 10) { await ctx.reply('āŒ Range too short (min 10 characters)'); return; } if (!this.sharedSession) { await ctx.reply('āŒ No active session'); return; } const expiry = new Date(this.sharedSession.expiry); const now = new Date(); if (expiry <= now) { await ctx.reply('āŒ Session expired'); return; } await ctx.reply( `šŸ”„ *Getting Number from Console Range*\n\nšŸ“ž Range: \`${range}\`\nā³ Processing...`, { parse_mode: 'Markdown' } ); const result = await this.requestNumber(ctx, { name: `Console Live (${range})`, country: 'Auto', service: 'Auto', range: range }); if (result.success) { const message = await ctx.reply( `āœ… *Number Allocated!*\n\nšŸ“ž Range: \`${range}\`\nšŸ“± Number: ${this.formatCopyableNumber(result.number)}\nšŸŒ Country: ${result.country}\nšŸ“Š Status: ${result.status}\n\nšŸ” Auto Monitoring Started!`, { parse_mode: 'Markdown' } ); this.lastGetNumMessages.set(userId, { messageId: message.message_id, range: range, chatId: ctx.chat.id }); const keyboard = Markup.inlineKeyboard([ [Markup.button.callback('šŸ”„ Change Num', 'change_num_1')], [Markup.button.callback('šŸ”„ Change Num 3', 'change_num_3')], [Markup.button.callback('āŒ Cancel', 'cancel_monitor')] ]); const buttonMessage = await ctx.reply('Change number options:', { ...keyboard }); this.userButtonMessages.set(userId, buttonMessage.message_id); } else { await ctx.reply(`āŒ Failed: ${result.error || 'Unknown error'}`); } } catch (error) { await ctx.reply('āŒ Error processing console range'); } } async checkGroupIdCommand(ctx) { try { await ctx.reply('šŸ” Checking group chat ID...'); const isValid = await this.checkAndValidateGroupChatId(); if (isValid && this.groupChatInfo) { const chat = this.groupChatInfo; let botStatus = 'Unknown'; try { const member = await this.bot.telegram.getChatMember(chat.id, (await this.bot.telegram.getMe()).id); botStatus = member.status; } catch (error) { botStatus = `Error: ${error.message}`; } await ctx.reply( `āœ… *Group Chat ID Valid!*\n\nšŸ’¬ *Group Information:*\nšŸ·ļø Title: ${chat.title || 'N/A'}\nšŸ“Š Type: ${chat.type}\nšŸ”— Username: ${chat.username || 'N/A'}\nšŸ†” ID: \`${chat.id}\`\n\nšŸ¤– *Bot Status:*\nšŸ“Š Status: ${botStatus}`, { parse_mode: 'Markdown' } ); } else { await ctx.reply( `āŒ *Group Chat ID Invalid!*\n\nāš™ļø Current Group Chat ID: \`${this.groupChatId || 'Not set'}\``, { parse_mode: 'Markdown' } ); } } catch (error) { await ctx.reply(`āŒ Error checking group: ${error.message}`); } } async showGroupInfo(ctx) { try { if (!this.groupChatId || !this.isGroupChatIdValid) { await ctx.reply('āŒ No valid group chat ID configured'); return; } const chat = this.groupChatInfo; if (!chat) { await ctx.reply('āŒ Group info not available'); return; } let botStatus = 'Unknown'; let canSendMessages = 'Unknown'; let canSendMedia = 'Unknown'; try { const member = await this.bot.telegram.getChatMember(chat.id, (await this.bot.telegram.getMe()).id); botStatus = member.status; const chatInfo = await this.bot.telegram.getChat(chat.id); if (chatInfo.permissions) { canSendMessages = chatInfo.permissions.can_send_messages ? 'āœ… Yes' : 'āŒ No'; canSendMedia = chatInfo.permissions.can_send_media_messages ? 'āœ… Yes' : 'āŒ No'; } } catch (error) { botStatus = `āŒ Error: ${error.message}`; } await ctx.reply( `šŸ’¬ *Group Details*\n\nšŸ“‹ *Basic Info:*\nšŸ·ļø Title: ${chat.title || 'N/A'}\nšŸ“Š Type: ${chat.type}\nšŸ”— Username: ${chat.username || 'N/A'}\nšŸ†” ID: \`${chat.id}\`\n\nšŸ¤– *Bot Permissions:*\nšŸ“Š Status: ${botStatus}\nšŸ“¤ Can Send Messages: ${canSendMessages}\nšŸ“Ž Can Send Media: ${canSendMedia}\n\nšŸ“Š *Console Stats:*\nšŸ”¢ Console Checks: ${this.consoleCheckCount}\nšŸ†” Last Console ID: ${this.lastConsoleId}`, { parse_mode: 'Markdown' } ); } catch (error) { await ctx.reply(`āŒ Error getting group info: ${error.message}`); } } getAdminContact() { if (this.adminUsernames.length > 0) { return this.adminUsernames.map(u => `@${u}`).join('\n'); } else if (this.adminIds.length > 0) { return this.adminIds.map(id => `ID: ${id}`).join('\n'); } return 'No admin contact configured'; } startConsoleMonitoring() { if (this.consoleMonitorInterval) { clearInterval(this.consoleMonitorInterval); } if (!this.sharedSession) { return; } this.consoleMonitorInterval = setInterval(async () => { try { this.consoleCheckCount++; if (!this.sharedSession) { return; } const expiry = new Date(this.sharedSession.expiry); if (expiry <= new Date()) { clearInterval(this.consoleMonitorInterval); return; } const result = await this.checkConsoleData(); } catch (error) {} }, this.consoleCheckInterval); setTimeout(() => { this.checkConsoleData(); }, 3000); } async checkConsoleData() { try { if (!this.sharedSession || !this.sharedSession.token) { return null; } const headers = { 'Host': 'x.mnitnetwork.com', 'sec-ch-ua-platform': '"Android"', 'User-Agent': 'Mozilla/5.0 (Linux; Android 14; CPH2641 Build/UP1A.231005.007) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.7499.192 Mobile Safari/537.36', 'Accept': 'application/json, text/plain, */*', 'mauthtoken': this.sharedSession.token, 'sec-ch-ua': '"Android WebView";v="143", "Chromium";v="143", "Not A(Brand";v="24"', 'sec-ch-ua-mobile': '?1', 'x-requested-with': 'mark.via.gp', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://x.mnitnetwork.com/mdashboard/console', 'accept-language': 'id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7', 'Cookie': this.sharedSession.cookie || this.sharedSession.token, 'priority': 'u=1, i' }; const response = await axios.get( `${this.apiBaseURL}/mapi/v1/mdashboard/console/info`, { headers: headers, timeout: 15000, validateStatus: function (status) { return status >= 200 && status < 500; } } ); if (response.data && response.data.meta && response.data.meta.code === 200) { const logs = response.data.data.logs || []; const facebookLogs = logs.filter(log => { const appName = (log.app_name || '').toString().toLowerCase(); return appName.includes('facebook'); }); const whatsappLogs = logs.filter(log => { const appName = (log.app_name || '').toString().toLowerCase(); return appName.includes('whatsapp'); }); let newFacebookCount = 0; let newWhatsappCount = 0; for (const log of facebookLogs) { const logId = parseInt(log.id) || 0; if (logId > this.lastConsoleId) { newFacebookCount++; this.lastConsoleId = logId; if (this.groupChatId && this.sendToGroup) { await this.sendConsoleToGroup(log); } } } for (const log of whatsappLogs) { const logId = parseInt(log.id) || 0; if (logId > this.lastConsoleId) { newWhatsappCount++; this.lastConsoleId = logId; if (this.groupChatId && this.sendToGroup) { await this.sendConsoleToGroup(log); } } } if (logs.length > 0) { const ids = logs.map(log => parseInt(log.id) || 0).filter(id => id > 0); if (ids.length > 0) { const maxId = Math.max(...ids); if (maxId > this.lastConsoleId) { this.lastConsoleId = maxId; } } } return { totalLogs: logs.length, whatsappCount: whatsappLogs.length, facebookCount: facebookLogs.length, newFacebookCount: newFacebookCount, newWhatsappCount: newWhatsappCount, lastConsoleId: this.lastConsoleId }; } else { return null; } } catch (error) { return null; } } async sendConsoleToGroup(log) { try { if (!this.groupChatId || !this.sendToGroup) { return; } const fullNumber = log.number; let displayRange = log.range; const appName = (log.app_name || '').toString().toLowerCase(); if (!displayRange.includes('X')) { const numberLength = fullNumber.length; const baseLength = displayRange.length; const xCount = numberLength - baseLength; if (xCount > 0 && xCount <= 4) { displayRange = displayRange + 'X'.repeat(xCount); } else { displayRange = displayRange + 'XXX'; } } const xCount = (displayRange.match(/X/g) || []).length; const otp = this.extractOTPFromConsole(log.sms); const count = this.consoleRangeCounts.get(displayRange) || 0; this.consoleRangeCounts.set(displayRange, count + 1); const copyableOTP = otp ? this.formatCopyableNumber(otp) : 'N/A'; let message = `šŸ“± *Live Message New Range* (${count + 1}Ɨ)\n\nšŸ“ž Range: \`${displayRange}\`\nšŸŒ Country: ${log.country}\nšŸ“± Service: ${log.app_name}\n\nšŸ“ Message:\n\`\`\`\n${log.sms}\n\`\`\`\n`; if (otp) { message += `šŸ”¢ OTP: ${copyableOTP}\n\n`; } const isValidRange = displayRange.includes('X') && displayRange.length >= 10 && xCount >= 3 && xCount <= 4; let keyboard = null; if (isValidRange) { const botUsername = (await this.bot.telegram.getMe()).username; keyboard = Markup.inlineKeyboard([ [Markup.button.url(`šŸ“± Get Number`, `https://t.me/${botUsername}?start=console_${displayRange}`)] ]); } try { await this.bot.telegram.sendMessage( this.groupChatId, message, { parse_mode: 'Markdown', ...(keyboard ? keyboard : {}) } ); } catch (sendError) {} } catch (error) {} } async processDirectRange(ctx, text) { const userId = ctx.from.id.toString(); if (!this.sharedSession) { await ctx.reply('āŒ No active session. Trying to refresh...'); await this.autoRunTestJS(); return; } const expiry = new Date(this.sharedSession.expiry); const now = new Date(); if (expiry <= now) { await ctx.reply('āŒ Session expired. Trying to refresh...'); await this.autoRunTestJS(); return; } let range = text.trim().toUpperCase(); if (!/^[0-9X]+$/.test(range)) { await ctx.reply('āŒ Only numbers and X allowed'); return; } if (!range.includes('X')) { await ctx.reply('āŒ Range must contain X'); return; } if (range.length < 10) { await ctx.reply('āŒ Too short (min 10 characters)'); return; } const xCount = (range.match(/X/g) || []).length; if (xCount > 4) { await ctx.reply('āŒ Max 4 X allowed'); return; } if (xCount < 2) { await ctx.reply('āŒ Min 2 X required'); return; } await ctx.reply(`šŸ”„ Getting number from range: \`${range}\`...`, { parse_mode: 'Markdown' }); await this.getSingleNumber(ctx, range); } async getSingleNumber(ctx, range) { const userId = ctx.from.id.toString(); try { const result = await this.requestNumber(ctx, { name: `Custom (${(range.match(/X/g) || []).length}X)`, country: 'Auto', service: 'Auto', range: range }); if (result.success) { const copyableNumber = this.formatCopyableNumber(result.number); const message = await ctx.reply( `āœ… *Number Allocated!*\n\nšŸ“ž Range: \`${range}\`\nšŸ“± Number: ${copyableNumber}\nšŸŒ Country: ${result.country}\nšŸ“Š Status: ${result.status}\n\nšŸ” Auto Monitoring Started!\nā° Timeout: ${this.timeoutMinutes} minutes`, { parse_mode: 'Markdown' } ); this.lastGetNumMessages.set(userId, { messageId: message.message_id, range: range, chatId: ctx.chat.id }); const keyboard = Markup.inlineKeyboard([ [Markup.button.callback('šŸ”„ Change Num', 'change_num_1')], [Markup.button.callback('šŸ”„ Change Num 3', 'change_num_3')], [Markup.button.callback('āŒ Cancel', 'cancel_monitor')] ]); const buttonMessage = await ctx.reply('Change number options:', { ...keyboard }); this.userButtonMessages.set(userId, buttonMessage.message_id); } else { await ctx.reply(`āŒ Failed: ${result.error || 'Unknown error'}`); } } catch (error) { await ctx.reply('āŒ Error getting number'); } } formatCopyableNumber(phoneNumber) { const cleanNumber = phoneNumber.replace(/\D/g, ''); return `\`${cleanNumber}\``; } formatSensoredNumber(phoneNumber) { if (!phoneNumber || phoneNumber.length < 7) return phoneNumber; const cleanNumber = phoneNumber.replace(/\D/g, ''); if (cleanNumber.length >= 8) { const firstPart = cleanNumber.substring(0, 5); const lastPart = cleanNumber.substring(cleanNumber.length - 3); return `\`${firstPart}***${lastPart}\``; } else { return `\`${cleanNumber}\``; } } async handleChangeSingleNum(ctx) { const userId = ctx.from.id.toString(); await ctx.answerCbQuery('Changing number...'); const lastMessage = this.lastGetNumMessages.get(userId); if (!lastMessage) { await ctx.reply('āŒ No recent get number message found'); return; } const { range, chatId, messageId } = lastMessage; try { try { await ctx.deleteMessage(messageId); const buttonMessageId = this.userButtonMessages.get(userId); if (buttonMessageId) { try { await ctx.deleteMessage(buttonMessageId); this.userButtonMessages.delete(userId); } catch (buttonError) {} } try { await ctx.deleteMessage(); } catch (error) {} } catch (error) {} const result = await this.requestNumber(ctx, { name: `Custom (${(range.match(/X/g) || []).length}X)`, country: 'Auto', service: 'Auto', range: range }); if (result.success) { const copyableNumber = this.formatCopyableNumber(result.number); const newMessage = await ctx.reply( `šŸ”„ *Number Changed!*\n\nšŸ“ž Range: \`${range}\`\nšŸ“± Number: ${copyableNumber}\nšŸŒ Country: ${result.country}\nšŸ“Š Status: ${result.status}\n\nšŸ” Auto Monitoring Started!`, { parse_mode: 'Markdown' } ); this.lastGetNumMessages.set(userId, { messageId: newMessage.message_id, range: range, chatId: ctx.chat.id }); const keyboard = Markup.inlineKeyboard([ [Markup.button.callback('šŸ”„ Change Num', 'change_num_1')], [Markup.button.callback('šŸ”„ Change Num 3', 'change_num_3')], [Markup.button.callback('āŒ Cancel', 'cancel_monitor')] ]); const buttonMessage = await ctx.reply('Change number options:', { ...keyboard }); this.userButtonMessages.set(userId, buttonMessage.message_id); } else { await ctx.reply(`āŒ Failed to change number: ${result.error}`); } } catch (error) { await ctx.reply('āŒ Error changing number'); } } async handleChangeThreeNums(ctx) { const userId = ctx.from.id.toString(); await ctx.answerCbQuery('Changing to 3 numbers...'); const lastMessage = this.lastGetNumMessages.get(userId); if (!lastMessage) { await ctx.reply('āŒ No recent get number message found'); return; } const { range, chatId, messageId } = lastMessage; try { try { await ctx.deleteMessage(messageId); const buttonMessageId = this.userButtonMessages.get(userId); if (buttonMessageId) { try { await ctx.deleteMessage(buttonMessageId); this.userButtonMessages.delete(userId); } catch (buttonError) {} } try { await ctx.deleteMessage(); } catch (error) {} } catch (error) {} const results = []; for (let i = 0; i < 3; i++) { try { const result = await this.requestNumber(ctx, { name: `Custom (${(range.match(/X/g) || []).length}X)`, country: 'Auto', service: 'Auto', range: range }); results.push(result); if (i < 2) { await new Promise(resolve => setTimeout(resolve, 1000)); } } catch (error) {} } const successCount = results.filter(r => r.success).length; if (successCount > 0) { let message = `šŸ“¦ *3 Numbers Allocated!*\n\nšŸ“ž Range: \`${range}\`\nšŸ“Š Status: success\n\n`; results.forEach((result, index) => { if (result.success) { const copyableNumber = this.formatCopyableNumber(result.number); message += `Number ${index + 1}:\n${copyableNumber}\n`; } }); message += `\nšŸŒ Country: ${results[0]?.country || 'Unknown'}`; const newMessage = await ctx.reply(message, { parse_mode: 'Markdown' }); this.lastGetNumMessages.set(userId, { messageId: newMessage.message_id, range: range, chatId: ctx.chat.id }); const keyboard = Markup.inlineKeyboard([ [Markup.button.callback('šŸ”„ Change Num', 'change_num_1')], [Markup.button.callback('šŸ”„ Change Num 3', 'change_num_3')], [Markup.button.callback('āŒ Cancel', 'cancel_monitor')] ]); const buttonMessage = await ctx.reply('Change number options:', { ...keyboard }); this.userButtonMessages.set(userId, buttonMessage.message_id); } else { await ctx.reply(`āŒ Failed to get numbers from range: ${range}`); } } catch (error) { await ctx.reply('āŒ Error changing numbers'); } } async requestNumber(ctx, rangeInfo) { const userId = ctx.from.id.toString(); try { const headers = { 'Host': 'x.mnitnetwork.com', 'User-Agent': 'Mozilla/5.0 (Linux; Android 14; CPH2641 Build/UP1A.231005.007) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.7499.34 Mobile Safari/537.36', 'Accept': 'application/json, text/plain, */*', 'mauthtoken': this.sharedSession.token, 'Content-Type': 'application/json', 'Origin': 'https://x.mnitnetwork.com', 'Referer': `https://x.mnitnetwork.com/mdashboard/getnum?range=${encodeURIComponent(rangeInfo.range || 'custom')}`, 'Cookie': this.sharedSession.cookie }; const range = rangeInfo.range; const payload = { range: range, is_national: false, remove_plus: false }; const response = await axios.post( `${this.apiBaseURL}/mapi/v1/mdashboard/getnum/number`, payload, { headers: headers, decompress: true, timeout: 15000 } ); if (response.data.meta.code === 200) { const numberData = response.data.data; const targetNumber = numberData.number.replace(/\+/g, ''); if (!this.users[userId]) { this.users[userId] = { id: userId, username: ctx.from.username || ctx.from.first_name, joinDate: new Date().toISOString(), requests: 0, success: 0, failed: 0, blocked: false }; } this.users[userId].requests++; await this.saveJSON(this.usersFile, this.users); if (!this.activeMonitors.has(userId)) { this.activeMonitors.set(userId, []); } if (!this.monitorIntervals.has(userId)) { this.monitorIntervals.set(userId, []); } const monitorId = Date.now().toString(); const monitor = { id: monitorId, userId: userId, targetNumber: targetNumber, chatId: ctx.chat.id, startTime: Date.now(), status: 'pending', rangeInfo: rangeInfo, rangeUsed: range }; this.activeMonitors.get(userId).push(monitor); const intervalId = this.startCheckInterval(userId, targetNumber, monitorId, ctx, range); this.monitorIntervals.get(userId).push(intervalId); return { success: true, number: targetNumber, country: numberData.country, status: numberData.status }; } else { return { success: false, error: response.data.meta.message }; } } catch (error) { if (error.response) { if (error.response.data && error.response.data.message === 'Database failed: No Number Found For Allocation') { return { success: false, error: 'No numbers available in this range' }; } } return { success: false, error: error.message }; } } startCheckInterval(userId, targetNumber, monitorId, ctx, rangeUsed) { const intervalId = setInterval(async () => { try { const status = await this.checkSingleNumberStatus(targetNumber); if (status.found) { if (status.status === 'success' && status.otp) { if (!this.userOTPHistory.has(userId)) { this.userOTPHistory.set(userId, []); } this.userOTPHistory.get(userId).push({ number: targetNumber, otp: status.otp, range: rangeUsed, timestamp: Date.now(), message: status.data.otp || status.data.message }); await this.sendOTPNotification(userId, status, targetNumber, rangeUsed); if (this.groupChatId && this.sendToGroup) { await this.sendOTPToGroup(status, targetNumber, userId, rangeUsed); } if (this.users[userId]) { this.users[userId].success++; await this.saveJSON(this.usersFile, this.users); } if (this.activeMonitors.has(userId)) { const monitors = this.activeMonitors.get(userId); const newMonitors = monitors.filter(m => m.id !== monitorId); this.activeMonitors.set(userId, newMonitors); } clearInterval(intervalId); if (this.monitorIntervals.has(userId)) { const intervals = this.monitorIntervals.get(userId); const newIntervals = intervals.filter(id => id !== intervalId); this.monitorIntervals.set(userId, newIntervals); } } else if (status.status === 'failed') { const copyableNumber = this.formatCopyableNumber(targetNumber); await ctx.reply( `āŒ *Number Failed*\n\nšŸ“± Number: ${copyableNumber}\nšŸ“ž Range: \`${rangeUsed}\`\nšŸ“Š Status: Failed`, { parse_mode: 'Markdown' } ); if (this.users[userId]) { this.users[userId].failed++; await this.saveJSON(this.usersFile, this.users); } if (this.activeMonitors.has(userId)) { const monitors = this.activeMonitors.get(userId); const newMonitors = monitors.filter(m => m.id !== monitorId); this.activeMonitors.set(userId, newMonitors); } clearInterval(intervalId); if (this.monitorIntervals.has(userId)) { const intervals = this.monitorIntervals.get(userId); const newIntervals = intervals.filter(id => id !== intervalId); this.monitorIntervals.set(userId, newIntervals); } } } } catch (error) {} }, this.checkInterval); setTimeout(() => { if (this.activeMonitors.has(userId)) { const monitors = this.activeMonitors.get(userId); const monitorExists = monitors.some(m => m.id === monitorId); if (monitorExists) { clearInterval(intervalId); const newMonitors = monitors.filter(m => m.id !== monitorId); this.activeMonitors.set(userId, newMonitors); if (this.monitorIntervals.has(userId)) { const intervals = this.monitorIntervals.get(userId); const newIntervals = intervals.filter(id => id !== intervalId); this.monitorIntervals.set(userId, newIntervals); } const copyableNumber = this.formatCopyableNumber(targetNumber); ctx.reply(`ā° Timeout for ${copyableNumber} (Range: ${rangeUsed})`); } } }, this.timeoutMinutes * 60 * 1000); return intervalId; } async sendOTPNotification(userId, status, targetNumber, rangeUsed) { try { const user = this.users[userId]; const data = status.data; const otp = status.otp; const fullMessage = data.otp || data.message || 'No message'; const copyableNumber = this.formatCopyableNumber(targetNumber); const copyableOTP = this.formatCopyableNumber(otp); const message = `āœ… *OTP SUCCESS!*\n\nšŸŒ Country: ${data.country || 'Unknown'}\nšŸ“± Number: ${copyableNumber}\nšŸ“ž Range: \`${rangeUsed}\`\n\nšŸ“ Message:\n\`\`\`\n${fullMessage}\n\`\`\`\n\nšŸ”¢ OTP: ${copyableOTP}\n\nšŸŽ‰ Monitor completed!`; await this.bot.telegram.sendMessage(userId, message, { parse_mode: 'Markdown' }); } catch (error) {} } async sendOTPToGroup(status, targetNumber, userId, rangeUsed) { try { if (!this.groupChatId || !this.sendToGroup) { return; } const user = this.users[userId]; const data = status.data; const otp = status.otp; const fullMessage = data.otp || data.message || 'No message'; const sensoredNumber = this.formatSensoredNumber(targetNumber); const copyableOTP = this.formatCopyableNumber(otp); const otpKey = `${targetNumber}-${otp}`; if (this.otpGroupSent.has(otpKey)) { return; } this.otpGroupSent.add(otpKey); let message = `āœ… OTP SUCCESS\n\n`; if (user?.username) { message += `šŸ‘¤ User: ${user.username}\n`; } message += `šŸŒ Country: ${data.country || 'Unknown'}\n`; message += `šŸ“ž Range Used: ${rangeUsed}\n`; message += `šŸ“± Number: ${sensoredNumber}\n`; message += `šŸ”¢ OTP: ${copyableOTP}\n\n`; message += `šŸ“ Full Message:\n${fullMessage}`; try { await this.bot.telegram.sendMessage(this.groupChatId, message); } catch (sendError) {} } catch (error) {} } startGlobalMonitoring() { if (this.globalMonitorInterval) { clearInterval(this.globalMonitorInterval); } if (!this.sharedSession) return; this.globalMonitorInterval = setInterval(async () => { try { if (!this.sharedSession) return; const expiry = new Date(this.sharedSession.expiry); if (expiry <= new Date()) { clearInterval(this.globalMonitorInterval); return; } await this.checkAllRecentOTPs(); } catch (error) {} }, 10000); } extractOTPFromConsole(message) { if (!message) return null; const patterns = [ /code[\s]*is[\s]*(\d{4,8})/i, /code[\s]*:\s*(\d{4,8})/i, /(\d{6})(?![0-9])/, /(\d{3})[\s-]?(\d{3})/, /\*\*\*\*\*\*.*?(\d{4,8})/, /Facebook.*?(\d{4,8})/i, /WhatsApp.*?(\d{4,8})/i, /le\s+(\d{4,8})/i, /est\s+(\d{4,8})/i, /is\s+(\d{4,8})/i ]; for (const pattern of patterns) { const match = message.match(pattern); if (match) { const otp = match[1] || match[0]; if (otp && otp.length >= 4 && otp.length <= 8) { return otp.replace(/\s/g, ''); } } } return null; } async reloadSession(ctx) { await ctx.answerCbQuery('Reloading...'); await this.loadData(); if (this.sharedSession) { const expiry = new Date(this.sharedSession.expiry); const now = new Date(); if (expiry <= now) { await ctx.reply('āŒ Session expired'); } else { await ctx.reply( `āœ… *Session Reloaded*\n\nšŸ‘¤ Account: ${this.sharedSession.username}\nšŸ’° Balance: ${this.sharedSession.balance}\nā° Expires: ${new Date(this.sharedSession.expiry).toLocaleString()}`, { parse_mode: 'Markdown' } ); } } else { await ctx.reply('āŒ No session found'); } } async checkAllRecentOTPs() { try { const today = new Date().toISOString().split('T')[0]; const headers = { 'Host': 'x.mnitnetwork.com', 'User-Agent': 'Mozilla/5.0 (Linux; Android 14; CPH2641 Build/UP1A.231005.007) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.7499.34 Mobile Safari/537.36', 'Accept': 'application/json, text/plain, */*', 'mauthtoken': this.sharedSession.token, 'Cookie': this.sharedSession.cookie }; const successUrl = `${this.apiBaseURL}/mapi/v1/mdashboard/getnum/info?date=${today}&page=1&search=&status=success`; const response = await axios.get(successUrl, { headers }); if (response.data.meta.code === 200) { const numbers = response.data.data.numbers || []; for (const item of numbers) { const itemId = item.id || item.number; if (!this.lastCheckedIds.has(itemId)) { this.lastCheckedIds.add(itemId); const otp = this.extractOTP(item.otp || item.message); if (otp && this.groupChatId && this.sendToGroup) { await this.sendGlobalOTPToGroup(item, otp); } } } if (this.lastCheckedIds.size > 1000) { const idsArray = Array.from(this.lastCheckedIds); this.lastCheckedIds = new Set(idsArray.slice(-500)); } } } catch (error) {} } async sendGlobalOTPToGroup(item, otp) { try { if (!this.groupChatId || !this.sendToGroup) { return; } const number = item.number.toString().replace(/\+/g, ''); const fullMessage = item.otp || item.message || 'No message'; const sensoredNumber = this.formatSensoredNumber(number); const copyableOTP = this.formatCopyableNumber(otp); const otpKey = `${number}-${otp}`; if (this.otpGroupSent.has(otpKey)) { return; } this.otpGroupSent.add(otpKey); const message = `āœ… OTP SUCCESS\n\nšŸ“± Number: ${sensoredNumber}\nšŸ”¢ OTP: ${copyableOTP}\nšŸŒ Country: ${item.country || ''}\n\nšŸ“ Full Message:\n${fullMessage}`; try { await this.bot.telegram.sendMessage(this.groupChatId, message); } catch (sendError) {} } catch (error) {} } async startCheckNumber(ctx) { const userId = ctx.from.id.toString(); if (!this.sharedSession) { await ctx.reply('āŒ No active session'); return; } const expiry = new Date(this.sharedSession.expiry); const now = new Date(); if (expiry <= now) { await ctx.reply('āŒ Session expired'); return; } await ctx.reply( `šŸ” *Check Number*\n\nSend number (example: 2250711051234):`, { parse_mode: 'Markdown' } ); this.waitingForCheckNumber.set(userId, true); } async processCheckNumber(ctx, text) { const userId = ctx.from.id.toString(); if (text === '/cancel') { await ctx.reply('āŒ Cancelled'); return; } if (!this.sharedSession) { await ctx.reply('āŒ No active session'); return; } const expiry = new Date(this.sharedSession.expiry); const now = new Date(); if (expiry <= now) { await ctx.reply('āŒ Session expired'); return; } let phoneNumber = text.trim().replace(/\s/g, ''); phoneNumber = phoneNumber.replace(/\+/g, ''); if (!/^\d{10,15}$/.test(phoneNumber)) { await ctx.reply('āŒ Invalid number format'); return; } const checkingMsg = await ctx.reply(`šŸ” Checking ${phoneNumber}...`); try { const status = await this.checkSingleNumberStatus(phoneNumber); if (status.found) { if (status.status === 'success' && status.otp) { const copyableNumber = this.formatCopyableNumber(phoneNumber); const copyableOTP = this.formatCopyableNumber(status.otp); await ctx.telegram.editMessageText( ctx.chat.id, checkingMsg.message_id, null, `āœ… *Number Found!*\n\nšŸ“± Number: ${copyableNumber}\nšŸ”¢ OTP: ${copyableOTP}\nšŸŒ Country: ${status.data.country || ''}`, { parse_mode: 'Markdown' } ); } else if (status.status === 'failed') { const copyableNumber = this.formatCopyableNumber(phoneNumber); await ctx.telegram.editMessageText( ctx.chat.id, checkingMsg.message_id, null, `āŒ *Number Failed*\n\nšŸ“± Number: ${copyableNumber}\nšŸ“Š Status: Failed`, { parse_mode: 'Markdown' } ); } } else { const copyableNumber = this.formatCopyableNumber(phoneNumber); await ctx.telegram.editMessageText( ctx.chat.id, checkingMsg.message_id, null, `āŒ *Number Not Found*\n\nšŸ“± Number: ${copyableNumber}\nšŸ“Š Status: Not found`, { parse_mode: 'Markdown' } ); } } catch (error) { await ctx.telegram.editMessageText( ctx.chat.id, checkingMsg.message_id, null, `āŒ Error checking number`, { parse_mode: 'Markdown' } ); } } async checkSingleNumberStatus(targetNumber) { try { const cleanTarget = targetNumber.replace(/\+/g, ''); const today = new Date().toISOString().split('T')[0]; const headers = { 'Host': 'x.mnitnetwork.com', 'User-Agent': 'Mozilla/5.0 (Linux; Android 14; CPH2641 Build/UP1A.231005.007) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.7499.34 Mobile Safari/537.36', 'Accept': 'application/json, text/plain, */*', 'mauthtoken': this.sharedSession.token, 'Cookie': this.sharedSession.cookie }; const failedUrl = `${this.apiBaseURL}/mapi/v1/mdashboard/getnum/info?date=${today}&page=1&search=&status=failed`; const failedRes = await axios.get(failedUrl, { headers }); if (failedRes.data.meta.code === 200) { for (const item of failedRes.data.data.numbers) { const itemNumber = item.number.toString().replace(/\+/g, ''); if (itemNumber === cleanTarget) { return { found: true, status: 'failed', data: item }; } } } const successUrl = `${this.apiBaseURL}/mapi/v1/mdashboard/getnum/info?date=${today}&page=1&search=&status=success`; const successRes = await axios.get(successUrl, { headers }); if (successRes.data.meta.code === 200) { for (const item of successRes.data.data.numbers) { const itemNumber = item.number.toString().replace(/\+/g, ''); if (itemNumber === cleanTarget) { const otp = this.extractOTP(item.otp || item.message); return { found: true, status: 'success', data: item, otp: otp }; } } } return { found: false, status: 'not_found' }; } catch (error) { return { found: false, status: 'error' }; } } async cancelAllMonitors(ctx) { const userId = ctx.from.id.toString(); if (!this.activeMonitors.has(userId) || this.activeMonitors.get(userId).length === 0) { await ctx.reply('āŒ No active monitors'); return; } const monitors = this.activeMonitors.get(userId); const count = monitors.length; if (this.monitorIntervals.has(userId)) { const intervals = this.monitorIntervals.get(userId); intervals.forEach(interval => clearInterval(interval)); this.monitorIntervals.delete(userId); } this.activeMonitors.delete(userId); await ctx.reply( `āœ… *All Monitors Cancelled*\n\nCancelled: ${count} monitors`, { parse_mode: 'Markdown' } ); } async cancelAllMonitorsFromButton(ctx) { const userId = ctx.from.id.toString(); if (!this.activeMonitors.has(userId) || this.activeMonitors.get(userId).length === 0) { await ctx.answerCbQuery('āŒ No active monitors'); return; } const monitors = this.activeMonitors.get(userId); const count = monitors.length; if (this.monitorIntervals.has(userId)) { const intervals = this.monitorIntervals.get(userId); intervals.forEach(interval => clearInterval(interval)); this.monitorIntervals.delete(userId); } this.activeMonitors.delete(userId); await ctx.answerCbQuery(`āœ… ${count} cancelled`); await ctx.editMessageText( `āœ… *All Monitors Cancelled*\n\nCancelled: ${count} monitors`, { parse_mode: 'Markdown' } ); } async showSessionInfo(ctx) { if (!this.sharedSession) { await ctx.reply('āŒ No active session'); return; } const expiry = new Date(this.sharedSession.expiry); const now = new Date(); const remaining = Math.floor((expiry - now) / 1000 / 60); const status = expiry > now ? 'āœ… Active' : 'āŒ Expired'; const message = `šŸ” *Session Info*\n\nšŸ‘¤ Account: ${this.sharedSession.username}\nšŸ“§ Email: ${this.sharedSession.email}\nšŸ’° Balance: ${this.sharedSession.balance}\nšŸ“Š Status: ${status}\n\nā° Expiry: ${new Date(this.sharedSession.expiry).toLocaleString()}\nā³ Remaining: ${remaining} minutes`; await ctx.reply(message, { parse_mode: 'Markdown' }); } async showRangeSelection(ctx) { const userId = ctx.from.id.toString(); if (!this.sharedSession) { await ctx.reply('āŒ No active session'); return; } const expiry = new Date(this.sharedSession.expiry); const now = new Date(); if (expiry <= now) { await ctx.reply('āŒ Session expired'); return; } const availableRanges = Object.entries(this.ranges) .filter(([_, range]) => range.enabled) .map(([id, range]) => ({ id, name: range.name, country: range.country, service: range.service, range: range.range })); if (availableRanges.length === 0) { await ctx.reply('āŒ No ranges available'); return; } const buttons = availableRanges.map(range => [ Markup.button.callback( `${range.range} (${range.service})`, `range_${range.id}` ) ]); buttons.push( [Markup.button.callback('Custom', 'custom_range')], [Markup.button.callback('Cancel', 'cancel_monitor')] ); const keyboard = Markup.inlineKeyboard(buttons); await ctx.reply('šŸ“± *Select Range*', { parse_mode: 'Markdown', ...keyboard }); } async showServiceStatus(ctx) { const userId = ctx.from.id.toString(); const user = this.users[userId]; if (!this.sharedSession) { await ctx.reply('āŒ No active session'); return; } const expiry = new Date(this.sharedSession.expiry); const now = new Date(); const sessionStatus = expiry > now ? 'āœ… Active' : 'āŒ Expired'; const remaining = Math.floor((expiry - now) / 1000 / 60); const activeCount = this.activeMonitors.has(userId) ? this.activeMonitors.get(userId).length : 0; const keyboard = Markup.inlineKeyboard([ [Markup.button.callback('āŒ Cancel Monitor', 'cancel_monitor')] ]); await ctx.reply( `šŸ“Š *Your Status*\n\nšŸ‘¤ ${user?.username || ''}\nšŸ’° Balance: ${this.sharedSession.balance}\nšŸ” Session: ${sessionStatus}\nā³ Remaining: ${remaining} minutes\n\nšŸ“ˆ Requests: ${user?.requests || 0}\nāœ… Success: ${user?.success || 0}\nāŒ Failed: ${user?.failed || 0}\n\nšŸ” Active Monitors: ${activeCount}`, { parse_mode: 'Markdown', ...keyboard } ); } async startCustomRange(ctx) { const userId = ctx.from.id.toString(); await ctx.answerCbQuery(); await ctx.reply( `šŸ“± *Custom Range*\n\nSend range with X (2-4 X):\nExample: \`2250711XX\`\nExample: \`22507XXX\`\nExample: \`2250XXXX\`\n\nType /cancel to cancel`, { parse_mode: 'Markdown' } ); this.waitingForCustomRange.set(userId, true); } async processCustomRange(ctx, text) { const userId = ctx.from.id.toString(); if (text === '/cancel') { await ctx.reply('āŒ Cancelled'); return; } if (!this.sharedSession) { await ctx.reply('āŒ No active session'); return; } const expiry = new Date(this.sharedSession.expiry); const now = new Date(); if (expiry <= now) { await ctx.reply('āŒ Session expired'); return; } let range = text.trim().toUpperCase(); if (!/^[0-9X]+$/.test(range)) { await ctx.reply('āŒ Only numbers and X'); return; } if (!range.includes('X')) { await ctx.reply('āŒ Must contain X'); return; } if (range.length < 10) { await ctx.reply('āŒ Too short (min 10)'); return; } const xCount = (range.match(/X/g) || []).length; if (xCount > 4) { await ctx.reply('āŒ Max 4 X'); return; } if (xCount < 2) { await ctx.reply('āŒ Min 2 X required'); return; } await this.getSingleNumber(ctx, range); } async selectRangeHandler(ctx) { const data = ctx.match[1]; const userId = ctx.from.id.toString(); if (this.isProcessing.has(userId)) { await ctx.answerCbQuery('ā³ Processing...'); return; } this.isProcessing.add(userId); try { const rangeId = data; const range = this.ranges[rangeId]; if (!range) { await ctx.answerCbQuery('āŒ Range not found'); this.isProcessing.delete(userId); return; } if (!this.sharedSession) { await ctx.answerCbQuery('āŒ No session'); this.isProcessing.delete(userId); return; } const expiry = new Date(this.sharedSession.expiry); const now = new Date(); if (expiry <= now) { await ctx.answerCbQuery('āŒ Session expired'); this.isProcessing.delete(userId); return; } await ctx.answerCbQuery('šŸ”„ Requesting...'); await this.getSingleNumber(ctx, range.range); } catch (error) { await ctx.answerCbQuery('āŒ Error'); } finally { this.isProcessing.delete(userId); } } async startAddRange(ctx) { await ctx.answerCbQuery(); await ctx.reply( 'āž• *Add Range*\n\nFormat: `nama range`\n\nContoh:\n`Ivory WA 2250711XX`\n`Indo Tokped 62812XXXX`\n`US FB 1202555XXX`\n\nType /cancel to cancel', { parse_mode: 'Markdown' } ); const userId = ctx.from.id.toString(); this.waitingForRangeInput.set(userId, true); } async processRangeInput(ctx, text) { if (text === '/cancel') { await ctx.reply('āŒ Cancelled'); return; } const parts = text.split(' '); if (parts.length < 2) { await ctx.reply('āŒ Format: nama range\nContoh: Ivory WA 2250711XX'); return; } const range = parts[parts.length - 1]; const name = parts.slice(0, parts.length - 1).join(' '); let country = 'Auto'; let service = 'Auto'; if (range.startsWith('225')) country = 'Ivory Coast'; else if (range.startsWith('628')) country = 'Indonesia'; else if (range.startsWith('1')) country = 'USA'; else if (range.startsWith('44')) country = 'UK'; else if (range.startsWith('33')) country = 'France'; const nameLower = name.toLowerCase(); if (nameLower.includes('wa') || nameLower.includes('whatsapp')) service = 'WhatsApp'; else if (nameLower.includes('fb') || nameLower.includes('facebook')) service = 'Facebook'; else if (nameLower.includes('tg') || nameLower.includes('telegram')) service = 'Telegram'; else if (nameLower.includes('tokped') || nameLower.includes('tokopedia')) service = 'Tokopedia'; else if (nameLower.includes('gojek') || nameLower.includes('go')) service = 'Gojek'; if (!/^[0-9X]+$/.test(range)) { await ctx.reply('āŒ Range harus angka dan X'); return; } if (!range.includes('X')) { await ctx.reply('āŒ Range harus mengandung X'); return; } const xCount = (range.match(/X/g) || []).length; if (xCount > 4) { await ctx.reply('āŒ Maksimal 4 X'); return; } if (xCount < 2) { await ctx.reply('āŒ Minimal 2 X'); return; } if (range.length < 10) { await ctx.reply('āŒ Terlalu pendek (min 10 digit)'); return; } const rangeId = Date.now().toString(); this.ranges[rangeId] = { id: rangeId, name: name, country: country, service: service, range: range, xCount: xCount, enabled: true, created: new Date().toISOString(), createdBy: ctx.from.username || ctx.from.id.toString() }; await this.saveJSON(this.rangesFile, this.ranges); await ctx.reply( `āœ… *Range Added*\n\nšŸ“ Nama: ${name}\nšŸŒ Negara: ${country}\nšŸ“± Service: ${service}\nšŸ“ž Range: ${range}`, { parse_mode: 'Markdown' } ); } async listRangesHandler(ctx) { await ctx.answerCbQuery(); const ranges = Object.values(this.ranges); if (ranges.length === 0) { await ctx.reply('āŒ No ranges'); return; } let message = 'šŸ“‹ *Ranges List*\n\n'; ranges.forEach((range, index) => { message += `${index + 1}. ${range.name}\n`; message += ` ${range.country} | ${range.service}\n`; message += ` ${range.range} | ${range.xCount}X | ${range.enabled ? 'āœ…' : 'āŒ'}\n\n`; }); const keyboard = Markup.inlineKeyboard( ranges.map(range => [ Markup.button.callback( `šŸ—‘ļø ${range.name.substring(0, 15)}`, `delete_range_${range.id}` ), Markup.button.callback( range.enabled ? 'āŒ Disable' : 'āœ… Enable', `toggle_range_${range.id}` ) ]) ); await ctx.reply(message, { parse_mode: 'Markdown', ...keyboard }); } async deleteRangeHandler(ctx) { const rangeId = ctx.match[1]; const rangeName = this.ranges[rangeId]?.name || ''; delete this.ranges[rangeId]; await this.saveJSON(this.rangesFile, this.ranges); await ctx.answerCbQuery('āœ… Deleted'); await ctx.editMessageText(`šŸ—‘ļø Range "${rangeName}" deleted`); } async toggleRangeHandler(ctx) { const rangeId = ctx.match[1]; if (this.ranges[rangeId]) { this.ranges[rangeId].enabled = !this.ranges[rangeId].enabled; await this.saveJSON(this.rangesFile, this.ranges); const status = this.ranges[rangeId].enabled ? 'enabled' : 'disabled'; await ctx.answerCbQuery(`āœ… ${status}`); await this.listRangesHandler(ctx); } } async showUsersHandler(ctx) { await ctx.answerCbQuery(); const users = Object.values(this.users); if (users.length === 0) { await ctx.reply('āŒ No users'); return; } let message = 'šŸ‘„ *Users List*\n\n'; users.forEach((user, index) => { const active = this.activeMonitors.has(user.id) ? '🟢' : '⚪'; message += `${index + 1}. ${user.username}\n`; message += ` šŸ“Š ${user.requests} req | āœ… ${user.success} | āŒ ${user.failed}\n`; message += ` šŸ“… ${new Date(user.joinDate).toLocaleDateString()} | ${active}\n\n`; }); await ctx.reply(message, { parse_mode: 'Markdown' }); } async showStatsHandler(ctx) { await ctx.answerCbQuery(); const totalUsers = Object.keys(this.users).length; const totalRanges = Object.keys(this.ranges).length; const activeMonitors = Array.from(this.activeMonitors.values()).reduce((sum, arr) => sum + arr.length, 0); let totalRequests = 0; let totalSuccess = 0; let totalFailed = 0; Object.values(this.users).forEach(user => { totalRequests += user.requests; totalSuccess += user.success; totalFailed += user.failed; }); const successRate = totalRequests > 0 ? ((totalSuccess / totalRequests) * 100).toFixed(2) : 0; const expiry = new Date(this.sharedSession.expiry); const now = new Date(); const remaining = Math.floor((expiry - now) / 1000 / 60); const sessionStatus = expiry > now ? 'āœ… Active' : 'āŒ Expired'; const verifiedUsers = totalUsers; const avgGroupsPerUser = 0; const message = `šŸ“Š *Bot Stats*\n\nšŸ‘„ Users: ${totalUsers}\nšŸ“‹ Ranges: ${totalRanges}\nšŸ” Active Monitors: ${activeMonitors}\n\nšŸ“ˆ Total Requests: ${totalRequests}\nāœ… Success: ${totalSuccess}\nāŒ Failed: ${totalFailed}\nšŸ“Š Success Rate: ${successRate}%\n\nšŸ” Session: ${sessionStatus}\nā³ Remaining: ${remaining} minutes\nšŸ’° Balance: ${this.sharedSession?.balance || 0}\n\nšŸ“± Console Monitor: ${this.consoleMonitorInterval ? 'āœ… Active' : 'āŒ Inactive'}\nšŸ“Š Console Checks: ${this.consoleCheckCount}\nā±ļø Console Interval: ${this.consoleCheckInterval/1000} seconds`; await ctx.reply(message, { parse_mode: 'Markdown' }); } async startBroadcast(ctx) { await ctx.answerCbQuery(); await ctx.reply('šŸ“¢ Send broadcast message:'); const userId = ctx.from.id.toString(); this.waitingForBroadcast.set(userId, true); } async processBroadcast(ctx, text) { const users = Object.keys(this.users); let sent = 0; const progressMsg = await ctx.reply(`šŸ“¢ Sending to ${users.length} users...\nāœ… Sent: 0/${users.length}`); for (const userId of users) { try { await this.bot.telegram.sendMessage(userId, `šŸ“¢ *Broadcast*\n\n${text}`, { parse_mode: 'Markdown' }); sent++; if (sent % 5 === 0) { await ctx.telegram.editMessageText(ctx.chat.id, progressMsg.message_id, null, `šŸ“¢ Sending...\nāœ… Sent: ${sent}/${users.length}`); } await new Promise(resolve => setTimeout(resolve, 100)); } catch (error) {} } await ctx.telegram.editMessageText(ctx.chat.id, progressMsg.message_id, null, `āœ… Sent to ${sent}/${users.length} users`); } extractOTP(message) { if (!message) return null; const patterns = [ /\b\d{6}\b/, /\b\d{3}-\d{3}\b/, /\b\d{3}\s\d{3}\b/, /\b\d{4,8}\b/ ]; for (const pattern of patterns) { const match = message.match(pattern); if (match) { return match[0].replace(/\s/g, '-'); } } return null; } async toggleConsoleMonitor(ctx) { if (this.consoleMonitorInterval) { clearInterval(this.consoleMonitorInterval); this.consoleMonitorInterval = null; await ctx.answerCbQuery('āœ… Console monitor stopped'); await ctx.reply('āœ… Console monitor stopped'); } else { this.startConsoleMonitoring(); await ctx.answerCbQuery('āœ… Console monitor started'); await ctx.reply('āœ… Console monitor started (checking every 5 seconds)'); } } async showAdminSettings(ctx) { const settings = { consoleMonitor: this.consoleMonitorInterval ? 'āœ… Active' : 'āŒ Inactive', globalMonitor: this.globalMonitorInterval ? 'āœ… Active' : 'āŒ Inactive', lastConsoleId: this.lastConsoleId, consoleCheckCount: this.consoleCheckCount, consoleCheckInterval: `${this.consoleCheckInterval/1000} seconds`, activeMonitors: Array.from(this.activeMonitors.values()).reduce((sum, arr) => sum + arr.length, 0), totalUsers: Object.keys(this.users).length }; const keyboard = Markup.inlineKeyboard([ [Markup.button.callback('šŸ“± Toggle Console Monitor', 'toggle_console_monitor')], [Markup.button.callback('šŸ“Š View Stats', 'show_stats')], [Markup.button.callback('šŸ” Check Console Now', 'check_console_now')] ]); await ctx.reply( `āš™ļø *Admin Settings*\n\nšŸ“± Console Monitor: ${settings.consoleMonitor}\nā±ļø Console Interval: ${settings.consoleCheckInterval}\nšŸŒ Global Monitor: ${settings.globalMonitor}\nšŸ“Š Last Console ID: ${settings.lastConsoleId}\nšŸ”¢ Console Checks: ${settings.consoleCheckCount}\nšŸ” Active Monitors: ${settings.activeMonitors}\nšŸ‘„ Total Users: ${settings.totalUsers}\n\nā±ļø Check Interval: ${this.checkInterval}ms\nā° Timeout: ${this.timeoutMinutes} minutes`, { parse_mode: 'Markdown', ...keyboard } ); } async startBulkRequest(ctx) { const userId = ctx.from.id.toString(); if (!this.sharedSession) { await ctx.reply('āŒ No active session'); return; } const expiry = new Date(this.sharedSession.expiry); const now = new Date(); if (expiry <= now) { await ctx.reply('āŒ Session expired'); return; } const availableRanges = Object.entries(this.ranges) .filter(([_, range]) => range.enabled) .map(([id, range]) => ({ id, name: range.name, country: range.country, service: range.service, range: range.range })); if (availableRanges.length === 0) { await ctx.reply('āŒ No ranges available for bulk'); return; } const buttons = availableRanges.map(range => [ Markup.button.callback( `${range.range} (${range.service})`, `bulk_range_${range.id}` ) ]); buttons.push([Markup.button.callback('Custom Bulk', 'custom_range')]); const keyboard = Markup.inlineKeyboard(buttons); await ctx.reply( `šŸ“¦ *Select Range for Bulk Request*\n\nBot akan otomatis get 3 numbers dari range yang dipilih dan mulai monitoring.`, { parse_mode: 'Markdown', ...keyboard } ); } async startBulkFromRange(ctx) { const rangeId = ctx.match[1]; const userId = ctx.from.id.toString(); if (this.isBulkProcessing) { await ctx.answerCbQuery('ā³ Sedang memproses bulk sebelumnya...'); return; } const range = this.ranges[rangeId]; if (!range) { await ctx.answerCbQuery('āŒ Range tidak ditemukan'); return; } await ctx.answerCbQuery(`šŸ”„ Memulai bulk request dari ${range.range}...`); this.isBulkProcessing = true; this.bulkRequests.set(userId, { range: range.range, count: 0, success: 0, failed: 0 }); await ctx.reply( `šŸ“¦ *Bulk Request Started*\n\nšŸ“ž Range: \`${range.range}\`\nšŸ”„ Mengambil 3 numbers...`, { parse_mode: 'Markdown' } ); const results = []; for (let i = 0; i < 3; i++) { try { const result = await this.requestNumber(ctx, { name: range.name, country: range.country, service: range.service, range: range.range }); results.push(result); if (result.success) { const bulkData = this.bulkRequests.get(userId); bulkData.success++; bulkData.count++; this.bulkRequests.set(userId, bulkData); } else { const bulkData = this.bulkRequests.get(userId); bulkData.failed++; bulkData.count++; this.bulkRequests.set(userId, bulkData); if (result.error === 'No numbers available in this range') { break; } } if (i < 2) { await new Promise(resolve => setTimeout(resolve, 2000)); } } catch (error) { const bulkData = this.bulkRequests.get(userId); bulkData.failed++; bulkData.count++; this.bulkRequests.set(userId, bulkData); } } const bulkData = this.bulkRequests.get(userId); if (bulkData.success > 0) { let message = `šŸ“¦ *3 Numbers Allocated!*\n\nšŸ“ž Range: \`${range.range}\`\nšŸ“Š Status: success\n\n`; results.forEach((result, index) => { if (result.success) { const copyableNumber = this.formatCopyableNumber(result.number); message += `Number ${index + 1}:\n${copyableNumber}\n`; } }); message += `\nšŸŒ Country: ${results[0]?.country || 'Unknown'}`; const newMessage = await ctx.reply(message, { parse_mode: 'Markdown' }); this.lastGetNumMessages.set(userId, { messageId: newMessage.message_id, range: range.range, chatId: ctx.chat.id }); const keyboard = Markup.inlineKeyboard([ [Markup.button.callback('šŸ”„ Change Num', 'change_num_1')], [Markup.button.callback('šŸ”„ Change Num 3', 'change_num_3')], [Markup.button.callback('āŒ Cancel', 'cancel_monitor')] ]); const buttonMessage = await ctx.reply('Change number options:', { ...keyboard }); this.userButtonMessages.set(userId, buttonMessage.message_id); } else { await ctx.reply( `šŸ“¦ *Bulk Request Complete*\n\nšŸ“ž Range: \`${range.range}\`\nšŸ“Š Total Attempts: ${bulkData.count}\nāœ… Success: ${bulkData.success}\nāŒ Failed: ${bulkData.failed}\n\nšŸ” All numbers are now being monitored.`, { parse_mode: 'Markdown' } ); } this.bulkRequests.delete(userId); this.isBulkProcessing = false; } async handleChangeNumber(ctx) { try { await ctx.answerCbQuery('Changing number...'); } catch (error) {} } } const bot = new TelegramOTPBot(); const gracefulShutdown = () => { console.log('Shutting down gracefully...'); if (bot.bot) { bot.bot.stop(); } if (bot.globalMonitorInterval) { clearInterval(bot.globalMonitorInterval); } if (bot.consoleMonitorInterval) { clearInterval(bot.consoleMonitorInterval); } if (bot.monitorIntervals) { for (const intervals of bot.monitorIntervals.values()) { intervals.forEach(interval => clearInterval(interval)); } } if (bot.server) { bot.server.close(); } process.exit(0); }; process.once('SIGINT', gracefulShutdown); process.once('SIGTERM', gracefulShutdown); process.on('uncaughtException', (error) => { console.error('UNCAUGHT:', error); }); process.on('unhandledRejection', (reason, promise) => { console.error('UNHANDLED:', promise, reason); }); bot.initialize().catch(error => { console.error('FAILED to initialize:', error); process.exit(1); });