TheFirstOython commited on
Commit
7e95f4d
·
verified ·
1 Parent(s): 328bb06

Update bot.js

Browse files
Files changed (1) hide show
  1. bot.js +158 -136
bot.js CHANGED
@@ -2,184 +2,206 @@ const TelegramBot = require('node-telegram-bot-api');
2
  const { spawn } = require('child_process');
3
  const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path;
4
  const os = require('os');
 
 
 
5
 
6
- // Initialize Telegram bot with polling
7
- const token = '8197567488:AAHqTu7Dko_iD3gp0khj_i2R7LnknGX7gs4';
8
- if (!token) {
9
- console.error('خطأ: يجب تعيين TELEGRAM_BOT_TOKEN في الكود');
10
- process.exit(1);
11
- }
12
- const bot = new TelegramBot(token, { polling: true });
13
 
14
- // Admin user ID (replace with your actual user ID)
15
- const ADMIN_USER_ID = '7708913693'; // TODO: Replace with your Telegram user ID
16
 
17
- // Store active streams: { id: { process: childProcess, chatId: number, rtmpsUrl: string } }
18
- const activeStreams = new Map();
 
 
 
19
 
20
- // Helper function to log with timestamp
21
- function logToConsole(message) {
22
- const timestamp = new Date().toISOString();
23
- console.log(`[${timestamp}] ${message}`);
24
  }
25
 
26
- // Generate a random 4-digit ID
27
  function generateStreamId() {
28
  return Math.floor(1000 + Math.random() * 9000).toString();
29
  }
30
 
31
- // Check if user is admin
32
- function isAdmin(userId) {
33
- return userId.toString() === ADMIN_USER_ID;
 
 
 
 
 
 
34
  }
35
 
