Spaces:
Running
Running
Update bot.js
Browse files
bot.js
CHANGED
@@ -1,195 +1,185 @@
|
|
1 |
const TelegramBot = require('node-telegram-bot-api');
|
2 |
-
const
|
3 |
-
const
|
4 |
-
const
|
5 |
-
|
6 |
-
|
7 |
-
const
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
const bot = new TelegramBot(TOKEN, { polling: true });
|
12 |
-
|
13 |
-
const userStreams = {}; // { chatId: { streamId: { proc, timeout } } }
|
14 |
-
const streamCounter = {}; // { chatId: number }
|
15 |
-
|
16 |
-
function getSystemSettings() {
|
17 |
-
return si.cpu()
|
18 |
-
.then(cpu => si.mem()
|
19 |
-
.then(mem => {
|
20 |
-
const cores = cpu.cores || 1;
|
21 |
-
const ramGB = mem.total / 1024 / 1024 / 1024;
|
22 |
-
|
23 |
-
let preset = 'ultrafast';
|
24 |
-
if (cores >= 8) preset = 'faster';
|
25 |
-
else if (cores >= 4) preset = 'veryfast';
|
26 |
-
|
27 |
-
let bitrate = '800k';
|
28 |
-
let resolution = '640x360';
|
29 |
-
if (ramGB >= 8) {
|
30 |
-
bitrate = '3000k';
|
31 |
-
resolution = '1280x720';
|
32 |
-
} else if (ramGB >= 4) {
|
33 |
-
bitrate = '1500k';
|
34 |
-
resolution = '854x480';
|
35 |
-
}
|
36 |
-
return { preset, bitrate, resolution };
|
37 |
-
})
|
38 |
-
);
|
39 |
}
|
|
|
40 |
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
`/check\n` +
|
52 |
-
`لعرض معلومات النظام`
|
53 |
-
);
|
54 |
}
|
55 |
|
56 |
-
//
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
if (!argsText) return usageExample(chatId);
|
61 |
-
|
62 |
-
const args = argsText.trim().split(/\s+/);
|
63 |
-
if (args.length < 2) return usageExample(chatId);
|
64 |
-
|
65 |
-
const key = args[0];
|
66 |
-
const m3u8 = args[1];
|
67 |
-
const ccText = args.slice(2).join(' ');
|
68 |
-
|
69 |
-
if (!streamCounter[chatId]) streamCounter[chatId] = 0;
|
70 |
-
streamCounter[chatId]++;
|
71 |
-
const streamId = streamCounter[chatId];
|
72 |
-
|
73 |
-
try {
|
74 |
-
const { preset, bitrate, resolution } = await getSystemSettings();
|
75 |
-
|
76 |
-
let command = ffmpeg(m3u8)
|
77 |
-
.videoCodec('libx264')
|
78 |
-
.audioCodec('aac')
|
79 |
-
.outputOptions([
|
80 |
-
`-preset ${preset}`,
|
81 |
-
`-b:v ${bitrate}`,
|
82 |
-
'-f', 'flv'
|
83 |
-
])
|
84 |
-
.size(resolution);
|
85 |
-
|
86 |
-
if (ccText && ccText.trim() !== '') {
|
87 |
-
const filter = `drawbox=x=(w-text_w)/2-10:y=h-text_h-40:w=text_w+20:h=text_h+30:[email protected]:t=max,` +
|
88 |
-
`drawtext=text='${ccText}':fontcolor=white:fontsize=24:x=(w-text_w)/2:y=h-text_h-30`;
|
89 |
-
command = command.videoFilters(filter);
|
90 |
-
}
|
91 |
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
}
|
106 |
-
}, 4 * 60 * 60 * 1000);
|
107 |
-
|
108 |
-
userStreams[chatId][streamId].timeout = timeout;
|
109 |
-
|
110 |
-
bot.sendMessage(chatId,
|
111 |
-
`✅ تم بدء البث بنجاح!\n\n` +
|
112 |
-
`▶️ معرف البث: ${streamId}\n` +
|
113 |
-
`الدقة: ${resolution}\n` +
|
114 |
-
`معدل البت: ${bitrate}\n` +
|
115 |
-
`الإعدادات: ${preset}\n` +
|
116 |
-
`⏳ سيتم إيقاف البث تلقائياً بعد 4 ساعات`
|
117 |
-
);
|
118 |
-
|
119 |
-
proc.on('error', err => {
|
120 |
-
bot.sendMessage(chatId, `❌ حدث خطأ في البث معرف ${streamId}:\n${err.message}`);
|
121 |
-
clearTimeout(timeout);
|
122 |
-
delete userStreams[chatId][streamId];
|
123 |
-
});
|
124 |
-
|
125 |
-
proc.on('exit', (code, signal) => {
|
126 |
-
if (signal === 'SIGTERM') {
|
127 |
-
bot.sendMessage(chatId, `⏹️ تم إيقاف البث معرف ${streamId}.`);
|
128 |
-
} else if (code !== 0) {
|
129 |
-
bot.sendMessage(chatId, `⚠️ البث معرف ${streamId} انتهى برمز خروج ${code}.`);
|
130 |
-
}
|
131 |
-
clearTimeout(timeout);
|
132 |
-
delete userStreams[chatId][streamId];
|
133 |
-
});
|
134 |
-
|
135 |
-
} catch (e) {
|
136 |
-
return bot.sendMessage(chatId, `❌ فشل بدء البث:\n${e.message}`);
|
137 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
138 |
});
|
139 |
|
140 |
-
// /
|
141 |
-
bot.onText(
|
|
|
142 |
const chatId = msg.chat.id;
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
return bot.sendMessage(chatId, `⚠️ لا يوجد بث نشط بالمعرف ${streamId}.`);
|
148 |
}
|
149 |
-
|
150 |
-
const
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
159 |
});
|
160 |
|
161 |
-
// /check
|
162 |
-
bot.onText(/\/check/,
|
|
|
163 |
const chatId = msg.chat.id;
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
const message = `
|
170 |
-
<b>معلومات النظام:</b>
|
171 |
-
نظام التشغيل: ${osInfo.distro} ${osInfo.release}
|
172 |
-
المعالج: ${cpu.manufacturer} ${cpu.brand} (${cpu.cores} أنوية)
|
173 |
-
الذاكرة: ${(mem.total / 1024 / 1024 / 1024).toFixed(2)} GB
|
174 |
-
`;
|
175 |
-
bot.sendMessage(chatId, message, { parse_mode: 'HTML' });
|
176 |
-
} catch (err) {
|
177 |
-
bot.sendMessage(chatId, `❌ خطأ في جلب معلومات النظام: ${err.message}`);
|
178 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
179 |
});
|
180 |
|
181 |
-
//
|
182 |
-
bot.
|
|
|
183 |
const chatId = msg.chat.id;
|
184 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
185 |
|
186 |
-
if
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
191 |
|
192 |
-
|
|
|
|
|
193 |
});
|
194 |
|
195 |
-
|
|
|
|
1 |
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 = '7899900752:AAFUdjznKaXgSkpp7eqFJjXanpb9eQVqw6E';
|
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('البوت يعمل...');
|