Spaces:
Running
Running
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 <fbkey> <m3u8> | |
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 <number> | |
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('البوت يعمل...'); |