const TelegramBot = require('node-telegram-bot-api'); const { spawn } = require('child_process'); const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path; const os = require('os'); // Initialize Telegram bot with polling const token = '7899900752:AAFUdjznKaXgSkpp7eqFJjXanpb9eQVqw6E'; if (!token) { console.error('خطأ: يجب تعيين TELEGRAM_BOT_TOKEN في الكود'); process.exit(1); } const bot = new TelegramBot(token, { polling: true }); // Admin user ID (replace with your actual user ID) const ADMIN_USER_ID = '7708913693'; // TODO: Replace with your Telegram user ID // Store active streams: { id: { process: childProcess, chatId: number, rtmpsUrl: string } } const activeStreams = new Map(); // Helper function to log with timestamp function logToConsole(message) { const timestamp = new Date().toISOString(); console.log(`[${timestamp}] ${message}`); } // Generate a random 4-digit ID function generateStreamId() { return Math.floor(1000 + Math.random() * 9000).toString(); } // Check if user is admin function isAdmin(userId) { return userId.toString() === ADMIN_USER_ID; } // Handle /start command bot.onText(/\/start/, (msg) => { const userId = msg.from.id; const chatId = msg.chat.id; if (!isAdmin(userId)) { bot.sendMessage(chatId, 'خطأ: لست مخولًا لاستخدام هذا البوت'); logToConsole(`Unauthorized user ${userId} sent /start`); return; } const message = 'مرحبًا! أنا بوت بث الفيديو. استخدم الأوامر التالية:\n' + '/stream <مفتاح_فيسبوك> <رابط_m3u8> - لبدء البث\n' + '/check - لعرض معلومات النظام\n' + '/stop <رقم_البث> - لإيقاف البث'; bot.sendMessage(chatId, message); logToConsole(`User ${userId} sent /start`); }); // Handle /stream bot.onText(/\/stream (.+) (.+)/, (msg, match) => { const userId = msg.from.id; const chatId = msg.chat.id; if (!isAdmin(userId)) { bot.sendMessage(chatId, 'خطأ: لست مخولًا لاستخدام هذا البوت'); logToConsole(`Unauthorized user ${userId} attempted /stream`); return; } const fbKey = match[1].trim(); const m3u8Url = match[2].trim(); const rtmpsUrl = `rtmps://live-api-s.facebook.com:443/rtmp/${fbKey}`; // Validate inputs if (!m3u8Url.startsWith('http') || !rtmpsUrl.startsWith('rtmps')) { bot.sendMessage(chatId, 'خطأ: رابط M3U8 أو RTMPS غير صالح'); logToConsole(`Invalid URLs for user ${userId}: ${m3u8Url}, ${rtmpsUrl}`); return; } // Generate unique stream ID let streamId; do { streamId = generateStreamId(); } while (activeStreams.has(streamId)); // Single-line FFmpeg command with reconnect and 5-second delay const ffmpegCommand = `ffmpeg -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5 -itsoffset 5 -re -i "${m3u8Url}" -c:v copy -c:a aac -f flv "${rtmpsUrl}"`; logToConsole(`Executing FFmpeg for Stream ${streamId}: ${ffmpegCommand}`); // Start FFmpeg process const ffmpegProcess = spawn(ffmpegCommand, { shell: true }); ffmpegProcess.stdout.on('data', (data) => { logToConsole(`FFmpeg stdout (Stream ${streamId}): ${data}`); }); ffmpegProcess.stderr.on('data', (data) => { logToConsole(`FFmpeg stderr (Stream ${streamId}): ${data}`); }); ffmpegProcess.on('spawn', () => { bot.sendMessage(chatId, `تم بدء البث برقم تعريف: ${streamId}`); activeStreams.set(streamId, { process: ffmpegProcess, chatId, rtmpsUrl }); logToConsole(`Stream ${streamId} started for user ${userId}`); }); ffmpegProcess.on('error', (err) => { bot.sendMessage(chatId, `فشل البث ${streamId}: ${err.message}`); logToConsole(`FFmpeg error (Stream ${streamId}): ${err.message}`); activeStreams.delete(streamId); }); ffmpegProcess.on('close', (code) => { bot.sendMessage(chatId, `انتهى البث ${streamId} (رمز الخروج: ${code}).`); logToConsole(`FFmpeg closed (Stream ${streamId}) with code ${code}`); activeStreams.delete(streamId); }); }); // Handle /check bot.onText(/\/check/, (msg) => { const userId = msg.from.id; const chatId = msg.chat.id; if (!isAdmin(userId)) { bot.sendMessage(chatId, 'خطأ: لست مخولًا لاستخدام هذا البوت'); logToConsole(`Unauthorized user ${userId} attempted /check`); return; } const systemInfo = ` معلومات النظام: - استخدام المعالج: ${Math.round(os.loadavg()[0] * 100) / 100}% (متوسط دقيقة واحدة) - الذاكرة الحرة: ${(os.freemem() / 1024 / 1024 / 1024).toFixed(2)} غيغابايت - إجمالي الذاكرة: ${(os.totalmem() / 1024 / 1024 / 1024).toFixed(2)} غيغابايت - البثوث النشطة: ${activeStreams.size} `; bot.sendMessage(chatId, systemInfo); logToConsole(`User ${userId} requested /check`); }); // Handle /stop bot.onText(/\/stop (\d{4})/, (msg, match) => { const userId = msg.from.id; const chatId = msg.chat.id; if (!isAdmin(userId)) { bot.sendMessage(chatId, 'خطأ: لست مخولًا لاستخدام هذا البوت'); logToConsole(`Unauthorized user ${userId} attempted /stop ${match[1]}`); return; } const streamId = match[1]; if (!activeStreams.has(streamId)) { bot.sendMessage(chatId, `لا يوجد بث برقم تعريف: ${streamId}`); logToConsole(`User ${userId} tried to stop non-existent stream ${streamId}`); return; } const stream = activeStreams.get(streamId); logToConsole(`Attempting to stop FFmpeg process for Stream ${streamId} (PID: ${stream.process.pid})`); // Try SIGTERM first, then SIGKILL after 5 seconds if not terminated stream.process.kill('SIGTERM'); const timeout = setTimeout(() => { if (activeStreams.has(streamId)) { stream.process.kill('SIGKILL'); logToConsole(`Force-killed FFmpeg process for Stream ${streamId} (PID: ${stream.process.pid})`); } }, 5000); stream.process.on('close', (code) => { clearTimeout(timeout); // Cancel SIGKILL if process closes bot.sendMessage(chatId, `انتهى البث ${streamId} (رمز الخروج: ${code}).`); logToConsole(`FFmpeg closed (Stream ${streamId}) with code ${code}`); activeStreams.delete(streamId); }); bot.sendMessage(chatId, `تم إيقاف البث ${streamId}.`); logToConsole(`User ${userId} stopped stream ${streamId}`); activeStreams.delete(streamId); // Ensure cleanup even if close event is delayed }); // Log errors bot.on('error', (err) => { logToConsole(`Bot error: ${err.message}`); }); // Handle polling errors bot.on('polling_error', (err) => { logToConsole(`Polling error: ${err.message}`); }); logToConsole('Bot started'); console.log('البوت يعمل...');