/** * ╔══════════════════════════════════════════════════════════════════════════╗ * ║ ║ * ║ ████████╗██╗██╗ ██╗████████╗ ██████╗ ██╗ ██╗ ██████╗ █████╗ ║ * ║ ╚══██╔══╝██║██║ ██╔╝╚══██╔══╝██╔═══██╗██║ ██╔╝ ██╔══██╗██╔══██╗ ║ * ║ ██║ ██║█████╔╝ ██║ ██║ ██║█████╔╝ ██████╔╝███████║ ║ * ║ ██║ ██║██╔═██╗ ██║ ██║ ██║██╔═██╗ ██╔══██╗██╔══██║ ║ * ║ ██║ ██║██║ ██╗ ██║ ╚██████╔╝██║ ██╗ ██║ ██║██║ ██║ ║ * ║ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ║ * ║ ║ * ║ 🎯 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) => `
v8.0 Premium Edition
@${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!');