File size: 6,869 Bytes
6af76e3
2d70f5d
 
 
 
 
 
 
 
 
6af76e3
2d70f5d
6af76e3
2d70f5d
 
 
 
 
 
 
 
 
 
6af76e3
 
2d70f5d
 
 
 
6af76e3
2d70f5d
 
 
 
 
 
 
 
 
 
 
 
 
6af76e3
2d70f5d
 
 
 
 
 
6af76e3
 
2d70f5d
 
 
6af76e3
2d70f5d
 
 
 
6af76e3
2d70f5d
 
 
 
 
 
 
 
 
6af76e3
2d70f5d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6af76e3
 
2d70f5d
 
 
6af76e3
2d70f5d
 
 
 
6af76e3
2d70f5d
 
 
 
 
 
 
 
 
6af76e3
 
2d70f5d
 
 
6af76e3
2d70f5d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6af76e3
2d70f5d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6af76e3
2d70f5d
 
 
6af76e3
 
2d70f5d
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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('البوت يعمل...');