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:AAGYH7PFD8vjbd5Ty6IJn2r8yBRpdSVEbQg'; | |
const ADMIN_USER_ID = '7708913693'; | |
const WATERMARK_DIR = path.join(__dirname, 'watermarks'); | |
if (!fs.existsSync(WATERMARK_DIR)) fs.mkdirSync(WATERMARK_DIR); | |
const activeStreams = new Map(); | |
const bot = new TelegramBot(token, { polling: true }); | |
// Logger with timestamp | |
function log(message) { | |
console.log(`[${new Date().toISOString()}] ${message}`); | |
} | |
// Check admin | |
function isAdmin(userId) { | |
return userId.toString() === ADMIN_USER_ID; | |
} | |
// Generate 4-digit stream ID | |
function generateStreamId() { | |
return Math.floor(1000 + Math.random() * 9000).toString(); | |
} | |
// Download watermark image from URL and save | |
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 command | |
bot.onText(/\/start/, (msg) => { | |
const userId = msg.from.id; | |
const chatId = msg.chat.id; | |
if (!isAdmin(userId)) { | |
bot.sendMessage(chatId, '❌ غير مصرح لك باستخدام هذا البوت.'); | |
log(`Unauthorized /start from user ${userId}`); | |
return; | |
} | |
const message = ` | |
مرحبًا! هذه أوامر البوت: | |
🟢 /stream <fbkey> <m3u8> [watermark] [cc] - بدء بث جديد | |
📷 /watermark <url> <name> - تحميل شعار جديد | |
🔁 /urlchange <id> <new_m3u8> - تغيير رابط البث | |
✍️ /cchange <id> <new_text> - تغيير نص CC المتحرك | |
🛑 /stop <id> - إيقاف بث | |
📟 /check - معلومات النظام | |
`; | |
bot.sendMessage(chatId, message.trim()); | |
log(`Admin ${userId} used /start`); | |
}); | |
// /watermark <url> <name> | |
bot.onText(/\/watermark (.+) (.+)/, async (msg, match) => { | |
const userId = msg.from.id; | |
const chatId = msg.chat.id; | |
if (!isAdmin(userId)) { | |
bot.sendMessage(chatId, '❌ غير مصرح.'); | |
log(`Unauthorized /watermark from user ${userId}`); | |
return; | |
} | |
const url = match[1].trim(); | |
const name = match[2].trim(); | |
try { | |
const filePath = await downloadWatermark(url, name); | |
bot.sendMessage(chatId, `✅ تم تحميل الشعار وحفظه باسم: ${name}.png`); | |
log(`Watermark downloaded by user ${userId}: ${filePath}`); | |
} catch (error) { | |
bot.sendMessage(chatId, `❌ خطأ في تحميل الشعار: ${error.message}`); | |
log(`Watermark download failed for user ${userId}: ${error.message}`); | |
} | |
}); | |
// /stream <fbkey> <m3u8> [watermark] [cc] | |
bot.onText(/\/stream (.+?) (.+?)(?: (.+?))?(?: (.+))?/, async (msg, match) => { | |
const userId = msg.from.id; | |
const chatId = msg.chat.id; | |
if (!isAdmin(userId)) { | |
bot.sendMessage(chatId, '❌ غير مصرح.'); | |
log(`Unauthorized /stream from user ${userId}`); | |
return; | |
} | |
const fbKey = match[1].trim(); | |
const m3u8Url = match[2].trim(); | |
const watermarkName = match[3] ? match[3].trim() : null; | |
const ccText = match[4] ? match[4].trim() : ''; | |
const rtmpsUrl = `rtmps://live-api-s.facebook.com:443/rtmp/${fbKey}`; | |
let watermarkPath = null; | |
if (watermarkName) { | |
watermarkPath = path.join(WATERMARK_DIR, `${watermarkName}.png`); | |
if (!fs.existsSync(watermarkPath)) { | |
bot.sendMessage(chatId, `❌ الشعار ${watermarkName}.png غير موجود.`); | |
log(`Watermark ${watermarkName}.png not found for user ${userId}`); | |
return; | |
} | |
} | |
if (!m3u8Url.startsWith('http') || !rtmpsUrl.startsWith('rtmps')) { | |
bot.sendMessage(chatId, '❌ رابط M3U8 أو مفتاح فيسبوك غير صالح.'); | |
log(`Invalid stream URLs from user ${userId}: ${m3u8Url}, ${rtmpsUrl}`); | |
return; | |
} | |
// Generate unique stream ID | |
let streamId; | |
do streamId = generateStreamId(); while (activeStreams.has(streamId)); | |
// Build FFmpeg command | |
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; | |
// Error detection & early failure message | |
proc.stderr.on('data', (data) => { | |
const errorText = data.toString(); | |
log(`FFmpeg stderr (Stream ${streamId}): ${errorText}`); | |
if (!hasResponded) { | |
if (errorText.includes('Server error') || errorText.includes('Invalid data')) { | |
bot.sendMessage(chatId, `❌ فشل بدء البث ${streamId}: مفتاح RTMPS غير صالح.`); | |
} else if (errorText.includes('No such file') || errorText.includes('Invalid argument')) { | |
bot.sendMessage(chatId, `❌ فشل بدء البث ${streamId}: رابط M3U8 غير صالح.`); | |
} else { | |
bot.sendMessage(chatId, `❌ فشل بدء البث ${streamId}: خطأ غير معروف.`); | |
} | |
hasResponded = true; | |
proc.kill('SIGTERM'); | |
activeStreams.delete(streamId); | |
} | |
}); | |
// On process spawn, wait 3 sec then confirm success if no errors | |
proc.on('spawn', () => { | |
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 <streamId> <new_m3u8> | |
bot.onText(/\/urlchange (\d{4}) (.+)/, (msg, match) => { | |
const userId = msg.from.id; | |
const chatId = msg.chat.id; | |
if (!isAdmin(userId)) { | |
bot.sendMessage(chatId, '❌ غير مصرح.'); | |
log(`Unauthorized /urlchange from user ${userId}`); | |
return; | |
} | |
const streamId = match[1]; | |
const newM3u8Url = match[2].trim(); | |
if (!activeStreams.has(streamId)) { | |
bot.sendMessage(chatId, `❌ لا يوجد بث برقم ${streamId}.`); | |
return; | |
} | |
if (!newM3u8Url.startsWith('http')) { | |
bot.sendMessage(chatId, '❌ رابط M3U8 جديد غير صالح.'); | |
return; | |
} | |
const stream = activeStreams.get(streamId); | |
stream.process.kill('SIGTERM'); | |
bot.sendMessage(chatId, `⏳ جاري تحديث رابط البث ${streamId}...`); | |
// Re-run stream with new URL but same other parameters | |
bot.emit('text', { | |
...msg, | |
text: `/stream ${stream.rtmpsUrl.split('/').pop()} ${newM3u8Url} ${stream.watermark || ''} ${stream.cc}`, | |
}); | |
}); | |
// /cchange <streamId> <new_cc> | |
bot.onText(/\/cchange (\d{4}) (.+)/, (msg, match) => { | |
const userId = msg.from.id; | |
const chatId = msg.chat.id; | |
if (!isAdmin(userId)) { | |
bot.sendMessage(chatId, '❌ غير مصرح.'); | |
log(`Unauthorized /cchange from user ${userId}`); | |
return; | |
} | |
const streamId = match[1]; | |
const newCcText = match[2].trim(); | |
if (!activeStreams.has(streamId)) { | |
bot.sendMessage(chatId, `❌ لا يوجد بث برقم ${streamId}.`); | |
return; | |
} | |
const stream = activeStreams.get(streamId); | |
stream.process.kill('SIGTERM'); | |
bot.sendMessage(chatId, `⏳ جاري تحديث النص المتحرك للبث ${streamId}...`); | |
// Re-run stream with new CC text but same other parameters | |
bot.emit('text', { | |
...msg, | |
text: `/stream ${stream.rtmpsUrl.split('/').pop()} ${stream.m3u8Url} ${stream.watermark || ''} ${newCcText}`, | |
}); | |
}); | |
// /stop <streamId> | |
bot.onText(/\/stop (\d{4})/, (msg, match) => { | |
const userId = msg.from.id; | |
const chatId = msg.chat.id; | |
const streamId = match[1]; | |
if (!isAdmin(userId)) { | |
bot.sendMessage(chatId, '❌ غير مصرح.'); | |
log(`Unauthorized /stop from user ${userId}`); | |
return; | |
} | |
if (!activeStreams.has(streamId)) { | |
bot.sendMessage(chatId, `❌ لا يوجد بث برقم ${streamId}.`); | |
return; | |
} | |
const stream = activeStreams.get(streamId); | |
stream.process.kill('SIGTERM'); | |
activeStreams.delete(streamId); | |
bot.sendMessage(chatId, `🛑 تم إيقاف البث ${streamId}.`); | |
log(`Stream ${streamId} stopped by user ${userId}`); | |
}); | |
// /check system info (RAM in GB, uptime, loadavg) | |
bot.onText(/\/check/, (msg) => { | |
const userId = msg.from.id; | |
const chatId = msg.chat.id; | |
if (!isAdmin(userId)) { | |
bot.sendMessage(chatId, '❌ غير مصرح.'); | |
log(`Unauthorized /check from user ${userId}`); | |
return; | |
} | |
const mem = process.memoryUsage(); | |
const totalMem = os.totalmem(); | |
const freeMem = os.freemem(); | |
const load = os.loadavg(); | |
const uptime = process.uptime(); | |
const usedGB = (mem.rss / 1024 / 1024 / 1024).toFixed(2); | |
const freeGB = (freeMem / 1024 / 1024 / 1024).toFixed(2); | |
const totalGB = (totalMem / 1024 / 1024 / 1024).toFixed(2); | |
bot.sendMessage(chatId, ` | |
📟 معلومات النظام: | |
- وقت التشغيل: ${(uptime / 60).toFixed(1)} دقيقة | |
- ذاكرة البوت المستخدمة: ${usedGB} جيجابايت | |
- الذاكرة الحرة للنظام: ${freeGB} جيجابايت | |
- إجمالي ذاكرة النظام: ${totalGB} جيجابايت | |
- معدل تحميل النظام: ${load.map(v => v.toFixed(2)).join(', ')} | |
`.trim()); | |
log(`System info sent to user ${userId}`); | |
}); | |
console.log(`✅ Bot started and polling...`); |