/** * ╔══════════════════════════════════════════════════════════════════════════╗ * ║ ║ * ║ ████████╗██╗██╗ ██╗████████╗ ██████╗ ██╗ ██╗ ██████╗ █████╗ ║ * ║ ╚══██╔══╝██║██║ ██╔╝╚══██╔══╝██╔═══██╗██║ ██╔╝ ██╔══██╗██╔══██╗ ║ * ║ ██║ ██║█████╔╝ ██║ ██║ ██║█████╔╝ ██████╔╝███████║ ║ * ║ ██║ ██║██╔═██╗ ██║ ██║ ██║██╔═██╗ ██╔══██╗██╔══██║ ║ * ║ ██║ ██║██║ ██╗ ██║ ╚██████╔╝██║ ██╗ ██║ ██║██║ ██║ ║ * ║ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ║ * ║ ║ * ║ 🎯 TikTok Radar Bot v8.0 Premium 🎯 ║ * ║ Based on Official Documentation ║ * ║ https://github.com/zerodytrash/TikTok-Live-Connector ║ * ║ ║ * ╚══════════════════════════════════════════════════════════════════════════╝ */ const http = require('http'); const axios = require('axios'); // ═══════════════════════════════════════════════════════════════════════════ // 🔐 AUTHENTICATION SETUP // ═══════════════════════════════════════════════════════════════════════════ process.env.WHITELIST_AUTHENTICATED_SESSION_ID_HOST = 'tiktok.eulerstream.com'; const PORT = process.env.PORT || 7860; // ═══════════════════════════════════════════════════════════════════════════ // ⚙️ CONFIGURATION // ═══════════════════════════════════════════════════════════════════════════ const CONFIG = { TIKTOK_USERNAME: (process.env.TIKTOK_USERNAME || 'a200_89').trim(), SESSION_ID: (process.env.SESSION_ID || '').trim(), TT_TARGET_IDC: (process.env.TT_TARGET_IDC || 'alisg').trim(), TELEGRAM_TOKEN: (process.env.TELEGRAM_TOKEN || '').trim(), TELEGRAM_CHAT_ID: (process.env.TELEGRAM_CHAT_ID || '').trim(), PROXY_URL: (process.env.PROXY_URL || '').trim().replace(/\/$/, ''), REPORT_INTERVAL: parseInt(process.env.REPORT_INTERVAL || '10'), }; // ═══════════════════════════════════════════════════════════════════════════ // 📊 STATE VARIABLES // ═══════════════════════════════════════════════════════════════════════════ let isConnected = false; let tiktokConnection = null; let totalGifts = 0; let totalDiamonds = 0; let totalLikes = 0; let totalComments = 0; let totalFollows = 0; let totalShares = 0; let maxViewers = 0; let currentViewers = 0; let startTime = null; let giftersMap = {}; let followersMap = {}; let reconnectTimer = null; let reportInterval = null; // ⚔️ PK Battle State let pkActive = false; let pkInfo = { battleId: '', participants: [], participantDetails: [], ourScore: 0, theirScore: 0, startTime: null, pkGifts: {}, }; // ═══════════════════════════════════════════════════════════════════════════ // 🌐 HTTP SERVER // ═══════════════════════════════════════════════════════════════════════════ const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); const topGiftersHtml = Object.values(giftersMap) .sort((a, b) => b.coins - a.coins) .slice(0, 5) .map((g, i) => `
${['🥇','🥈','🥉','🏅','🎖'][i]} ${g.name}: ${g.coins}💎
`) .join('') || '
لا يوجد داعمين بعد
'; res.end(` 📡 TikTok Radar v8.0

📡 TikTok Radar

v8.0 Premium Edition

