| | 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); |
| | }); |