Spaces:
Forgets
/
Runtime error

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