${isConnected ? '🟢 متصل بالبث المباشر' : '🔴 في انتظار البث'}
👤 @${CONFIG.TIKTOK_USERNAME}
⏰ ${new Date().toLocaleString('ar-IQ', {timeZone:'Asia/Baghdad'})}
${isConnected ? `
👁 ${currentViewers}
المشاهدين
🔝 ${maxViewers}
الأعلى
💎 ${totalDiamonds}
الماسات
🎁 ${totalGifts}
الهدايا
❤️ ${totalLikes}
الإعجابات
💬 ${totalComments}
التعليقات

🏆 أفضل الداعمين

${topGiftersHtml}
${pkActive ? '
⚔️ جولة PK نشطة!
' : ''} ` : ''}
`); }); server.listen(PORT, '0.0.0.0', () => { console.log(''); console.log('╔══════════════════════════════════════════════════════════════╗'); console.log('║ 🎯 TikTok Radar Bot v8.0 Premium Edition 🎯 ║'); console.log('╠══════════════════════════════════════════════════════════════╣'); console.log(`║ 🌐 Server: http://localhost:${PORT} ║`); console.log(`║ 👤 TikTok: @${CONFIG.TIKTOK_USERNAME.padEnd(40)}║`); console.log(`║ 📱 Telegram: ${CONFIG.TELEGRAM_TOKEN ? '✅ Connected' : '❌ Not Set'} ║`); console.log(`║ 🔑 Session: ${CONFIG.SESSION_ID ? '✅ Authenticated' : '⚠️ Public Mode'} ║`); console.log(`║ 🌐 Proxy: ${CONFIG.PROXY_URL ? '✅ Enabled' : '❌ Direct'} ║`); console.log('╚══════════════════════════════════════════════════════════════╝'); console.log(''); setTimeout(startBot, 2000); }); // ═══════════════════════════════════════════════════════════════════════════ // 📱 TELEGRAM API (Direct & Buttons) // ═══════════════════════════════════════════════════════════════════════════ async function sendTg(text, silent = false, buttons = null) { if (!CONFIG.TELEGRAM_TOKEN || !CONFIG.TELEGRAM_CHAT_ID) return false; const urls = []; if (CONFIG.PROXY_URL) urls.push(`${CONFIG.PROXY_URL}/bot${CONFIG.TELEGRAM_TOKEN}`); urls.push(`https://api.telegram.org/bot${CONFIG.TELEGRAM_TOKEN}`); for (const baseUrl of urls) { try { const payload = { chat_id: CONFIG.TELEGRAM_CHAT_ID, text, parse_mode: 'HTML', disable_notification: silent, disable_web_page_preview: true }; if (buttons) payload.reply_markup = { inline_keyboard: buttons }; const res = await axios.post(`${baseUrl}/sendMessage`, payload, { timeout: 15000 }); if (res.data?.ok) { console.log(`[Telegram] ✅ Message sent successfully`); return true; } } catch (e) { console.log(`[Telegram] ⚠️ Failed to send message`); } } return false; } // ═══════════════════════════════════════════════════════════════════════════ // 🛠️ UTILITY FUNCTIONS // ═══════════════════════════════════════════════════════════════════════════ function esc(t) { return t ? String(t).replace(/&/g,'&').replace(//g,'>') : ''; } function formatK(n) { n = Number(n) || 0; if (n >= 1000000) return (n/1000000).toFixed(1) + 'M'; if (n >= 1000) return (n/1000).toFixed(1) + 'K'; return n.toString(); } function formatDuration(s) { if (!s) return '00:00:00'; const d = Math.floor((Date.now() - s.getTime()) / 1000); const h = Math.floor(d / 3600); const m = Math.floor((d % 3600) / 60); const sec = d % 60; return `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:${String(sec).padStart(2,'0')}`; } function getTime() { return new Date().toLocaleString('ar-IQ', { timeZone: 'Asia/Baghdad', hour: '2-digit', minute: '2-digit', hour12: true }); } function getDate() { return new Date().toLocaleString('ar-IQ', { timeZone: 'Asia/Baghdad', year: 'numeric', month: '2-digit', day: '2-digit' }); } // 🏆 Top Gifters Formatter function getTopGifters(limit = 5) { const gifters = Object.values(giftersMap) .sort((a, b) => b.coins - a.coins) .slice(0, limit); if (gifters.length === 0) return '• لا يوجد داعمين بعد'; const medals = ['🥇', '🥈', '🥉', '🏅', '🎖️', '⭐', '✨', '💫', '🌟', '⚡']; return gifters.map((g, i) => { const medal = medals[i] || '•'; const name = esc(g.name); const username = g.username ? `\n ┗ @${g.username}` : ''; const coins = formatK(g.coins); return `${medal} ${name}: ${coins} 💎${username}`; }).join('\n\n'); } // ➕ Top Followers Formatter function getTopFollowers(limit = 5) { const followers = Object.values(followersMap) .sort((a, b) => b.time - a.time) .slice(0, limit); if (followers.length === 0) return '• لا يوجد متابعين جدد'; return followers.map((f, i) => { const name = esc(f.name); const username = f.username ? `@${f.username}` : ''; return `${i + 1}. ${name}\n ┗ ${username || 'يوزر غير معروف'}`; }).join('\n\n'); } // ═══════════════════════════════════════════════════════════════════════════ // 📊 PERIODIC REPORTS // ═══════════════════════════════════════════════════════════════════════════ function startReports() { if (reportInterval) clearInterval(reportInterval); reportInterval = setInterval(() => { if (!isConnected) return; const liveLink = `https://www.tiktok.com/@${CONFIG.TIKTOK_USERNAME}/live`; const msg = `📊 تقرير البث المباشر\n` + `━━━━━━━━━━━━━━━━━━━━━━\n\n` + `👤 المُضيف: ${CONFIG.TIKTOK_USERNAME}\n` + `🔗 @${CONFIG.TIKTOK_USERNAME}\n\n` + `⏱ المدة: ${formatDuration(startTime)}\n` + `📅 التاريخ: ${getDate()}\n\n` + `━━━━ 📈 الإحصائيات ━━━━\n\n` + `👁 المشاهدين الحاليين: ${formatK(currentViewers)}\n` + `🔝 أعلى مشاهدة: ${formatK(maxViewers)}\n` + `💎 إجمالي الماسات: ${formatK(totalDiamonds)}\n` + `🎁 عدد الهدايا: ${formatK(totalGifts)}\n` + `❤️ الإعجابات: ${formatK(totalLikes)}\n` + `💬 التعليقات: ${formatK(totalComments)}\n` + `➕ المتابعين الجدد: ${formatK(totalFollows)}\n` + `↪️ المشاركات: ${formatK(totalShares)}\n\n` + `━━━━ 🏆 الداعمين ━━━━\n\n` + `${getTopGifters(5)}`; sendTg(msg, false, [ [{ text: '🔴 دخول البث المباشر', url: liveLink }] ]); }, CONFIG.REPORT_INTERVAL * 60000); } // ═══════════════════════════════════════════════════════════════════════════ // 🤖 MAIN BOT // ═══════════════════════════════════════════════════════════════════════════ async function startBot() { console.log(`\n[Bot] 🔎 Searching for @${CONFIG.TIKTOK_USERNAME}...`); let TikTokLiveConnection, WebcastEvent, ControlEvent; try { const lib = require('tiktok-live-connector'); TikTokLiveConnection = lib.TikTokLiveConnection; WebcastEvent = lib.WebcastEvent; ControlEvent = lib.ControlEvent; console.log('[Bot] ✅ Library loaded successfully'); } catch (e) { console.error('[Bot] ❌ Library error:', e.message); setTimeout(startBot, 30000); return; } try { const options = { processInitialData: true, enableExtendedGiftInfo: true, fetchRoomInfoOnConnect: true, }; if (CONFIG.SESSION_ID && CONFIG.SESSION_ID.length > 10) { options.session = { cookie: { type: 'cookie', value: { sessionId: CONFIG.SESSION_ID, ttTargetIdc: CONFIG.TT_TARGET_IDC } } }; options.authenticateWs = true; console.log('[Bot] 🔑 Using authenticated connection'); } else { console.log('[Bot] 🔓 Using public connection'); } tiktokConnection = new TikTokLiveConnection(CONFIG.TIKTOK_USERNAME, options); // ═══════════════════════════════════════════════════════════════ // 🎮 CONTROL EVENTS // ═══════════════════════════════════════════════════════════════ // ✅ Connected tiktokConnection.on(ControlEvent.CONNECTED, (state) => { isConnected = true; startTime = new Date(); // Reset all stats totalGifts = 0; totalDiamonds = 0; totalLikes = 0; totalComments = 0; totalFollows = 0; totalShares = 0; maxViewers = 0; currentViewers = 0; giftersMap = {}; followersMap = {}; pkActive = false; console.log(`[Bot] ✅ Connected to room: ${state.roomId}`); const liveLink = `https://www.tiktok.com/@${CONFIG.TIKTOK_USERNAME}/live`; sendTg( `📡 البث المباشر بدأ الآن!\n` + `━━━━━━━━━━━━━━━━━━━━━━\n\n` + `🎬 معلومات البث:\n\n` + `👤 المُضيف: ${CONFIG.TIKTOK_USERNAME}\n` + `🔗 اليوزر: @${CONFIG.TIKTOK_USERNAME}\n` + `⏰ وقت البدء: ${getTime()}\n` + `📅 التاريخ: ${getDate()}\n\n` + `━━━━━━━━━━━━━━━━━━━━━━\n` + `🎉 البث بدأ! انضم الآن وادعم!`, false, [ [{ text: '🔴 دخول البث المباشر', url: liveLink }], [{ text: '📊 الإحصائيات', url: `https://www.tiktok.com/@${CONFIG.TIKTOK_USERNAME}` }] ] ); startReports(); }); // 📴 Disconnected tiktokConnection.on(ControlEvent.DISCONNECTED, ({ code, reason }) => { isConnected = false; console.log(`[Bot] 📴 Disconnected (code: ${code}, reason: ${reason || 'unknown'})`); if (reportInterval) clearInterval(reportInterval); scheduleReconnect(30000); }); // ⚠️ Error tiktokConnection.on(ControlEvent.ERROR, (err) => { console.error('[Bot] ⚠️ Error:', err?.info || err?.message || err); }); // ═══════════════════════════════════════════════════════════════ // 📺 WEBCAST EVENTS // ═══════════════════════════════════════════════════════════════ // 🔴 Stream End tiktokConnection.on(WebcastEvent.STREAM_END, ({ action }) => { isConnected = false; console.log(`[Bot] 🔴 Stream ended (action: ${action})`); if (reportInterval) clearInterval(reportInterval); sendTg( `🔴 انتهى البث المباشر\n` + `━━━━━━━━━━━━━━━━━━━━━━\n\n` + `👤 المُضيف: ${CONFIG.TIKTOK_USERNAME}\n` + `🔗 @${CONFIG.TIKTOK_USERNAME}\n\n` + `━━━━ 📊 ملخص البث ━━━━\n\n` + `⏱ مدة البث: ${formatDuration(startTime)}\n` + `👁 أعلى مشاهدة: ${formatK(maxViewers)}\n` + `💎 إجمالي الماسات: ${formatK(totalDiamonds)}\n` + `🎁 عدد الهدايا: ${formatK(totalGifts)}\n` + `❤️ الإعجابات: ${formatK(totalLikes)}\n` + `💬 التعليقات: ${formatK(totalComments)}\n` + `➕ المتابعين الجدد: ${formatK(totalFollows)}\n\n` + `━━━━ 🏆 أفضل الداعمين ━━━━\n\n` + `${getTopGifters(5)}\n\n` + `━━━━━━━━━━━━━━━━━━━━━━\n` + `🙏 شكراً لكل من حضر ودعم!` ); scheduleReconnect(60000); }); // 💬 Chat tiktokConnection.on(WebcastEvent.CHAT, (data) => { totalComments++; const nickname = data.user?.nickname || data.user?.uniqueId || '?'; const comment = data.comment || ''; console.log(`[Chat] 💬 ${nickname}: ${comment}`); }); // 🎁 Gift - FIXED! tiktokConnection.on(WebcastEvent.GIFT, (data) => { // User info const uniqueId = data.user?.uniqueId || data.uniqueId || ''; const oderId = data.user?.userId || data.userId || uniqueId || 'unknown'; const nickname = data.user?.nickname || data.nickname || uniqueId || '?'; // Gift info - check ALL possible sources const giftId = data.giftId || data.gift?.gift_id || 0; const giftName = data.giftName || data.extendedGiftInfo?.name || data.gift?.name || data.describe?.replace('Sent ', '') || `Gift#${giftId}`; // Gift type const giftType = data.giftType ?? data.gift?.gift_type ?? 0; const repeatCount = data.repeatCount ?? data.gift?.repeat_count ?? 1; const repeatEnd = data.repeatEnd ?? data.gift?.repeat_end ?? true; // 💎 Diamond count - FIXED: Check all sources correctly let diamondCount = 0; // Priority order for finding diamond count if (data.diamondCount && data.diamondCount > 0) { diamondCount = data.diamondCount; } else if (data.extendedGiftInfo?.diamondCount && data.extendedGiftInfo.diamondCount > 0) { diamondCount = data.extendedGiftInfo.diamondCount; } else if (data.extendedGiftInfo?.diamond_count && data.extendedGiftInfo.diamond_count > 0) { diamondCount = data.extendedGiftInfo.diamond_count; } else if (data.gift?.diamond_count && data.gift.diamond_count > 0) { diamondCount = data.gift.diamond_count; } else if (data.giftDetails?.diamondCount && data.giftDetails.diamondCount > 0) { diamondCount = data.giftDetails.diamondCount; } // Log for debugging console.log(`[Gift] 🎁 ${nickname} -> ${giftName}`); console.log(`[Gift] 💎 Diamonds: ${diamondCount} x ${repeatCount} = ${diamondCount * repeatCount}`); console.log(`[Gift] Type: ${giftType}, RepeatEnd: ${repeatEnd}`); // Skip streak gifts until they end if (giftType === 1 && repeatEnd === false) { console.log(`[Gift] ⏳ Streak in progress...`); return; } // Calculate total const totalCost = diamondCount * repeatCount; // Update totals totalGifts++; totalDiamonds += totalCost; // Track gifter if (!giftersMap[oderId]) { giftersMap[oderId] = { name: nickname, username: uniqueId, coins: 0, count: 0 }; } giftersMap[oderId].coins += totalCost; giftersMap[oderId].count++; // Track PK gifts if (pkActive) { if (!pkInfo.pkGifts[oderId]) { pkInfo.pkGifts[oderId] = { name: nickname, username: uniqueId, coins: 0 }; } pkInfo.pkGifts[oderId].coins += totalCost; } console.log(`[Gift] ✅ Recorded: ${nickname} = ${totalCost}💎 (Total in map: ${giftersMap[oderId].coins}💎)`); // Send notification let emoji = '🎁'; if (totalCost >= 10000) emoji = '💎🔥'; else if (totalCost >= 5000) emoji = '💎✨'; else if (totalCost >= 1000) emoji = '💎'; else if (totalCost >= 100) emoji = '🎁✨'; const userLink = uniqueId ? `\n🔗 @${uniqueId}` : ''; sendTg( `${emoji} هدية جديدة!\n` + `━━━━━━━━━━━━━━━\n\n` + `👤 ${esc(nickname)}${userLink}\n\n` + `🎁 أرسل: ${esc(giftName)}\n` + `💎 القيمة: ${formatK(totalCost)} ماسة` + (repeatCount > 1 ? ` (x${repeatCount})` : ''), totalCost < 50 ); }); // ❤️ Like tiktokConnection.on(WebcastEvent.LIKE, (data) => { const likes = data.likeCount || data.totalLikeCount || 1; totalLikes += likes; }); // ➕ Follow - Enhanced tiktokConnection.on(WebcastEvent.FOLLOW, (data) => { totalFollows++; const uniqueId = data.user?.uniqueId || ''; const nickname = data.user?.nickname || uniqueId || '?'; const oderId = data.user?.userId || uniqueId || Date.now().toString(); // Track follower followersMap[oderId] = { name: nickname, username: uniqueId, time: Date.now() }; console.log(`[Follow] ➕ ${nickname} (@${uniqueId})`); const userLink = uniqueId ? `\n🔗 @${uniqueId}` : ''; sendTg( `➕ متابع جديد!\n` + `━━━━━━━━━━━━━━━\n\n` + `👤 ${esc(nickname)}${userLink}\n\n` + `🎉 شكراً على المتابعة!`, true ); }); // ↪️ Share tiktokConnection.on(WebcastEvent.SHARE, (data) => { totalShares++; const nickname = data.user?.nickname || data.user?.uniqueId || '?'; console.log(`[Share] ↪️ ${nickname} shared the stream`); }); // 👁 Viewer Count tiktokConnection.on(WebcastEvent.ROOM_USER, (data) => { currentViewers = data.viewerCount || 0; if (currentViewers > maxViewers) maxViewers = currentViewers; }); // 👤 Member Join tiktokConnection.on(WebcastEvent.MEMBER, (data) => { const nickname = data.user?.nickname || data.user?.uniqueId || '?'; console.log(`[Member] 👤 ${nickname} joined`); }); // 📦 Envelope tiktokConnection.on(WebcastEvent.ENVELOPE, (data) => { if (data.envelopeInfo) { const sender = data.envelopeInfo.sendUserName || '?'; const diamonds = data.envelopeInfo.diamondCount || 0; console.log(`[Envelope] 📦 ${sender} sent treasure box (${diamonds}💎)`); sendTg( `📦 صندوق كنز!\n` + `━━━━━━━━━━━━━━━\n\n` + `👤 ${esc(sender)}\n` + `💎 القيمة: ${formatK(diamonds)} ماسة` ); } }); // ⚔️ PK Battle Start - Enhanced tiktokConnection.on(WebcastEvent.LINK_MIC_BATTLE, (data) => { pkActive = true; pkInfo = { battleId: data.battleId || '', participants: [], participantDetails: [], ourScore: 0, theirScore: 0, startTime: new Date(), pkGifts: {}, }; console.log(`[PK] 📦 Raw data:`, JSON.stringify(data.anchorInfo || {}).substring(0, 500)); // Extract participants const participantDetails = []; const anchorInfo = data.anchorInfo || {}; for (const [key, info] of Object.entries(anchorInfo)) { const user = info.user || info || {}; const displayId = user.displayId || user.display_id || user.uniqueId || user.unique_id || ''; const nickName = user.nickName || user.nickname || user.nick_name || displayId || 'مجهول'; const oderId = user.userId || user.user_id || user.id || key || ''; if (displayId || nickName !== 'مجهول') { participantDetails.push({ name: nickName, username: displayId, oderId: oderId, }); } } pkInfo.participantDetails = participantDetails; pkInfo.participants = participantDetails.map(p => p.name); console.log(`[PK] ⚔️ Battle started: ${data.battleId}`); console.log(`[PK] 👥 Participants:`, participantDetails); // Find opponent const host = participantDetails.find(p => p.username === CONFIG.TIKTOK_USERNAME || p.name === CONFIG.TIKTOK_USERNAME ) || participantDetails[0] || null; const opponent = participantDetails.find(p => p.username !== CONFIG.TIKTOK_USERNAME && p.name !== CONFIG.TIKTOK_USERNAME ) || participantDetails[1] || null; const liveLink = `https://www.tiktok.com/@${CONFIG.TIKTOK_USERNAME}/live`; let pkMsg = `⚔️🔥 بدأت جولة PK جديدة!\n` + `━━━━━━━━━━━━━━━━━━━━━━\n\n`; // Host info pkMsg += `🏠 صاحب البث:\n` + ` 👤 ${esc(CONFIG.TIKTOK_USERNAME)}\n` + ` 🔗 @${CONFIG.TIKTOK_USERNAME}\n\n`; pkMsg += ` 🆚\n\n`; // Opponent info if (opponent) { pkMsg += `🥊 المنافس:\n` + ` 👤 ${esc(opponent.name)}\n` + ` 🔗 @${opponent.username || 'غير معروف'}\n\n`; } else { pkMsg += `🥊 المنافس:\n` + ` ⏳ جاري تحميل البيانات...\n\n`; } pkMsg += `━━━━━━━━━━━━━━━━━━━━━━\n` + `🚀 ادعموا البث وساهموا بالفوز!\n` + `💎 كل هدية تحسب في النتيجة!`; sendTg(pkMsg, false, [ [{ text: '🔴 ادخل البث وادعم الآن!', url: liveLink }] ]); }); // 📊 PK Update tiktokConnection.on(WebcastEvent.LINK_MIC_ARMIES, (data) => { if (!pkActive) return; if (data.totalDiamondCount) { pkInfo.ourScore = data.totalDiamondCount; console.log(`[PK] 📊 Score update: ${data.totalDiamondCount}💎`); } }); // 🎬 Live Intro tiktokConnection.on(WebcastEvent.LIVE_INTRO, (data) => { const host = data.host?.nickname || data.host?.uniqueId || ''; const desc = data.description || ''; if (host || desc) { console.log(`[Intro] 🎬 Host: ${host}, Description: ${desc}`); } }); // ═══════════════════════════════════════════════════════════════ // 🔌 CONNECT // ═══════════════════════════════════════════════════════════════ console.log('[Bot] 🔌 Connecting to TikTok...'); await tiktokConnection.connect(); } catch (err) { const errMsg = err.message || String(err); console.error('[Bot] ❌ Connection failed:', errMsg); isConnected = false; if (errMsg.includes('offline') || errMsg.includes('not online')) { console.log('[Bot] 📴 User is not live, will retry...'); } scheduleReconnect(60000); } } // ═══════════════════════════════════════════════════════════════════════════ // 🔄 RECONNECTION // ═══════════════════════════════════════════════════════════════════════════ function scheduleReconnect(delay) { if (reconnectTimer) clearTimeout(reconnectTimer); console.log(`[Bot] 🔄 Reconnecting in ${delay/1000} seconds...`); reconnectTimer = setTimeout(() => { reconnectTimer = null; if (!isConnected) startBot(); }, delay); } // ═══════════════════════════════════════════════════════════════════════════ // 🛡️ ERROR HANDLING & KEEP ALIVE // ═══════════════════════════════════════════════════════════════════════════ process.on('uncaughtException', (e) => { console.error('[FATAL] Uncaught Exception:', e.message); }); process.on('unhandledRejection', (r) => { console.error('[FATAL] Unhandled Rejection:', r); }); // Keep alive ping setInterval(() => { http.get(`http://localhost:${PORT}`, r => r.resume()).on('error', () => {}); }, 60000); // Graceful shutdown process.on('SIGINT', async () => { console.log('\n[Bot] 👋 Shutting down gracefully...'); if (tiktokConnection) { try { await tiktokConnection.disconnect(); } catch(e) {} } await sendTg( `💤 البوت توقف مؤقتاً\n` + `━━━━━━━━━━━━━━━\n` + `⏰ ${getTime()}\n` + `📅 ${getDate()}` ); process.exit(0); }); console.log('[Bot] 🚀 TikTok Radar v8.0 Premium initialized!');