36
- // Handle /start command
37
  bot.onText(/\/start/, (msg) => {
38
- const userId = msg.from.id;
39
- const chatId = msg.chat.id;
40
- if (!isAdmin(userId)) {
41
- bot.sendMessage(chatId, 'خطأ: لست مخولًا لاستخدام هذا البوت');
42
- logToConsole(`Unauthorized user ${userId} sent /start`);
43
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
45
- const message = 'مرحبًا! أنا بوت بث الفيديو. استخدم الأوامر التالية:\n' +
46
- '/stream <مفتاح_فيسبوك> <رابط_m3u8> - لبدء البث\n' +
47
- '/check - لعرض معلومات النظام\n' +
48
- '/stop <رقم_البث> - لإيقاف البث';
49
- bot.sendMessage(chatId, message);
50
- logToConsole(`User ${userId} sent /start`);
51
  });
52
 
53
- // Handle /stream <fbkey> <m3u8>
54
- bot.onText(/\/stream (.+) (.+)/, (msg, match) => {
55
- const userId = msg.from.id;
56
  const chatId = msg.chat.id;
57
- if (!isAdmin(userId)) {
58
- bot.sendMessage(chatId, 'خطأ: لست مخولًا لاستخدام هذا البوت');
59
- logToConsole(`Unauthorized user ${userId} attempted /stream`);
60
- return;
 
 
 
 
 
 
 
 
61
  }
62
- const fbKey = match[1].trim();
63
- const m3u8Url = match[2].trim();
64
- const rtmpsUrl = `rtmps://live-api-s.facebook.com:443/rtmp/${fbKey}`;
65
 
66
- // Validate inputs
67
  if (!m3u8Url.startsWith('http') || !rtmpsUrl.startsWith('rtmps')) {
68
- bot.sendMessage(chatId, 'خطأ: رابط M3U8 أو RTMPS غير صالح');
69
- logToConsole(`Invalid URLs for user ${userId}: ${m3u8Url}, ${rtmpsUrl}`);
70
- return;
71
  }
72
 
73
- // Generate unique stream ID
74
  let streamId;
75
- do {
76
- streamId = generateStreamId();
77
- } while (activeStreams.has(streamId));
78
-
79
- // Single-line FFmpeg command with reconnect and 5-second delay
80
- 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}"`;
81
- logToConsole(`Executing FFmpeg for Stream ${streamId}: ${ffmpegCommand}`);
82
 
83
- // Start FFmpeg process
84
- const ffmpegProcess = spawn(ffmpegCommand, { shell: true });
85
-
86
- ffmpegProcess.stdout.on('data', (data) => {
87
- logToConsole(`FFmpeg stdout (Stream ${streamId}): ${data}`);
88
- });
89
-
90
- ffmpegProcess.stderr.on('data', (data) => {
91
- logToConsole(`FFmpeg stderr (Stream ${streamId}): ${data}`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  });
93
 
94
- ffmpegProcess.on('spawn', () => {
95
- bot.sendMessage(chatId, `تم بدء البث برقم تعريف: ${streamId}`);
96
- activeStreams.set(streamId, { process: ffmpegProcess, chatId, rtmpsUrl });
97
- logToConsole(`Stream ${streamId} started for user ${userId}`);
 
 
 
 
 
 
98
  });
99
 
100
- ffmpegProcess.on('error', (err) => {
101
- bot.sendMessage(chatId, `فشل البث ${streamId}: ${err.message}`);
102
- logToConsole(`FFmpeg error (Stream ${streamId}): ${err.message}`);
103
- activeStreams.delete(streamId);
 
 
 
 
104
  });
105
 
106
- ffmpegProcess.on('close', (code) => {
107
- bot.sendMessage(chatId, `انتهى البث ${streamId} (رمز الخروج: ${code}).`);
108
- logToConsole(`FFmpeg closed (Stream ${streamId}) with code ${code}`);
 
 
109
  activeStreams.delete(streamId);
 
110
  });
111
  });
112
 
113
- // Handle /check
114
- bot.onText(/\/check/, (msg) => {
115
- const userId = msg.from.id;
116
- const chatId = msg.chat.id;
117
- if (!isAdmin(userId)) {
118
- bot.sendMessage(chatId, 'خطأ: لست مخولًا لاستخدام هذا البوت');
119
- logToConsole(`Unauthorized user ${userId} attempted /check`);
120
- return;
121
- }
122
- const systemInfo = `
123
- معلومات النظام:
124
- - استخدام المعالج: ${Math.round(os.loadavg()[0] * 100) / 100}% (متوسط دقيقة واحدة)
125
- - الذاكرة الحرة: ${(os.freemem() / 1024 / 1024 / 1024).toFixed(2)} غيغابايت
126
- - إجمالي الذاكرة: ${(os.totalmem() / 1024 / 1024 / 1024).toFixed(2)} غيغابايت
127
- - البثوث النشطة: ${activeStreams.size}
128
- `;
129
- bot.sendMessage(chatId, systemInfo);
130
- logToConsole(`User ${userId} requested /check`);
131
- });
132
-
133
- // Handle /stop <number>
134
- bot.onText(/\/stop (\d{4})/, (msg, match) => {
135
- const userId = msg.from.id;
136
- const chatId = msg.chat.id;
137
- if (!isAdmin(userId)) {
138
- bot.sendMessage(chatId, 'خطأ: لست مخولًا لاستخدام هذا البوت');
139
- logToConsole(`Unauthorized user ${userId} attempted /stop ${match[1]}`);
140
- return;
141
- }
142
- const streamId = match[1];
143
 
144
- if (!activeStreams.has(streamId)) {
145
- bot.sendMessage(chatId, `لا يوجد بث برقم تعريف: ${streamId}`);
146
- logToConsole(`User ${userId} tried to stop non-existent stream ${streamId}`);
147
- return;
148
- }
 
149
 
150
- const stream = activeStreams.get(streamId);
151
- logToConsole(`Attempting to stop FFmpeg process for Stream ${streamId} (PID: ${stream.process.pid})`);
 
 
 
 
152
 
153
- // Try SIGTERM first, then SIGKILL after 5 seconds if not terminated
154
  stream.process.kill('SIGTERM');
155
- const timeout = setTimeout(() => {
156
- if (activeStreams.has(streamId)) {
157
- stream.process.kill('SIGKILL');
158
- logToConsole(`Force-killed FFmpeg process for Stream ${streamId} (PID: ${stream.process.pid})`);
159
- }
160
- }, 5000);
161
-
162
- stream.process.on('close', (code) => {
163
- clearTimeout(timeout); // Cancel SIGKILL if process closes
164
- bot.sendMessage(chatId, `انتهى البث ${streamId} (رمز الخروج: ${code}).`);
165
- logToConsole(`FFmpeg closed (Stream ${streamId}) with code ${code}`);
166
- activeStreams.delete(streamId);
167
  });
168
-
169
- bot.sendMessage(chatId, `تم إيقاف البث ${streamId}.`);
170
- logToConsole(`User ${userId} stopped stream ${streamId}`);
171
- activeStreams.delete(streamId); // Ensure cleanup even if close event is delayed
172
  });
173
 
174
- // Log errors
175
- bot.on('error', (err) => {
176
- logToConsole(`Bot error: ${err.message}`);
 
 
 
 
 
 
 
177
  });
178
 
179
- // Handle polling errors
180
- bot.on('polling_error', (err) => {
181
- logToConsole(`Polling error: ${err.message}`);
 
 
 
 
 
 
 
182
  });
183
 
184
- logToConsole('Bot started');
185
- console.log('البوت يعمل...');
 
2
  const { spawn } = require('child_process');
3
  const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path;
4
  const os = require('os');
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const axios = require('axios');
8
 
9
+ const token = '8141535657:AAHpD6GNWncBD9lEdfEuiJExwEVuROIodAI';
10
+ const ADMIN_USER_ID = '7708913693';
11
+ const WATERMARK_DIR = path.join(__dirname, 'watermarks');
12
+ const activeStreams = new Map();
 
 
 
13
 
14
+ if (!fs.existsSync(WATERMARK_DIR)) fs.mkdirSync(WATERMARK_DIR);
 
15
 
16
+ const bot = new TelegramBot(token, { polling: true });
17
+
18
+ function log(msg) {
19
+ console.log(`[${new Date().toISOString()}] ${msg}`);
20
+ }
21
 
22
+ function isAdmin(id) {
23
+ return id.toString() === ADMIN_USER_ID;
 
 
24
  }
25
 
 
26
  function generateStreamId() {
27
  return Math.floor(1000 + Math.random() * 9000).toString();
28
  }
29
 
30
+ async function downloadWatermark(url, name) {
31
+ const filePath = path.join(WATERMARK_DIR, `${name}.png`);
32
+ const response = await axios({ url, method: 'GET', responseType: 'stream' });
33
+ const writer = fs.createWriteStream(filePath);
34
+ response.data.pipe(writer);
35
+ return new Promise((resolve, reject) => {
36
+ writer.on('finish', () => resolve(filePath));
37
+ writer.on('error', reject);
38
+ });
39
  }
40
 
41
+ // /start
42
  bot.onText(/\/start/, (msg) => {
43
+ const id = msg.from.id;
44
+ if (!isAdmin(id)) return bot.sendMessage(msg.chat.id, '❌ غير مصرح لك.');
45
+ bot.sendMessage(msg.chat.id, `
46
+ مرحبًا! أوامر البوت:
47
+ 🟢 /stream <fbkey> <m3u8> [watermark] [cc] - بدء البث
48
+ 📷 /watermark <url> <name> - تحميل شعار
49
+ 🔁 /urlchange <id> <new_m3u8> - تغيير المصدر
50
+ ✍️ /cchange <id> <new_text> - تغيير النص
51
+ 📴 /stop <id> - إيقاف البث
52
+ 📟 /check - معلومات النظام`);
53
+ });
54
+
55
+ // /watermark
56
+ bot.onText(/\/watermark (.+) (.+)/, async (msg, [_, url, name]) => {
57
+ if (!isAdmin(msg.from.id)) return bot.sendMessage(msg.chat.id, '❌ غير مصرح.');
58
+ try {
59
+ const filePath = await downloadWatermark(url.trim(), name.trim());
60
+ bot.sendMessage(msg.chat.id, `✅ تم تحميل الشعار: ${filePath}`);
61
+ } catch (e) {
62
+ bot.sendMessage(msg.chat.id, `❌ خطأ في التحميل: ${e.message}`);
63
  }
 
 
 
 
 
 
64
  });
65
 
66
+ // /stream with single success/failure message
67
+ bot.onText(/\/stream (.+?) (.+?)(?: (.+?))?(?: (.+))?/, async (msg, match) => {
68
+ const [_, fbKey, m3u8Url, watermarkName, ccText = ''] = match;
69
  const chatId = msg.chat.id;
70
+ const userId = msg.from.id;
71
+
72
+ if (!isAdmin(userId)) return bot.sendMessage(chatId, '❌ غير مصرح.');
73
+
74
+ const rtmpsUrl = `rtmps://live-api-s.facebook.com:443/rtmp/${fbKey.trim()}`;
75
+ let watermarkPath = null;
76
+
77
+ if (watermarkName) {
78
+ watermarkPath = path.join(WATERMARK_DIR, `${watermarkName}.png`);
79
+ if (!fs.existsSync(watermarkPath)) {
80
+ return bot.sendMessage(chatId, `❌ الشعار ${watermarkName}.png غير موجود`);
81
+ }
82
  }
 
 
 
83
 
 
84
  if (!m3u8Url.startsWith('http') || !rtmpsUrl.startsWith('rtmps')) {
85
+ return bot.sendMessage(chatId, ' رابط M3U8 أو مفتاح فيسبوك غير صالح.');
 
 
86
  }
87
 
 
88
  let streamId;
89
+ do streamId = generateStreamId(); while (activeStreams.has(streamId));
 
 
 
 
 
 
90
 
91
+ let cmd = `${ffmpegPath} -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5 -itsoffset 5 -re -i "${m3u8Url}" `;
92
+ if (watermarkPath) {
93
+ cmd += `-i "${watermarkPath}" -filter_complex "[0:v][1:v]overlay=10:10[vt];`;
94
+ } else {
95
+ cmd += `-filter_complex "[0:v]copy[vt];`;
96
+ }
97
+ 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,`;
98
+ 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]" `;
99
+ cmd += `-map "[outv]" -map 0:a -c:v libx264 -preset veryfast -b:v 3000k -c:a aac -f flv "${rtmpsUrl}"`;
100
+
101
+ const proc = spawn(cmd, { shell: true });
102
+ let hasResponded = false;
103
+
104
+ proc.stderr.on('data', (data) => {
105
+ const err = data.toString();
106
+ log(`FFmpeg stderr (Stream ${streamId}): ${err}`);
107
+
108
+ if (!hasResponded) {
109
+ if (err.includes('Server error') || err.includes('Invalid data')) {
110
+ bot.sendMessage(chatId, `❌ فشل بدء البث ${streamId}: مفتاح RTMPS غير صالح`);
111
+ } else if (err.includes('No such file') || err.includes('Invalid argument')) {
112
+ bot.sendMessage(chatId, `❌ فشل بدء البث ${streamId}: رابط M3U8 غير صالح`);
113
+ } else {
114
+ bot.sendMessage(chatId, `❌ فشل بدء البث ${streamId}: خطأ غير معروف`);
115
+ }
116
+ hasResponded = true;
117
+ proc.kill('SIGTERM');
118
+ activeStreams.delete(streamId);
119
+ }
120
  });
121
 
122
+ proc.on('spawn', () => {
123
+ // Wait 3 seconds to catch early errors before confirming success
124
+ setTimeout(() => {
125
+ if (!hasResponded) {
126
+ bot.sendMessage(chatId, `✅ تم بدء البث: ${streamId}`);
127
+ hasResponded = true;
128
+ activeStreams.set(streamId, { process: proc, chatId, rtmpsUrl, m3u8Url, cc: ccText, watermark: watermarkName });
129
+ log(`Stream ${streamId} started for user ${userId}`);
130
+ }
131
+ }, 3000);
132
  });
133
 
134
+ proc.on('close', (code) => {
135
+ log(`FFmpeg closed (Stream ${streamId}) with code ${code}`);
136
+ if (!hasResponded && code !== 0) {
137
+ bot.sendMessage(chatId, `❌ فشل بدء البث ${streamId}: FFmpeg أغلق بالكود ${code}`);
138
+ activeStreams.delete(streamId);
139
+ } else {
140
+ activeStreams.delete(streamId);
141
+ }
142
  });
143
 
144
+ proc.on('error', (err) => {
145
+ if (!hasResponded) {
146
+ bot.sendMessage(chatId, `❌ خطأ في البث ${streamId}: ${err.message}`);
147
+ hasResponded = true;
148
+ }
149
  activeStreams.delete(streamId);
150
+ log(`FFmpeg error (Stream ${streamId}): ${err.message}`);
151
  });
152
  });
153
 
154
+ // /urlchange
155
+ bot.onText(/\/urlchange (\d{4}) (.+)/, (msg, match) => {
156
+ const [_, id, newUrl] = match;
157
+ const stream = activeStreams.get(id);
158
+ if (!isAdmin(msg.from.id)) return bot.sendMessage(msg.chat.id, '❌ غير مصرح.');
159
+ if (!stream) return bot.sendMessage(msg.chat.id, `❌ لا يوجد بث ${id}`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
+ stream.process.kill('SIGTERM');
162
+ bot.emit('text', {
163
+ ...msg,
164
+ text: `/stream ${stream.rtmpsUrl.split('/').pop()} ${newUrl} ${stream.watermark || ''} ${stream.cc}`
165
+ });
166
+ });
167
 
168
+ // /cchange
169
+ bot.onText(/\/cchange (\d{4}) (.+)/, (msg, match) => {
170
+ const [_, id, newCC] = match;
171
+ const stream = activeStreams.get(id);
172
+ if (!isAdmin(msg.from.id)) return bot.sendMessage(msg.chat.id, '❌ غير مصرح.');
173
+ if (!stream) return bot.sendMessage(msg.chat.id, `❌ لا يوجد بث ${id}`);
174
 
 
175
  stream.process.kill('SIGTERM');
176
+ bot.emit('text', {
177
+ ...msg,
178
+ text: `/stream ${stream.rtmpsUrl.split('/').pop()} ${stream.m3u8Url} ${stream.watermark || ''} ${newCC}`
 
 
 
 
 
 
 
 
 
179
  });
 
 
 
 
180
  });
181
 
182
+ // /stop
183
+ bot.onText(/\/stop (\d{4})/, (msg, match) => {
184
+ const id = match[1];
185
+ const stream = activeStreams.get(id);
186
+ if (!isAdmin(msg.from.id)) return bot.sendMessage(msg.chat.id, '❌ غير مصرح.');
187
+ if (!stream) return bot.sendMessage(msg.chat.id, `❌ لا يوجد بث ${id}`);
188
+
189
+ stream.process.kill('SIGTERM');
190
+ activeStreams.delete(id);
191
+ bot.sendMessage(msg.chat.id, `🛑 تم إيقاف البث ${id}`);
192
  });
193
 
194
+ // /check
195
+ bot.onText(/\/check/, (msg) => {
196
+ if (!isAdmin(msg.from.id)) return bot.sendMessage(msg.chat.id, '❌ غير مصرح.');
197
+ const mem = process.memoryUsage();
198
+ const load = os.loadavg();
199
+ const uptime = process.uptime();
200
+ bot.sendMessage(msg.chat.id, `📟 النظام:
201
+ - Uptime: ${(uptime / 60).toFixed(1)} min
202
+ - RAM: ${(mem.rss / 1024 / 1024).toFixed(1)} MB
203
+ - Load Avg: ${load.map(v => v.toFixed(2)).join(', ')}`);
204
  });
205
 
206
+ // Bot startup log
207
+ console.log(`✅ Bot is running and polling for updates...`);