| 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() { |
| |
| 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) : []; |
| |
| |
| 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; |
| |
| |
| 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(); |
| |
| |
| this.startExpressServer(); |
| } |
|
|
| startExpressServer() { |
| this.expressApp = express(); |
| |
| |
| this.expressApp.use((req, res, next) => { |
| console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`); |
| next(); |
| }); |
| |
| |
| 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' }); |
| } |
| }); |
| } |
| |
| |
| 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); |
| }); |
| |
| |
| 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 = ` |
| <!DOCTYPE html> |
| <html> |
| <head> |
| <title>Telegram OTP Bot - ${this.domain}</title> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <style> |
| body { |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| margin: 0; |
| padding: 20px; |
| min-height: 100vh; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| } |
| .container { |
| background: white; |
| border-radius: 15px; |
| padding: 40px; |
| box-shadow: 0 20px 60px rgba(0,0,0,0.3); |
| max-width: 600px; |
| width: 100%; |
| } |
| h1 { |
| color: #333; |
| text-align: center; |
| margin-bottom: 30px; |
| font-size: 2.5em; |
| } |
| .status-card { |
| background: #f8f9fa; |
| border-radius: 10px; |
| padding: 25px; |
| margin-bottom: 20px; |
| border-left: 5px solid #28a745; |
| } |
| .status-item { |
| display: flex; |
| justify-content: space-between; |
| margin-bottom: 15px; |
| padding-bottom: 15px; |
| border-bottom: 1px solid #dee2e6; |
| } |
| .status-item:last-child { |
| margin-bottom: 0; |
| padding-bottom: 0; |
| border-bottom: none; |
| } |
| .label { |
| font-weight: 600; |
| color: #495057; |
| } |
| .value { |
| font-weight: 700; |
| color: #212529; |
| } |
| .online { |
| color: #28a745; |
| } |
| .offline { |
| color: #dc3545; |
| } |
| .stats { |
| display: grid; |
| grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); |
| gap: 15px; |
| margin-top: 30px; |
| } |
| .stat-box { |
| background: #e9ecef; |
| border-radius: 8px; |
| padding: 20px; |
| text-align: center; |
| } |
| .stat-number { |
| font-size: 2em; |
| font-weight: 700; |
| color: #495057; |
| display: block; |
| } |
| .stat-label { |
| font-size: 0.9em; |
| color: #6c757d; |
| margin-top: 5px; |
| } |
| .last-update { |
| text-align: center; |
| color: #6c757d; |
| font-size: 0.9em; |
| margin-top: 30px; |
| padding-top: 20px; |
| border-top: 1px solid #dee2e6; |
| } |
| .domain-info { |
| background: #e3f2fd; |
| border-radius: 8px; |
| padding: 15px; |
| margin-top: 20px; |
| text-align: center; |
| } |
| .bot-info { |
| background: #e8f5e9; |
| border-radius: 8px; |
| padding: 15px; |
| margin-top: 15px; |
| text-align: center; |
| } |
| </style> |
| <meta http-equiv="refresh" content="10"> |
| </head> |
| <body> |
| <div class="container"> |
| <h1>π€ Telegram OTP Bot</h1> |
| |
| <div class="status-card"> |
| <div class="status-item"> |
| <span class="label">Bot Status:</span> |
| <span class="value ${this.isBotRunning ? 'online' : 'offline'}">${botStatus}</span> |
| </div> |
| <div class="status-item"> |
| <span class="label">Bot Username:</span> |
| <span class="value">@${botUsername}</span> |
| </div> |
| <div class="status-item"> |
| <span class="label">Session Status:</span> |
| <span class="value">${sessionStatus}</span> |
| </div> |
| <div class="status-item"> |
| <span class="label">Account:</span> |
| <span class="value">${this.sharedSession ? this.sharedSession.username : 'N/A'}</span> |
| </div> |
| <div class="status-item"> |
| <span class="label">Balance:</span> |
| <span class="value">${balance}</span> |
| </div> |
| <div class="status-item"> |
| <span class="label">Session Expires:</span> |
| <span class="value">${expiry}</span> |
| </div> |
| </div> |
| |
| <div class="stats"> |
| <div class="stat-box"> |
| <span class="stat-number">${Object.keys(this.users || {}).length}</span> |
| <span class="stat-label">Total Users</span> |
| </div> |
| <div class="stat-box"> |
| <span class="stat-number">${Array.from(this.activeMonitors.values()).reduce((sum, arr) => sum + arr.length, 0)}</span> |
| <span class="stat-label">Active Monitors</span> |
| </div> |
| <div class="stat-box"> |
| <span class="stat-number">${Object.keys(this.ranges || {}).length}</span> |
| <span class="stat-label">Available Ranges</span> |
| </div> |
| <div class="stat-box"> |
| <span class="stat-number">${this.expressPort}</span> |
| <span class="stat-label">API Port</span> |
| </div> |
| </div> |
| |
| <div class="bot-info"> |
| <strong>π€ Bot Info:</strong><br> |
| @${botUsername}<br> |
| ${this.adminIds.length} Admin(s) |
| </div> |
| |
| <div class="domain-info"> |
| <strong>π Domain:</strong> ${this.domain}<br> |
| <strong>π Environment:</strong> ${process.env.NODE_ENV || 'development'} |
| </div> |
| |
| <div class="last-update"> |
| Last updated: ${new Date().toLocaleString()}<br> |
| Auto-refresh every 10 seconds |
| </div> |
| </div> |
| </body> |
| </html> |
| `; |
| |
| res.send(html); |
| }); |
| |
| |
| 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); |
| }); |
| |
| |
| 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); |
| }); |
| |
| |
| this.expressApp.post('/webhook', express.json(), (req, res) => { |
| console.log('Webhook received:', req.body); |
| res.json({ received: true }); |
| }); |
| |
| |
| if (this.enablePing) { |
| this.expressApp.get('/ping', (req, res) => { |
| res.json({ |
| pong: new Date().toISOString(), |
| bot: this.isBotRunning, |
| uptime: process.uptime() |
| }); |
| }); |
| } |
| |
| |
| this.expressApp.use((req, res) => { |
| res.status(404).json({ |
| error: 'Not Found', |
| available_routes: ['/', '/status', '/health', '/api/info', '/webhook'].concat(this.enablePing ? ['/ping'] : []) |
| }); |
| }); |
| |
| |
| this.expressApp.use((err, req, res, next) => { |
| console.error('Express error:', err); |
| res.status(500).json({ error: 'Internal Server Error' }); |
| }); |
| |
| |
| 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'}`); |
| }); |
| |
| |
| 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); |
| }); |