Spaces:
Paused
Paused
| const TelegramBot = require('node-telegram-bot-api'); | |
| const { spawn } = require('child_process'); | |
| const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path; | |
| const os = require('os'); | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const axios = require('axios'); | |
| const token = '8141535657:AAHpD6GNWncBD9lEdfEuiJExwEVuROIodAI'; | |
| const ADMIN_USER_ID = '7708913693'; | |
| const WATERMARK_DIR = path.join(__dirname, 'watermarks'); | |
| const activeStreams = new Map(); | |
| if (!fs.existsSync(WATERMARK_DIR)) fs.mkdirSync(WATERMARK_DIR); | |
| const bot = new TelegramBot(token, { polling: true }); | |
| function log(msg) { | |
| console.log(`[${new Date().toISOString()}] ${msg}`); | |
| } | |
| function isAdmin(id) { | |
| return id.toString() === ADMIN_USER_ID; | |
| } | |
| function generateStreamId() { | |
| return Math.floor(1000 + Math.random() * 9000).toString(); | |
| } | |
| async function downloadWatermark(url, name) { | |
| const filePath = path.join(WATERMARK_DIR, `${name}.png`); | |
| const response = await axios({ url, method: 'GET', responseType: 'stream' }); | |
| const writer = fs.createWriteStream(filePath); | |
| response.data.pipe(writer); | |
| return new Promise((resolve, reject) => { | |
| writer.on('finish', () => resolve(filePath)); | |
| writer.on('error', reject); | |
| }); | |
| } | |
| // /start | |
| bot.onText(/\/start/, (msg) => { | |
| const id = msg.from.id; | |
| if (!isAdmin(id)) return bot.sendMessage(msg.chat.id, '❌ غير مصرح لك.'); | |
| bot.sendMessage(msg.chat.id, ` | |
| مرحبًا! أوامر البوت: | |
| 🟢 /stream <fbkey> <m3u8> [watermark] [cc] - بدء البث | |
| 📷 /watermark <url> <name> - تحميل شعار | |
| 🔁 /urlchange <id> <new_m3u8> - تغيير المصدر | |
| ✍️ /cchange <id> <new_text> - تغيير النص | |
| 📴 /stop <id> - إيقاف البث | |
| 📟 /check - معلومات النظام`); | |
| }); | |
| // /watermark | |
| bot.onText(/\/watermark (.+) (.+)/, async (msg, [_, url, name]) => { | |
| if (!isAdmin(msg.from.id)) return bot.sendMessage(msg.chat.id, '❌ غير مصرح.'); | |
| try { | |
| const filePath = await downloadWatermark(url.trim(), name.trim()); | |
| bot.sendMessage(msg.chat.id, `✅ تم تحميل الشعار: ${filePath}`); | |
| } catch (e) { | |
| bot.sendMessage(msg.chat.id, `❌ خطأ في التحميل: ${e.message}`); | |
| } | |
| }); | |
| // /stream with single success/failure message | |
| bot.onText(/\/stream (.+?) (.+?)(?: (.+?))?(?: (.+))?/, async (msg, match) => { | |
| const [_, fbKey, m3u8Url, watermarkName, ccText = ''] = match; | |
| const chatId = msg.chat.id; | |
| const userId = msg.from.id; | |
| if (!isAdmin(userId)) return bot.sendMessage(chatId, '❌ غير مصرح.'); | |
| const rtmpsUrl = `rtmps://live-api-s.facebook.com:443/rtmp/${fbKey.trim()}`; | |
| let watermarkPath = null; | |
| if (watermarkName) { | |
| watermarkPath = path.join(WATERMARK_DIR, `${watermarkName}.png`); | |
| if (!fs.existsSync(watermarkPath)) { | |
| return bot.sendMessage(chatId, `❌ الشعار ${watermarkName}.png غير موجود`); | |
| } | |
| } | |
| if (!m3u8Url.startsWith('http') || !rtmpsUrl.startsWith('rtmps')) { | |
| return bot.sendMessage(chatId, '❌ رابط M3U8 أو مفتاح فيسبوك غير صالح.'); | |
| } | |
| let streamId; | |
| do streamId = generateStreamId(); while (activeStreams.has(streamId)); | |
| let cmd = `${ffmpegPath} -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5 -itsoffset 5 -re -i "${m3u8Url}" `; | |
| if (watermarkPath) { | |
| cmd += `-i "${watermarkPath}" -filter_complex "[0:v][1:v]overlay=10:10[vt];`; | |
| } else { | |
| cmd += `-filter_complex "[0:v]copy[vt];`; | |
| } | |
| cmd += `[vt]drawtext=fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf:text='${ccText}':fontcolor=white:fontsize=24:x=w-tw-10*t:y=h-th-10,`; | |
| cmd += `drawtext=fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf:text='Vanilla X':fontcolor=white:fontsize=16:x=10:y=10:box=1:boxcolor=black@0.5:boxborderw=5[outv]" `; | |
| cmd += `-map "[outv]" -map 0:a -c:v libx264 -preset veryfast -b:v 3000k -c:a aac -f flv "${rtmpsUrl}"`; | |
| const proc = spawn(cmd, { shell: true }); | |
| let hasResponded = false; | |
| proc.stderr.on('data', (data) => { | |
| const err = data.toString(); | |
| log(`FFmpeg stderr (Stream ${streamId}): ${err}`); | |
| if (!hasResponded) { | |
| if (err.includes('Server error') || err.includes('Invalid data')) { | |
| bot.sendMessage(chatId, `❌ فشل بدء البث ${streamId}: مفتاح RTMPS غير صالح`); | |
| } else if (err.includes('No such file') || err.includes('Invalid argument')) { | |
| bot.sendMessage(chatId, `❌ فشل بدء البث ${streamId}: رابط M3U8 غير صالح`); | |
| } else { | |
| bot.sendMessage(chatId, `❌ فشل بدء البث ${streamId}: خطأ غير معروف`); | |
| } | |
| hasResponded = true; | |
| proc.kill('SIGTERM'); | |
| activeStreams.delete(streamId); | |
| } | |
| }); | |
| proc.on('spawn', () => { | |
| // Wait 3 seconds to catch early errors before confirming success | |
| setTimeout(() => { | |
| if (!hasResponded) { | |
| bot.sendMessage(chatId, `✅ تم بدء البث: ${streamId}`); | |
| hasResponded = true; | |
| activeStreams.set(streamId, { process: proc, chatId, rtmpsUrl, m3u8Url, cc: ccText, watermark: watermarkName }); | |
| log(`Stream ${streamId} started for user ${userId}`); | |
| } | |
| }, 3000); | |
| }); | |
| proc.on('close', (code) => { | |
| log(`FFmpeg closed (Stream ${streamId}) with code ${code}`); | |
| if (!hasResponded && code !== 0) { | |
| bot.sendMessage(chatId, `❌ فشل بدء البث ${streamId}: FFmpeg أغلق بالكود ${code}`); | |
| activeStreams.delete(streamId); | |
| } else { | |
| activeStreams.delete(streamId); | |
| } | |
| }); | |
| proc.on('error', (err) => { | |
| if (!hasResponded) { | |
| bot.sendMessage(chatId, `❌ خطأ في البث ${streamId}: ${err.message}`); | |
| hasResponded = true; | |
| } | |
| activeStreams.delete(streamId); | |
| log(`FFmpeg error (Stream ${streamId}): ${err.message}`); | |
| }); | |
| }); | |
| // /urlchange | |
| bot.onText(/\/urlchange (\d{4}) (.+)/, (msg, match) => { | |
| const [_, id, newUrl] = match; | |
| const stream = activeStreams.get(id); | |
| if (!isAdmin(msg.from.id)) return bot.sendMessage(msg.chat.id, '❌ غير مصرح.'); | |
| if (!stream) return bot.sendMessage(msg.chat.id, `❌ لا يوجد بث ${id}`); | |
| stream.process.kill('SIGTERM'); | |
| bot.emit('text', { | |
| ...msg, | |
| text: `/stream ${stream.rtmpsUrl.split('/').pop()} ${newUrl} ${stream.watermark || ''} ${stream.cc}` | |
| }); | |
| }); | |
| // /cchange | |
| bot.onText(/\/cchange (\d{4}) (.+)/, (msg, match) => { | |
| const [_, id, newCC] = match; | |
| const stream = activeStreams.get(id); | |
| if (!isAdmin(msg.from.id)) return bot.sendMessage(msg.chat.id, '❌ غير مصرح.'); | |
| if (!stream) return bot.sendMessage(msg.chat.id, `❌ لا يوجد بث ${id}`); | |
| stream.process.kill('SIGTERM'); | |
| bot.emit('text', { | |
| ...msg, | |
| text: `/stream ${stream.rtmpsUrl.split('/').pop()} ${stream.m3u8Url} ${stream.watermark || ''} ${newCC}` | |
| }); | |
| }); | |
| // /stop | |
| bot.onText(/\/stop (\d{4})/, (msg, match) => { | |
| const id = match[1]; | |
| const stream = activeStreams.get(id); | |
| if (!isAdmin(msg.from.id)) return bot.sendMessage(msg.chat.id, '❌ غير مصرح.'); | |
| if (!stream) return bot.sendMessage(msg.chat.id, `❌ لا يوجد بث ${id}`); | |
| stream.process.kill('SIGTERM'); | |
| activeStreams.delete(id); | |
| bot.sendMessage(msg.chat.id, `🛑 تم إيقاف البث ${id}`); | |
| }); | |
| // /check | |
| bot.onText(/\/check/, (msg) => { | |
| if (!isAdmin(msg.from.id)) return bot.sendMessage(msg.chat.id, '❌ غير مصرح.'); | |
| const mem = process.memoryUsage(); | |
| const load = os.loadavg(); | |
| const uptime = process.uptime(); | |
| bot.sendMessage(msg.chat.id, `📟 النظام: | |
| - Uptime: ${(uptime / 60).toFixed(1)} min | |
| - RAM: ${(mem.rss / 1024 / 1024).toFixed(1)} MB | |
| - Load Avg: ${load.map(v => v.toFixed(2)).join(', ')}`); | |
| }); | |
| // Bot startup log | |
| console.log(`✅ Bot is running and polling for updates...`); |