Spaces:
Paused
Paused
Update index.js
Browse files
index.js
CHANGED
|
@@ -12,6 +12,7 @@ const MY_PROXY_URL = "https://tg-proxy.dr-toktok00.workers.dev";
|
|
| 12 |
|
| 13 |
dns.setServers(['8.8.8.8']);
|
| 14 |
|
|
|
|
| 15 |
const COUNTRY_FLAGS = {
|
| 16 |
'SA': { name: 'السعودية', flag: '🇸🇦' },
|
| 17 |
'EG': { name: 'مصر', flag: '🇪🇬' },
|
|
@@ -81,143 +82,148 @@ const COUNTRY_FLAGS = {
|
|
| 81 |
'IE': { name: 'أيرلندا', flag: '🇮🇪' },
|
| 82 |
};
|
| 83 |
|
| 84 |
-
//
|
| 85 |
-
const
|
| 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 |
-
const
|
| 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 |
-
'golden ticket': 'تذكرة ذهبية', 'golden star': 'نجمة ذهبية',
|
| 152 |
-
'diamond ring': 'خاتم ماسي', 'diamond necklace': 'عقد ماسي',
|
| 153 |
-
'golden egg': 'بيضة ذهبية', 'golden heart': 'قلب ذهبي',
|
| 154 |
-
'crystal ball': 'كرة بلورية', 'magic book': 'كتاب سحري',
|
| 155 |
-
'tropical island': 'جزيرة استوائية', 'paradise': 'جنّة',
|
| 156 |
-
'royal carriage': 'عربة ملكية', 'castle': 'قصر',
|
| 157 |
-
'dragon flame': 'لهب التنين', 'phoenix': 'فينيक्स',
|
| 158 |
-
'mermaid': 'مرأة البحر', 'fairy': 'جنية',
|
| 159 |
-
'angel': 'ملاك', 'devil': 'شيطان',
|
| 160 |
-
'robot': 'روبوت', 'dinosaur': 'ديناصور',
|
| 161 |
-
'penguin': 'بطريق', 'panda': 'باندا', 'koala': 'كوالة',
|
| 162 |
-
'lion': 'أسد', 'eagle': 'نسر', 'wolf': 'ذئب',
|
| 163 |
-
'fox': 'ثعلب', 'rabbit': 'أرنب', 'hamster': 'هامستر',
|
| 164 |
-
'gold bar': 'سبيكة ذهب', 'gold coin': 'عملة ذهبية',
|
| 165 |
-
'money': 'مال', 'cash': 'كاش', 'dollar': 'دولار',
|
| 166 |
-
'money bag': 'كيس مال', 'money rain': 'مطر مال',
|
| 167 |
-
'diamond crown': 'تاج ماسي', 'diamond heart': 'قلب ماسي',
|
| 168 |
-
'super star': 'نجمة خارقة', 'mega star': 'نجمة عملاقة',
|
| 169 |
-
'cosmic rose': 'وردة كونية', 'stellar': 'نجمي',
|
| 170 |
-
'infinity': 'لا نهاية', 'universe': 'كون',
|
| 171 |
-
'supreme': 'علوي', 'ultimate': 'نهائي',
|
| 172 |
-
'legend': 'أسطوري', 'epic': 'ملحمي', 'mythic': 'خرافي',
|
| 173 |
-
'golden dragon': 'تنين ذهبي', 'silver dragon': 'تنين فضي',
|
| 174 |
-
'rainbow crown': 'تاج قوس قزح', 'diamond galaxy': 'مجرة ماسية',
|
| 175 |
-
'royal guard': 'حارس ملكي', 'royal': 'ملكي',
|
| 176 |
-
'neon heart': 'قلب نيون', 'neon star': 'نجمة نيون',
|
| 177 |
-
'pixel heart': 'قلب بيكسل', 'retro': 'ريترو',
|
| 178 |
-
'halo': 'هالة', 'wings': 'أجنحة',
|
| 179 |
-
'bow': 'قوس', 'arrow': 'سهم',
|
| 180 |
-
'sword': 'سيف', 'shield': 'درع',
|
| 181 |
-
'cherry': 'كرز', 'apple': 'تفاحة', 'grape': 'عنب',
|
| 182 |
-
'watermelon': 'بطيخ', 'orange': 'برتقالة', 'lemon': 'ليمون',
|
| 183 |
-
'strawberry': 'فراولة', 'banana': 'موز', 'peach': 'خوخ',
|
| 184 |
-
'cupcake': 'كعكة صغيرة', 'muffin': 'مافن', 'pie': 'فطيرة',
|
| 185 |
-
'bread': 'خبز', 'burger': 'برجر', 'fries': 'بطاطس مقلية',
|
| 186 |
-
'coffee': 'قهوة', 'tea': 'شاي', 'milk': 'حليب',
|
| 187 |
-
'beer': 'بيرة', 'cocktail': 'كوكتيل', 'whiskey': 'ويسكي',
|
| 188 |
-
'sparkler': 'شرارة', 'lantern': 'مصباح', 'candle': 'شمعة',
|
| 189 |
-
'bell': 'جرس', 'whistle': 'صفارة',
|
| 190 |
-
'balloon': 'بالون', 'ribbon': 'شريط',
|
| 191 |
-
'gift box': 'صندوق هدية', 'gift': 'هدية', 'present': 'هدية',
|
| 192 |
-
'music': 'موسيقى', 'song': 'أغنية', 'note': 'ملحوظة',
|
| 193 |
-
'paint': 'طلي', 'brush': 'فرشاة', 'palette': 'لوحة ألوان',
|
| 194 |
-
'camera': 'كاميرا', 'film': 'فيلم', 'microphone': 'ميكروفون',
|
| 195 |
-
'speaker': 'مكبر صوت', 'radio': 'راديو', 'tv': 'تلفاز',
|
| 196 |
-
'phone': 'هاتف', 'computer': 'كمبيوتر', 'gamepad': 'يد تحكم',
|
| 197 |
-
'trophy': 'كأس', 'medal': 'وسام', 'star badge': 'شارة نجمة',
|
| 198 |
-
'ribbon badge': 'شارة شريط', 'badge': 'شارة',
|
| 199 |
-
'flag': 'علم', 'banner': 'لافتة', 'balloon animal': 'بالون حيواني',
|
| 200 |
-
'party hat': 'قبعة حفلة', 'party popper': 'فرقعة حفلة',
|
| 201 |
-
'confetti': 'قصاصات ورقية', 'streamer': 'شريط ملون',
|
| 202 |
-
'clown': 'مهرج', 'jester': 'مضحك',
|
| 203 |
-
'wizard': 'ساحر', 'mage': 'كاهن',
|
| 204 |
-
'knight': 'فرس', 'ninja': 'نينجا', 'samurai': 'ساموراي',
|
| 205 |
-
'pirate': 'قراصنة', 'viking': 'فايكنغ', 'pharaoh': 'فرعون',
|
| 206 |
-
'ghost face': 'وجه شبح', 'pumpkin': 'قرعة',
|
| 207 |
-
'snowman': 'رجل ثلج', 'snowflake': 'رقصة ثلج',
|
| 208 |
-
'christmas tree': 'شجرة عيد الميلاد', 'stocking': 'جوارب عيد الميلاد',
|
| 209 |
-
'santa': 'سانتا', 'reindeer': 'أيل',
|
| 210 |
-
'easter egg': 'بيضة عيد الفصح', 'chicken': 'دجاجة',
|
| 211 |
-
'turkey': 'ديك رومي', 'corn': 'ذرة',
|
| 212 |
-
'flower': 'زهرة', 'blossom': 'ازهار', 'tulip': 'توليب',
|
| 213 |
-
'orchid': 'أوركيد', 'daisy': 'أرجوان', 'lily': 'زنبق',
|
| 214 |
-
'sunflower': 'دوار الشمس', 'daffodil': 'نرجس',
|
| 215 |
-
'palm tree': 'نخلة', 'cactus': 'صبار', 'mushroom': 'فطر',
|
| 216 |
-
'treasure': 'كنز', 'key': 'مفتاح', 'lock': 'قفل',
|
| 217 |
-
'anchor': 'مرساة', 'compass': 'بوصلة', 'map': 'خريطة',
|
| 218 |
-
'crown prince': 'ولي عهد', 'king': 'ملك', 'queen': 'ملكة',
|
| 219 |
};
|
| 220 |
|
|
|
|
| 221 |
function getCountryInfo(code) {
|
| 222 |
if (!code) return { name: 'غير محدد', flag: '🌍' };
|
| 223 |
const upperCode = code.toUpperCase();
|
|
@@ -226,7 +232,8 @@ function getCountryInfo(code) {
|
|
| 226 |
|
| 227 |
function getGiftArabicName(englishName, giftId) {
|
| 228 |
if (!englishName) {
|
| 229 |
-
|
|
|
|
| 230 |
}
|
| 231 |
const lower = englishName.toLowerCase().trim();
|
| 232 |
if (GIFT_ARABIC[lower]) return GIFT_ARABIC[lower];
|
|
@@ -234,26 +241,46 @@ function getGiftArabicName(englishName, giftId) {
|
|
| 234 |
}
|
| 235 |
|
| 236 |
function getGiftDiamonds(data) {
|
| 237 |
-
// ✅
|
| 238 |
-
const
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
}
|
| 250 |
|
| 251 |
function formatK(num) {
|
|
|
|
| 252 |
if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M';
|
| 253 |
if (num >= 1000) return (num / 1000).toFixed(1) + 'K';
|
| 254 |
return num.toString();
|
| 255 |
}
|
| 256 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
function getBaghdadTime() {
|
| 258 |
return new Intl.DateTimeFormat('ar-IQ', {
|
| 259 |
timeZone: 'Asia/Baghdad',
|
|
@@ -265,15 +292,16 @@ function getBaghdadTime() {
|
|
| 265 |
}
|
| 266 |
|
| 267 |
function generateProgressBar(ourScore, theirScore, length = 10) {
|
| 268 |
-
const total = ourScore + theirScore;
|
| 269 |
if (total === 0) return "⬜".repeat(length);
|
| 270 |
const ourPercent = Math.min(
|
| 271 |
-
Math.max(Math.round((ourScore / total) * length), 0),
|
| 272 |
length
|
| 273 |
);
|
| 274 |
return "🟦".repeat(ourPercent) + "🟥".repeat(length - ourPercent);
|
| 275 |
}
|
| 276 |
|
|
|
|
| 277 |
function httpGet(url, headers = {}, timeout = 15000) {
|
| 278 |
return new Promise((resolve, reject) => {
|
| 279 |
const urlObj = new URL(url);
|
|
@@ -284,7 +312,7 @@ function httpGet(url, headers = {}, timeout = 15000) {
|
|
| 284 |
path: urlObj.pathname + urlObj.search,
|
| 285 |
method: 'GET',
|
| 286 |
headers: {
|
| 287 |
-
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
| 288 |
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
| 289 |
'Accept-Language': 'en-US,en;q=0.9,ar;q=0.8',
|
| 290 |
'Accept-Encoding': 'gzip, deflate',
|
|
@@ -308,7 +336,7 @@ function httpPost(url, payload, timeout = 15000) {
|
|
| 308 |
return new Promise((resolve, reject) => {
|
| 309 |
const urlObj = new URL(url);
|
| 310 |
const protocol = urlObj.protocol === 'https:' ? https : http;
|
| 311 |
-
const
|
| 312 |
const options = {
|
| 313 |
hostname: urlObj.hostname,
|
| 314 |
port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
|
|
@@ -316,7 +344,7 @@ function httpPost(url, payload, timeout = 15000) {
|
|
| 316 |
method: 'POST',
|
| 317 |
headers: {
|
| 318 |
'Content-Type': 'application/json',
|
| 319 |
-
'Content-Length': Buffer.byteLength(
|
| 320 |
},
|
| 321 |
timeout: timeout
|
| 322 |
};
|
|
@@ -327,11 +355,12 @@ function httpPost(url, payload, timeout = 15000) {
|
|
| 327 |
});
|
| 328 |
req.on('error', reject);
|
| 329 |
req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
|
| 330 |
-
req.write(
|
| 331 |
req.end();
|
| 332 |
});
|
| 333 |
}
|
| 334 |
|
|
|
|
| 335 |
async function getUserDataWithRegion(username) {
|
| 336 |
try {
|
| 337 |
const response = await httpGet('https://www.tiktok.com/@' + username);
|
|
@@ -357,10 +386,18 @@ async function getUserDataWithRegion(username) {
|
|
| 357 |
return null;
|
| 358 |
}
|
| 359 |
|
| 360 |
-
|
| 361 |
-
let
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 362 |
let userCache = {};
|
| 363 |
|
|
|
|
| 364 |
async function sendToTelegram(text, photoUrl = null, buttons = null) {
|
| 365 |
try {
|
| 366 |
const payload = {
|
|
@@ -397,99 +434,104 @@ async function resolveUser(username) {
|
|
| 397 |
return null;
|
| 398 |
}
|
| 399 |
|
| 400 |
-
//
|
|
|
|
| 401 |
let tiktokConnection = new TikTokLiveConnection(TIKTOK_USERNAME, {
|
| 402 |
processInitialData: true,
|
| 403 |
enableExtendedGiftInfo: true,
|
| 404 |
-
webcastKernelVersion: 5,
|
| 405 |
clientParams: {
|
| 406 |
app_language: "ar-IQ",
|
| 407 |
device_platform: "web"
|
| 408 |
}
|
| 409 |
});
|
| 410 |
|
|
|
|
| 411 |
async function startRadar() {
|
| 412 |
console.log('⏳ جاري البحث: ' + TIKTOK_USERNAME + '...');
|
| 413 |
userCache = {};
|
| 414 |
giftersMap = {};
|
| 415 |
maxViewers = 0;
|
| 416 |
totalLikes = 0;
|
|
|
|
| 417 |
totalShares = 0;
|
| 418 |
pkActive = false;
|
|
|
|
|
|
|
| 419 |
|
| 420 |
const userData = await getUserDataWithRegion(TIKTOK_USERNAME);
|
| 421 |
const countryInfo = getCountryInfo(userData?.region);
|
| 422 |
|
| 423 |
-
|
| 424 |
-
const
|
|
|
|
| 425 |
|
| 426 |
-
|
| 427 |
let avatarUrl = '';
|
| 428 |
-
const
|
| 429 |
-
if (
|
| 430 |
-
|
| 431 |
-
if (
|
| 432 |
-
else if (
|
| 433 |
-
else if (
|
| 434 |
-
else if (
|
| 435 |
-
else if (
|
| 436 |
-
else if (
|
| 437 |
-
else if (
|
| 438 |
-
else if (
|
| 439 |
-
else if (
|
|
|
|
|
|
|
|
|
|
| 440 |
}
|
| 441 |
-
// fallback
|
| 442 |
-
if (!avatarUrl && state.roomInfo?.sharedCover) avatarUrl = state.roomInfo.sharedCover;
|
| 443 |
-
if (!avatarUrl && state.roomInfo?.coverUrl) avatarUrl = state.roomInfo.coverUrl;
|
| 444 |
|
| 445 |
const displayName = userData?.nickname || TIKTOK_USERNAME;
|
| 446 |
-
const verifiedBadge = userData?.verified ? '✅' : '';
|
| 447 |
|
| 448 |
-
// ✅ تنسيق أفخم لبداية البث
|
| 449 |
const msg = '🔴 ' + displayName + verifiedBadge + '\n\n' +
|
| 450 |
'🌐 @' + TIKTOK_USERNAME + '\n' +
|
| 451 |
countryInfo.flag + ' ' + countryInfo.name + '\n' +
|
| 452 |
'⏰ ' + getBaghdadTime();
|
| 453 |
|
| 454 |
sendToTelegram(msg, avatarUrl || undefined, [[{ text: "📺 دخول البث المباشر", url: liveLink }]]);
|
| 455 |
-
|
| 456 |
-
console.log('✅ متصل! البلد: ' + countryInfo.name);
|
| 457 |
console.log('📸 صورة: ' + (avatarUrl ? 'متوفرة' : 'غير متوفرة'));
|
| 458 |
-
}
|
| 459 |
-
console.log('🔎 مغلق: ' + err.message);
|
| 460 |
setTimeout(startRadar, 60000);
|
| 461 |
-
}
|
| 462 |
}
|
| 463 |
|
| 464 |
-
//
|
|
|
|
|
|
|
| 465 |
tiktokConnection.on(WebcastEvent.ROOM_USER, data => {
|
| 466 |
const count = data.viewerCount || 0;
|
| 467 |
if (count > maxViewers) maxViewers = count;
|
| 468 |
});
|
| 469 |
|
|
|
|
| 470 |
tiktokConnection.on(WebcastEvent.LIKE, data => {
|
| 471 |
-
|
|
|
|
|
|
|
| 472 |
if (pkActive) {
|
| 473 |
-
pkInfo.ourLikes +=
|
| 474 |
}
|
| 475 |
});
|
| 476 |
|
| 477 |
-
// ✅ مشاركة
|
| 478 |
tiktokConnection.on(WebcastEvent.SOCIAL, data => {
|
| 479 |
totalShares++;
|
| 480 |
console.log('↪️ مشاركة!');
|
| 481 |
});
|
| 482 |
|
| 483 |
-
// ✅ تعليقات
|
| 484 |
tiktokConnection.on(WebcastEvent.CHAT, data => {
|
| 485 |
// لا نرسل إشعارات لتجنب السبام
|
| 486 |
});
|
| 487 |
|
| 488 |
-
// ✅ ========== الهدايا
|
| 489 |
tiktokConnection.on(WebcastEvent.GIFT, async data => {
|
| 490 |
-
// ✅ في v2: البيانات في مستوى علوي مباشرة
|
| 491 |
-
// data.uniqueId, data.nickname, data.diamondCount, data.giftName, data.giftId, etc.
|
| 492 |
-
|
| 493 |
const gifterUsername = data.uniqueId || '';
|
| 494 |
const nickname = data.nickname || '';
|
| 495 |
const giftId = data.giftId || 0;
|
|
@@ -498,30 +540,28 @@ tiktokConnection.on(WebcastEvent.GIFT, async data => {
|
|
| 498 |
const giftType = data.giftType || 0;
|
| 499 |
const repeatEnd = data.repeatEnd;
|
| 500 |
|
| 501 |
-
// تجاهل تحديثات الستريك
|
| 502 |
if (giftType === 1 && !repeatEnd) return;
|
| 503 |
|
| 504 |
-
// ✅
|
| 505 |
const baseDiamonds = getGiftDiamonds(data);
|
| 506 |
const giftCost = baseDiamonds * repeatCount;
|
| 507 |
|
| 508 |
// ✅ الاسم بالعربي
|
| 509 |
const arabicName = getGiftArabicName(giftNameEn, giftId);
|
| 510 |
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
' = ' + baseDiamonds + '×' + repeatCount + ' = ' + giftCost + ' 💎' +
|
| 514 |
-
' [dc=' + data.diamondCount + ' ext=' + (data.extendedGiftInfo?.diamond_count || 'none') + ']');
|
| 515 |
|
| 516 |
if (!gifterUsername) {
|
| 517 |
-
console.log('⚠️ هدية بدون اسم
|
| 518 |
return;
|
| 519 |
}
|
| 520 |
|
| 521 |
const userId = data.userId || gifterUsername;
|
| 522 |
if (!giftersMap[userId]) {
|
| 523 |
giftersMap[userId] = {
|
| 524 |
-
name: nickname,
|
| 525 |
username: gifterUsername,
|
| 526 |
coins: 0,
|
| 527 |
region: null,
|
|
@@ -530,21 +570,21 @@ tiktokConnection.on(WebcastEvent.GIFT, async data => {
|
|
| 530 |
}
|
| 531 |
giftersMap[userId].coins += giftCost;
|
| 532 |
|
| 533 |
-
// ✅ جلب بيانات الدولة
|
| 534 |
const userRegionData = await resolveUser(gifterUsername);
|
| 535 |
let region = null;
|
| 536 |
-
let displayName = nickname;
|
| 537 |
|
| 538 |
if (userRegionData && userRegionData.region) {
|
| 539 |
region = userRegionData.region;
|
| 540 |
-
displayName = userRegionData.nickname ||
|
| 541 |
giftersMap[userId].region = region;
|
| 542 |
}
|
| 543 |
|
| 544 |
const gc = getCountryInfo(region);
|
| 545 |
giftersMap[userId].flag = gc.flag;
|
| 546 |
|
| 547 |
-
// ✅
|
| 548 |
const repeatText = repeatCount > 1 ? ' ×' + repeatCount : '';
|
| 549 |
const msg = '🎁 ' + arabicName + repeatText + '\n\n' +
|
| 550 |
gc.flag + ' ' + displayName + '\n' +
|
|
@@ -558,16 +598,65 @@ tiktokConnection.on(WebcastEvent.GIFT, async data => {
|
|
| 558 |
]]);
|
| 559 |
});
|
| 560 |
|
| 561 |
-
// ✅
|
| 562 |
-
tiktokConnection.on(WebcastEvent.
|
| 563 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
});
|
| 565 |
|
|
|
|
| 566 |
tiktokConnection.on(WebcastEvent.DISCONNECTED, () => {
|
| 567 |
console.log('❌ تم القطع');
|
| 568 |
});
|
| 569 |
|
| 570 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 571 |
tiktokConnection.on('pkStart', data => {
|
| 572 |
pkActive = true;
|
| 573 |
pkInfo = { opponentName: data.opponentNickname || 'منافس', ourLikes: 0, theirLikes: 0 };
|
|
@@ -592,13 +681,16 @@ tiktokConnection.on('pkEnd', data => {
|
|
| 592 |
);
|
| 593 |
});
|
| 594 |
|
| 595 |
-
// ✅ نهاية البث
|
| 596 |
tiktokConnection.on('streamEnd', () => {
|
|
|
|
|
|
|
| 597 |
const top = getTopGifters(giftersMap, 5);
|
| 598 |
const medals = ['🥇', '🥈', '🥉', '4️⃣', '5️⃣'];
|
| 599 |
let topText = top.map((g, i) =>
|
| 600 |
-
medals[i] + ' ' + (g.flag || '🌍') + ' ' + g.name + '
|
| 601 |
-
|
|
|
|
| 602 |
|
| 603 |
const countryStats = {};
|
| 604 |
Object.values(giftersMap).forEach(g => {
|
|
@@ -611,21 +703,24 @@ tiktokConnection.on('streamEnd', () => {
|
|
| 611 |
.slice(0, 3)
|
| 612 |
.map(([r, c], i) =>
|
| 613 |
['👑', '🥈', '🥉'][i] + ' ' + getCountryInfo(r).flag + ' ' +
|
| 614 |
-
getCountryInfo(r).name + '
|
| 615 |
-
).join('\n') || '
|
| 616 |
|
| 617 |
sendToTelegram(
|
| 618 |
'📊 التقرير النهائي\n\n' +
|
| 619 |
-
'
|
|
|
|
| 620 |
'❤️ إعجابات: ' + formatK(totalLikes) + '\n' +
|
| 621 |
-
'↪️ مشاركات: ' + formatK(totalShares) + '\n
|
| 622 |
-
'
|
| 623 |
-
'
|
|
|
|
| 624 |
);
|
| 625 |
|
| 626 |
setTimeout(startRadar, 60000);
|
| 627 |
});
|
| 628 |
|
|
|
|
| 629 |
const server = http.createServer((req, res) => {
|
| 630 |
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
| 631 |
res.end('OK');
|
|
|
|
| 12 |
|
| 13 |
dns.setServers(['8.8.8.8']);
|
| 14 |
|
| 15 |
+
// ====== قاموس الدول ======
|
| 16 |
const COUNTRY_FLAGS = {
|
| 17 |
'SA': { name: 'السعودية', flag: '🇸🇦' },
|
| 18 |
'EG': { name: 'مصر', flag: '🇪🇬' },
|
|
|
|
| 82 |
'IE': { name: 'أيرلندا', flag: '🇮🇪' },
|
| 83 |
};
|
| 84 |
|
| 85 |
+
// ====== قاموس الهدايا بالعربي ======
|
| 86 |
+
const GIFT_ARABIC = {
|
| 87 |
+
'rose':'وردة','rosebud':'برعم ورد','smile':'ابتسامة','heart':'قلب',
|
| 88 |
+
'thumbs up':'إبهام','thumbsup':'إبهام','star':'نجمة','fire':'نار',
|
| 89 |
+
'flame':'لهب','crown':'تاج','diamond':'ماسة','tiger':'نمر',
|
| 90 |
+
'dragon':'تنين','unicorn':'أحادي القرن','rainbow':'قوس قزح',
|
| 91 |
+
'galaxy':'مجرة','meteor':'شهاب','rocket':'صاروخ','love':'حب',
|
| 92 |
+
'kiss':'قبلة','hug':'عناق','baby elephant':'فيلا صغير','elephant':'فيل',
|
| 93 |
+
'butterfly':'فراشة','sunflower':'دوار الشمس','lollipop':'حلوى',
|
| 94 |
+
'cookie':'بسكويت','donut':'دونات','ice cream':'آيس كريم','pizza':'بيتزا',
|
| 95 |
+
'champagne':'شمبانيا','wine':'نبيذ','ring':'خاتم','gem':'جوهرة',
|
| 96 |
+
'pearl':'لؤلؤ','teddy bear':'دب','cat':'قطة','dog':'كلب',
|
| 97 |
+
'parrot':'ببغاء','dove':'حمام','swan':'بط','hot air balloon':'مظلة',
|
| 98 |
+
'spaceship':'سفينة فضائية','comet':'مذنب','planet':'كوكب',
|
| 99 |
+
'moon':'قمر','sun':'شمس','ghost':'شبح','skull':'جمجمة',
|
| 100 |
+
'candy':'حلوى','chocolate':'شوكولاتة','birthday cake':'كيكة عيد ميلاد',
|
| 101 |
+
'cake':'كيكة','tshirt':'تيشيرت','sneakers':'أحذية','king':'ملك',
|
| 102 |
+
'queen':'ملكة','prince':'أمير','princess':'أميرة',
|
| 103 |
+
'rose garden':'حديقة ورود','flower garden':'حديقة زهور',
|
| 104 |
+
'treasure chest':'كنز','lightning':'برق','thunder':'رعد',
|
| 105 |
+
'blue heart':'قلب أزرق','purple heart':'قلب بنفسجي',
|
| 106 |
+
'golden crown':'تاج ذهبي','silver crown':'تاج فضي',
|
| 107 |
+
'firework':'ألعاب نارية','fireworks':'ألعاب نارية',
|
| 108 |
+
'nevalyashka doll':'دمية','dancing bear':'دب راقص',
|
| 109 |
+
'lucky cat':'قطة محظوظة','magic':'سحر',
|
| 110 |
+
'rainbow rose':'وردة قوس قزح','pink rose':'وردة وردية',
|
| 111 |
+
'red rose':'وردة حمراء','white rose':'وردة بيضاء','blue rose':'وردة زرقاء',
|
| 112 |
+
'golden ticket':'تذكرة ذهبية','golden star':'نجمة ذهبية',
|
| 113 |
+
'diamond ring':'خاتم ماسي','diamond necklace':'عقد ماسي',
|
| 114 |
+
'golden egg':'بيضة ذهبية','golden heart':'قلب ذهبي',
|
| 115 |
+
'crystal ball':'كرة بلورية','tropical island':'جزيرة استوائية',
|
| 116 |
+
'royal carriage':'عربة ملكية','castle':'قصر','dragon flame':'لهب التنين',
|
| 117 |
+
'phoenix':'فيني克斯','mermaid':'مرأة البحر','fairy':'جنية',
|
| 118 |
+
'angel':'ملاك','devil':'شيطان','robot':'روبوت','dinosaur':'ديناصور',
|
| 119 |
+
'penguin':'بطريق','panda':'باندا','koala':'كوالة','lion':'أسد',
|
| 120 |
+
'eagle':'نسر','wolf':'ذئب','fox':'ثعلب','rabbit':'أرنب',
|
| 121 |
+
'gold bar':'سبيكة ذهب','gold coin':'عملة ذهبية','money bag':'كيس مال',
|
| 122 |
+
'diamond crown':'تاج ماسي','diamond heart':'قلب ماسي',
|
| 123 |
+
'super star':'نجمة خارقة','mega star':'نجمة عملاقة',
|
| 124 |
+
'cosmic rose':'وردة كونية','infinity':'لا نهاية','universe':'كون',
|
| 125 |
+
'supreme':'علوي','ultimate':'نهائي','legend':'أسطوري',
|
| 126 |
+
'epic':'ملحمي','mythic':'خرافي','golden dragon':'تنين ذهبي',
|
| 127 |
+
'rainbow crown':'تاج قوس قزح','diamond galaxy':'مجرة ماسية',
|
| 128 |
+
'halo':'هالة','wings':'أجنحة','bow':'قوس','arrow':'سهم',
|
| 129 |
+
'sword':'سيف','shield':'درع','cherry':'كرز','apple':'تفاحة',
|
| 130 |
+
'grape':'عنب','watermelon':'بطيخ','strawberry':'فراولة',
|
| 131 |
+
'banana':'موز','peach':'خوخ','cupcake':'كعكة صغيرة',
|
| 132 |
+
'sparkler':'شرارة','lantern':'مصباح','candle':'شمعة',
|
| 133 |
+
'balloon':'بالون','ribbon':'شريط','gift box':'صندوق هدية',
|
| 134 |
+
'gift':'هدية','present':'هدية','music':'موسيقى',
|
| 135 |
+
'trophy':'كأس','medal':'وسام','flag':'علم',
|
| 136 |
+
'party hat':'قبعة حفلة','confetti':'قصاصات ورقية',
|
| 137 |
+
'wizard':'ساحر','knight':'فرس','ninja':'نينجا',
|
| 138 |
+
'pirate':'قراصنة','pumpkin':'قرعة','snowman':'رجل ثلج',
|
| 139 |
+
'snowflake':'رقصة ثلج','santa':'سانتا','reindeer':'أيل',
|
| 140 |
+
'flower':'زهرة','blossom':'ازهار','tulip':'توليب',
|
| 141 |
+
'orchid':'أوركيد','daisy':'أرجوان','lily':'زنبق',
|
| 142 |
+
'palm tree':'نخلة','cactus':'صبار','mushroom':'فطر',
|
| 143 |
+
'crown prince':'ولي عهد','treasure':'كنز',
|
| 144 |
+
'wine bottle':'زجاجة نبيذ','champagne bottle':'زجاجة شمبانيا',
|
| 145 |
+
'magic wand':'صباح سحري','magic trick':'حيلة سحرية',
|
| 146 |
+
'neon heart':'قلب نيون','neon star':'نجمة نيون',
|
| 147 |
+
'retro':'ريترو','pixel heart':'قلب بيكسل',
|
| 148 |
+
'golden heart':'قلب ذهبي','silver heart':'قلب فضي',
|
| 149 |
+
'black heart':'قلب أسود','broken heart':'قلب مكسور',
|
| 150 |
+
'fire heart':'قلب محترق','sparkling heart':'قلب لامع',
|
| 151 |
+
'yellow heart':'قلب أصفر','green heart':'قلب أخضر',
|
| 152 |
+
'orange heart':'قلب برتقالي','brown heart':'قلب بني',
|
| 153 |
+
'light blue heart':'قلب سماوي','gray heart':'قلب رمادي',
|
| 154 |
+
'100':'100','money':'مال','cash':'كاش','dollar':'دولار',
|
| 155 |
+
'money rain':'مطر مال','viking':'فايكنغ','pharaoh':'فرعون',
|
| 156 |
+
'easter egg':'بيضة عيد الفصح','chicken':'دجاجة',
|
| 157 |
+
'ghost face':'وجه شبح','christmas tree':'شجرة عيد الميلاد',
|
| 158 |
+
'easter bunny':'أرنب عيد الفصح','jack o lantern':'جوجه قرعة',
|
| 159 |
+
't-rex':'تي-ريكس','skeleton':'هيكل عظمي',
|
| 160 |
+
'vampire':'مدمن دم','zombie':'زومبي',
|
| 161 |
+
'alien':'غريب','wizard hat':'قبعة ساحر',
|
| 162 |
+
'top hat':'قبعة عالية','bow tie':'ربطة عنق',
|
| 163 |
+
'sunglasses':'نظارة شمسية','cool':'بارد',
|
| 164 |
+
'thumbs up':'إبهام للأعلى','clap':'تصفيق',
|
| 165 |
+
'pray':'دعاء','clap hands':'تصفيق',
|
| 166 |
+
'folded hands':'يدان مطويتان','raising hands':'أيدي مرفوعة',
|
| 167 |
+
'handshake':'مصافحة','victory':'انتصار',
|
| 168 |
+
'crossed fingers':'أصابع متقاطعة','pinching hand':'قبضة صغيرة',
|
| 169 |
+
'love you':'أحبك','call me':'اتصل بي',
|
| 170 |
+
'ok hand':'حسناً','raised hand':'يد مرفوعة',
|
| 171 |
+
'waving hand':'يد متموجة','flexed biceps':'بطن مشدود',
|
| 172 |
+
'selfie':'سيلفي','nail polish':'طلاء أظافر',
|
| 173 |
+
'lipstick':'أحمر شفاه','kiss mark':'علامة قبلة',
|
| 174 |
+
'lipstick kiss':'قبلة أحمر شفاه','heart eyes':'عينات قلب',
|
| 175 |
+
'starry eyes':'عينات نجمية','drooling face':'وجه يتلوى',
|
| 176 |
+
'thinking':'فكر','nerd':'مهووس','sunglasses face':'وجه بنظارة',
|
| 177 |
+
'mask':'قناع','thermometer':'مقياس حرارة',
|
| 178 |
+
'bandage':'ضماد':'sneezing face':'وجه يعطس',
|
| 179 |
+
'face with head bandage':'وجه بضماد على الرأس',
|
| 180 |
+
'nauseated face':'وجه غاضب','vomiting face':'وجه يتقيء',
|
| 181 |
+
'cool face':'وجه بارد','party face':'وجه حفلة',
|
| 182 |
+
'disguised face':'وجه متخفٍ','cowboy hat face':'وجه بقبعة',
|
| 183 |
+
'exploding head':'رأس ينفجر','face with diamonds':'وجه بماسات',
|
| 184 |
+
'lying face':'وجه يكذب','melting face':'وجه يذوب',
|
| 185 |
};
|
| 186 |
|
| 187 |
+
// ====== قيم الهدايا بالدiamonds ======
|
| 188 |
+
const GIFT_VALUES = {
|
| 189 |
+
5953:25, 5952:1, 5951:5, 5950:10, 5949:15, 5948:20,
|
| 190 |
+
5947:30, 5946:50, 5945:100, 5944:200, 5943:500,
|
| 191 |
+
5942:1000, 5941:2000, 5940:5000, 5939:10000,
|
| 192 |
+
5938:25000, 5937:50000, 5936:100000,
|
| 193 |
+
5935:1, 5934:3, 5933:5, 5932:10, 5931:15,
|
| 194 |
+
5930:20, 5929:30, 5928:40, 5927:50, 5926:60,
|
| 195 |
+
5925:80, 5924:100, 5923:150, 5922:200, 5921:300,
|
| 196 |
+
5920:400, 5919:500, 5918:700, 5917:1000,
|
| 197 |
+
5916:1500, 5915:2000, 5914:3000, 5913:5000,
|
| 198 |
+
5912:10000, 5911:20000, 5910:50000, 5909:100000,
|
| 199 |
+
5908:250000, 5907:500000, 5906:1000000,
|
| 200 |
+
5905:10, 5904:25, 5903:50, 5902:100,
|
| 201 |
+
5901:200, 5900:500, 5899:1000, 5898:2000,
|
| 202 |
+
5897:5000, 5896:10000, 5895:20000, 5894:50000,
|
| 203 |
+
5893:100000, 5892:250000, 5891:500000, 5890:1000000,
|
| 204 |
+
5729:1, 5728:5, 5727:10, 5726:15, 5725:20,
|
| 205 |
+
5724:30, 5723:50, 5722:100, 5721:200, 5720:500,
|
| 206 |
+
5719:1000, 5718:2000, 5717:5000, 5716:10000,
|
| 207 |
+
5715:25000, 5714:50000, 5713:100000,
|
| 208 |
+
5712:250000, 5711:500000, 5710:1000000,
|
| 209 |
+
5972:1, 5971:1, 5970:1, 5969:1, 5968:3,
|
| 210 |
+
5967:5, 5966:10, 5965:15, 5964:20, 5963:30,
|
| 211 |
+
5962:40, 5961:50, 5960:100,
|
| 212 |
+
1817:1, 1816:1, 1815:1, 1814:1, 1813:1,
|
| 213 |
+
1812:1, 1811:1, 1810:5, 1809:5, 1808:5,
|
| 214 |
+
1807:5, 1806:10, 1805:10, 1804:10,
|
| 215 |
+
1803:15, 1802:20, 1801:25, 1800:30,
|
| 216 |
+
1799:35, 1798:40, 1797:50, 1796:60,
|
| 217 |
+
1795:70, 1794:80, 1793:90, 1792:100,
|
| 218 |
+
1791:150, 1790:200, 1789:250, 1788:300,
|
| 219 |
+
1787:400, 1786:500, 1785:700, 1784:1000,
|
| 220 |
+
1783:1500, 1782:2000, 1781:3000, 1780:5000,
|
| 221 |
+
1779:7000, 1778:10000, 1777:15000, 1776:20000,
|
| 222 |
+
1775:30000, 1774:50000, 1773:75000, 1772:100000,
|
| 223 |
+
1807:5000, 1808:5000, 1809:3000, 1810:3000,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
};
|
| 225 |
|
| 226 |
+
// ====== دوال مساعدة ======
|
| 227 |
function getCountryInfo(code) {
|
| 228 |
if (!code) return { name: 'غير محدد', flag: '🌍' };
|
| 229 |
const upperCode = code.toUpperCase();
|
|
|
|
| 232 |
|
| 233 |
function getGiftArabicName(englishName, giftId) {
|
| 234 |
if (!englishName) {
|
| 235 |
+
const val = GIFT_VALUES[giftId];
|
| 236 |
+
return val ? '💎' + val : 'هدية';
|
| 237 |
}
|
| 238 |
const lower = englishName.toLowerCase().trim();
|
| 239 |
if (GIFT_ARABIC[lower]) return GIFT_ARABIC[lower];
|
|
|
|
| 241 |
}
|
| 242 |
|
| 243 |
function getGiftDiamonds(data) {
|
| 244 |
+
// ✅ v2: diamondCount في مستوى علوي
|
| 245 |
+
const dc = data.diamondCount;
|
| 246 |
+
if (dc && dc > 0) return dc;
|
| 247 |
+
|
| 248 |
+
// ✅ من extendedGiftInfo
|
| 249 |
+
const extDc = data.extendedGiftInfo?.diamond_count;
|
| 250 |
+
if (extDc && extDc > 0) return extDc;
|
| 251 |
+
|
| 252 |
+
// ✅ من قيمة الهدية في القاموس
|
| 253 |
+
const gid = data.giftId;
|
| 254 |
+
if (GIFT_VALUES[gid]) return GIFT_VALUES[gid];
|
| 255 |
+
|
| 256 |
+
// ✅ من coinValue في gift
|
| 257 |
+
const coinVal = data.gift?.coinValue;
|
| 258 |
+
if (coinVal && coinVal > 0) return coinVal;
|
| 259 |
+
|
| 260 |
+
// ✅ من diamondCount في gift
|
| 261 |
+
const giftDc = data.gift?.diamondCount;
|
| 262 |
+
if (giftDc && giftDc > 0) return giftDc;
|
| 263 |
+
|
| 264 |
+
return 1; // افتراضي
|
| 265 |
}
|
| 266 |
|
| 267 |
function formatK(num) {
|
| 268 |
+
if (!num || isNaN(num) || num === 0) return '0';
|
| 269 |
if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M';
|
| 270 |
if (num >= 1000) return (num / 1000).toFixed(1) + 'K';
|
| 271 |
return num.toString();
|
| 272 |
}
|
| 273 |
|
| 274 |
+
function formatDuration(seconds) {
|
| 275 |
+
if (!seconds || seconds <= 0) return 'دقيقة واحدة';
|
| 276 |
+
const h = Math.floor(seconds / 3600);
|
| 277 |
+
const m = Math.floor((seconds % 3600) / 60);
|
| 278 |
+
const parts = [];
|
| 279 |
+
if (h > 0) parts.push(h + ' ساعة');
|
| 280 |
+
if (m > 0) parts.push(m + ' دقيقة');
|
| 281 |
+
return parts.join(' و ') || 'أقل من دقيقة';
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
function getBaghdadTime() {
|
| 285 |
return new Intl.DateTimeFormat('ar-IQ', {
|
| 286 |
timeZone: 'Asia/Baghdad',
|
|
|
|
| 292 |
}
|
| 293 |
|
| 294 |
function generateProgressBar(ourScore, theirScore, length = 10) {
|
| 295 |
+
const total = (ourScore || 0) + (theirScore || 0);
|
| 296 |
if (total === 0) return "⬜".repeat(length);
|
| 297 |
const ourPercent = Math.min(
|
| 298 |
+
Math.max(Math.round(((ourScore || 0) / total) * length), 0),
|
| 299 |
length
|
| 300 |
);
|
| 301 |
return "🟦".repeat(ourPercent) + "🟥".repeat(length - ourPercent);
|
| 302 |
}
|
| 303 |
|
| 304 |
+
// ====== HTTP Functions ======
|
| 305 |
function httpGet(url, headers = {}, timeout = 15000) {
|
| 306 |
return new Promise((resolve, reject) => {
|
| 307 |
const urlObj = new URL(url);
|
|
|
|
| 312 |
path: urlObj.pathname + urlObj.search,
|
| 313 |
method: 'GET',
|
| 314 |
headers: {
|
| 315 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/1.0.0 Safari/537.36',
|
| 316 |
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
| 317 |
'Accept-Language': 'en-US,en;q=0.9,ar;q=0.8',
|
| 318 |
'Accept-Encoding': 'gzip, deflate',
|
|
|
|
| 336 |
return new Promise((resolve, reject) => {
|
| 337 |
const urlObj = new URL(url);
|
| 338 |
const protocol = urlObj.protocol === 'https:' ? https : http;
|
| 339 |
+
const body = JSON.stringify(payload);
|
| 340 |
const options = {
|
| 341 |
hostname: urlObj.hostname,
|
| 342 |
port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
|
|
|
|
| 344 |
method: 'POST',
|
| 345 |
headers: {
|
| 346 |
'Content-Type': 'application/json',
|
| 347 |
+
'Content-Length': Buffer.byteLength(body)
|
| 348 |
},
|
| 349 |
timeout: timeout
|
| 350 |
};
|
|
|
|
| 355 |
});
|
| 356 |
req.on('error', reject);
|
| 357 |
req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
|
| 358 |
+
req.write(body);
|
| 359 |
req.end();
|
| 360 |
});
|
| 361 |
}
|
| 362 |
|
| 363 |
+
// ====== جلب بيانات المستخدم + الدولة ======
|
| 364 |
async function getUserDataWithRegion(username) {
|
| 365 |
try {
|
| 366 |
const response = await httpGet('https://www.tiktok.com/@' + username);
|
|
|
|
| 386 |
return null;
|
| 387 |
}
|
| 388 |
|
| 389 |
+
// ====== متغيرات عامة ======
|
| 390 |
+
let maxViewers = 0;
|
| 391 |
+
let totalLikes = 0;
|
| 392 |
+
let totalShares = 0;
|
| 393 |
+
let totalLikesEvents = 0;
|
| 394 |
+
let streamStartTime = null;
|
| 395 |
+
let giftersMap = {};
|
| 396 |
+
let pkActive = false;
|
| 397 |
+
let pkInfo = { opponentName: "", ourLikes: 0, theirLikes: 0 };
|
| 398 |
let userCache = {};
|
| 399 |
|
| 400 |
+
// ====== إرسال تليقرام ======
|
| 401 |
async function sendToTelegram(text, photoUrl = null, buttons = null) {
|
| 402 |
try {
|
| 403 |
const payload = {
|
|
|
|
| 434 |
return null;
|
| 435 |
}
|
| 436 |
|
| 437 |
+
// ====== الاتصال بـ TikTok Live ======
|
| 438 |
+
// ✅ استخدام TikTokLiveConnection بدون webcastKernelVersion
|
| 439 |
let tiktokConnection = new TikTokLiveConnection(TIKTOK_USERNAME, {
|
| 440 |
processInitialData: true,
|
| 441 |
enableExtendedGiftInfo: true,
|
|
|
|
| 442 |
clientParams: {
|
| 443 |
app_language: "ar-IQ",
|
| 444 |
device_platform: "web"
|
| 445 |
}
|
| 446 |
});
|
| 447 |
|
| 448 |
+
// ====== تشغيل الرادار ======
|
| 449 |
async function startRadar() {
|
| 450 |
console.log('⏳ جاري البحث: ' + TIKTOK_USERNAME + '...');
|
| 451 |
userCache = {};
|
| 452 |
giftersMap = {};
|
| 453 |
maxViewers = 0;
|
| 454 |
totalLikes = 0;
|
| 455 |
+
totalLikesEvents = 0;
|
| 456 |
totalShares = 0;
|
| 457 |
pkActive = false;
|
| 458 |
+
pkInfo = { opponentName: "", ourLikes: 0, theirLikes: 0 };
|
| 459 |
+
streamStartTime = Date.now();
|
| 460 |
|
| 461 |
const userData = await getUserDataWithRegion(TIKTOK_USERNAME);
|
| 462 |
const countryInfo = getCountryInfo(userData?.region);
|
| 463 |
|
| 464 |
+
try {
|
| 465 |
+
const state = await tiktokConnection.connect();
|
| 466 |
+
console.log('✅ متصل بالغرفة!');
|
| 467 |
|
| 468 |
+
const liveLink = 'https://www.tiktok.com/@' + TIKTOK_USERNAME + '/live';
|
| 469 |
let avatarUrl = '';
|
| 470 |
+
const owner = state.roomInfo?.owner || null;
|
| 471 |
+
if (owner) {
|
| 472 |
+
if (owner.profilePicture) avatarUrl = owner.profilePicture;
|
| 473 |
+
else if (owner.profilePictureUrl) avatarUrl = owner.profilePictureUrl;
|
| 474 |
+
else if (owner.profileThumbnail) avatarUrl = owner.profileThumbnail;
|
| 475 |
+
else if (owner.avatarLarge?.urlList?.[0]) avatarUrl = owner.avatarLarge.urlList[0];
|
| 476 |
+
else if (owner.avatarLarge?.url?.[0]) avatarUrl = owner.avatarLarge.url[0];
|
| 477 |
+
else if (owner.avatarThumb?.urlList?.[0]) avatarUrl = owner.avatarThumb.urlList[0];
|
| 478 |
+
else if (owner.avatarThumb?.url?.[0]) avatarUrl = owner.avatarThumb.url[0];
|
| 479 |
+
else if (owner.avatar?.urlList?.[0]) avatarUrl = owner.avatar.urlList[0];
|
| 480 |
+
else if (owner.avatar?.url?.[0]) avatarUrl = owner.avatar.url[0];
|
| 481 |
+
else if (owner.profilePictureLarge?.url?.[0]) avatarUrl = owner.profilePictureLarge.url[0];
|
| 482 |
+
}
|
| 483 |
+
if (!avatarUrl) {
|
| 484 |
+
avatarUrl = state.roomInfo?.sharedCover || state.roomInfo?.coverUrl || '';
|
| 485 |
}
|
|
|
|
|
|
|
|
|
|
| 486 |
|
| 487 |
const displayName = userData?.nickname || TIKTOK_USERNAME;
|
| 488 |
+
const verifiedBadge = userData?.verified ? ' ✅' : '';
|
| 489 |
|
|
|
|
| 490 |
const msg = '🔴 ' + displayName + verifiedBadge + '\n\n' +
|
| 491 |
'🌐 @' + TIKTOK_USERNAME + '\n' +
|
| 492 |
countryInfo.flag + ' ' + countryInfo.name + '\n' +
|
| 493 |
'⏰ ' + getBaghdadTime();
|
| 494 |
|
| 495 |
sendToTelegram(msg, avatarUrl || undefined, [[{ text: "📺 دخول البث المباشر", url: liveLink }]]);
|
| 496 |
+
console.log('🌍 البلد: ' + countryInfo.name);
|
|
|
|
| 497 |
console.log('📸 صورة: ' + (avatarUrl ? 'متوفرة' : 'غير متوفرة'));
|
| 498 |
+
} catch (err) {
|
| 499 |
+
console.log('🔎 مغلق: ' + (err.message || err));
|
| 500 |
setTimeout(startRadar, 60000);
|
| 501 |
+
}
|
| 502 |
}
|
| 503 |
|
| 504 |
+
// ====== الأحداث ======
|
| 505 |
+
|
| 506 |
+
// ✅ عدد المشاهدين
|
| 507 |
tiktokConnection.on(WebcastEvent.ROOM_USER, data => {
|
| 508 |
const count = data.viewerCount || 0;
|
| 509 |
if (count > maxViewers) maxViewers = count;
|
| 510 |
});
|
| 511 |
|
| 512 |
+
// ✅ الإعجابات
|
| 513 |
tiktokConnection.on(WebcastEvent.LIKE, data => {
|
| 514 |
+
totalLikesEvents++;
|
| 515 |
+
const lc = data.likeCount || 0;
|
| 516 |
+
if (lc > 0) totalLikes += lc;
|
| 517 |
if (pkActive) {
|
| 518 |
+
pkInfo.ourLikes += lc;
|
| 519 |
}
|
| 520 |
});
|
| 521 |
|
| 522 |
+
// ✅ مشاركة
|
| 523 |
tiktokConnection.on(WebcastEvent.SOCIAL, data => {
|
| 524 |
totalShares++;
|
| 525 |
console.log('↪️ مشاركة!');
|
| 526 |
});
|
| 527 |
|
| 528 |
+
// ✅ تعليقات
|
| 529 |
tiktokConnection.on(WebcastEvent.CHAT, data => {
|
| 530 |
// لا نرسل إشعارات لتجنب السبام
|
| 531 |
});
|
| 532 |
|
| 533 |
+
// ✅ ========== الهدايا ==========
|
| 534 |
tiktokConnection.on(WebcastEvent.GIFT, async data => {
|
|
|
|
|
|
|
|
|
|
| 535 |
const gifterUsername = data.uniqueId || '';
|
| 536 |
const nickname = data.nickname || '';
|
| 537 |
const giftId = data.giftId || 0;
|
|
|
|
| 540 |
const giftType = data.giftType || 0;
|
| 541 |
const repeatEnd = data.repeatEnd;
|
| 542 |
|
| 543 |
+
// تجاهل تحديثات الستريك
|
| 544 |
if (giftType === 1 && !repeatEnd) return;
|
| 545 |
|
| 546 |
+
// ✅ القيمة
|
| 547 |
const baseDiamonds = getGiftDiamonds(data);
|
| 548 |
const giftCost = baseDiamonds * repeatCount;
|
| 549 |
|
| 550 |
// ✅ الاسم بالعربي
|
| 551 |
const arabicName = getGiftArabicName(giftNameEn, giftId);
|
| 552 |
|
| 553 |
+
console.log('🎁 ' + gifterUsername + ' → ' + arabicName + ' (ID:' + giftId + ') ×' +
|
| 554 |
+
repeatCount + ' = ' + giftCost + '💎 [dc:' + data.diamondCount + ']');
|
|
|
|
|
|
|
| 555 |
|
| 556 |
if (!gifterUsername) {
|
| 557 |
+
console.log('⚠️ هدية بدون اسم');
|
| 558 |
return;
|
| 559 |
}
|
| 560 |
|
| 561 |
const userId = data.userId || gifterUsername;
|
| 562 |
if (!giftersMap[userId]) {
|
| 563 |
giftersMap[userId] = {
|
| 564 |
+
name: nickname || gifterUsername,
|
| 565 |
username: gifterUsername,
|
| 566 |
coins: 0,
|
| 567 |
region: null,
|
|
|
|
| 570 |
}
|
| 571 |
giftersMap[userId].coins += giftCost;
|
| 572 |
|
| 573 |
+
// ✅ جلب بيانات الدولة
|
| 574 |
const userRegionData = await resolveUser(gifterUsername);
|
| 575 |
let region = null;
|
| 576 |
+
let displayName = nickname || gifterUsername;
|
| 577 |
|
| 578 |
if (userRegionData && userRegionData.region) {
|
| 579 |
region = userRegionData.region;
|
| 580 |
+
displayName = userRegionData.nickname || displayName;
|
| 581 |
giftersMap[userId].region = region;
|
| 582 |
}
|
| 583 |
|
| 584 |
const gc = getCountryInfo(region);
|
| 585 |
giftersMap[userId].flag = gc.flag;
|
| 586 |
|
| 587 |
+
// ✅ الرسالة
|
| 588 |
const repeatText = repeatCount > 1 ? ' ×' + repeatCount : '';
|
| 589 |
const msg = '🎁 ' + arabicName + repeatText + '\n\n' +
|
| 590 |
gc.flag + ' ' + displayName + '\n' +
|
|
|
|
| 598 |
]]);
|
| 599 |
});
|
| 600 |
|
| 601 |
+
// ✅ ========== المشرفين ==========
|
| 602 |
+
tiktokConnection.on(WebcastEvent.MEMBER, async data => {
|
| 603 |
+
const memberUsername = data.uniqueId || '';
|
| 604 |
+
if (!memberUsername) return;
|
| 605 |
+
|
| 606 |
+
// ✅ فقط نكشف الدولة، ما نرسل إشعار دخول عادي
|
| 607 |
+
// لكن نرسل لو كان مشرف أو VIP
|
| 608 |
+
const memberData = await resolveUser(memberUsername);
|
| 609 |
+
if (memberData) {
|
| 610 |
+
userCache[memberUsername] = memberData;
|
| 611 |
+
}
|
| 612 |
+
});
|
| 613 |
+
|
| 614 |
+
// ✅ متابعة جديدة
|
| 615 |
+
tiktokConnection.on(WebcastEvent.FOLLOW, async data => {
|
| 616 |
+
const followUsername = data.uniqueId || '';
|
| 617 |
+
if (!followUsername) return;
|
| 618 |
+
|
| 619 |
+
console.log('➕ متابعة من: ' + followUsername);
|
| 620 |
+
const followData = await resolveUser(followUsername);
|
| 621 |
+
let region = null;
|
| 622 |
+
let displayName = data.nickname || followUsername;
|
| 623 |
+
|
| 624 |
+
if (followData) {
|
| 625 |
+
region = followData.region;
|
| 626 |
+
displayName = followData.nickname || displayName;
|
| 627 |
+
userCache[followUsername] = followData;
|
| 628 |
+
}
|
| 629 |
+
|
| 630 |
+
const gc = getCountryInfo(region);
|
| 631 |
+
const msg = '➕ متابع جديد!\n\n' +
|
| 632 |
+
gc.flag + ' ' + displayName + '\n' +
|
| 633 |
+
'📱 @' + followUsername + '\n' +
|
| 634 |
+
'🌍 ' + gc.name;
|
| 635 |
+
|
| 636 |
+
sendToTelegram(msg);
|
| 637 |
+
});
|
| 638 |
+
|
| 639 |
+
// ✅ ========== نهاية البث ==========
|
| 640 |
+
tiktokConnection.on(WebcastEvent.ROOM_STATS, data => {
|
| 641 |
+
// تحديث الإحصائيات من TikTok مباشرة
|
| 642 |
+
if (data.totalLikeCount) {
|
| 643 |
+
totalLikes = data.totalLikeCount;
|
| 644 |
+
}
|
| 645 |
+
console.log('📊 إحصائيات: مشاهدات=' + (data.viewerCount || maxViewers) +
|
| 646 |
+
' إعجابات=' + totalLikes + ' مشاركات=' + totalShares);
|
| 647 |
});
|
| 648 |
|
| 649 |
+
// ✅ Disconnect
|
| 650 |
tiktokConnection.on(WebcastEvent.DISCONNECTED, () => {
|
| 651 |
console.log('❌ تم القطع');
|
| 652 |
});
|
| 653 |
|
| 654 |
+
// ✅ Connected
|
| 655 |
+
tiktokConnection.on(WebcastEvent.CONNECTED, state => {
|
| 656 |
+
console.log('✅ متصل بالغرفة:', state.roomId);
|
| 657 |
+
});
|
| 658 |
+
|
| 659 |
+
// 🔥 ========== أحداث PK ==========
|
| 660 |
tiktokConnection.on('pkStart', data => {
|
| 661 |
pkActive = true;
|
| 662 |
pkInfo = { opponentName: data.opponentNickname || 'منافس', ourLikes: 0, theirLikes: 0 };
|
|
|
|
| 681 |
);
|
| 682 |
});
|
| 683 |
|
| 684 |
+
// ✅ ========== نهاية البث + التقرير ==========
|
| 685 |
tiktokConnection.on('streamEnd', () => {
|
| 686 |
+
const streamDuration = streamStartTime ? formatDuration(Math.floor((Date.now() - streamStartTime) / 1000)) : 'غير معروف';
|
| 687 |
+
|
| 688 |
const top = getTopGifters(giftersMap, 5);
|
| 689 |
const medals = ['🥇', '🥈', '🥉', '4️⃣', '5️⃣'];
|
| 690 |
let topText = top.map((g, i) =>
|
| 691 |
+
medals[i] + ' ' + (g.flag || '🌍') + ' ' + g.name + '\n' +
|
| 692 |
+
' @' + g.username + ' → ' + formatK(g.coins) + ' 💎'
|
| 693 |
+
).join('\n\n') || 'لا يوجد داعمين';
|
| 694 |
|
| 695 |
const countryStats = {};
|
| 696 |
Object.values(giftersMap).forEach(g => {
|
|
|
|
| 703 |
.slice(0, 3)
|
| 704 |
.map(([r, c], i) =>
|
| 705 |
['👑', '🥈', '🥉'][i] + ' ' + getCountryInfo(r).flag + ' ' +
|
| 706 |
+
getCountryInfo(r).name + ' → ' + formatK(c) + ' 💎'
|
| 707 |
+
).join('\n') || 'لا بيانات';
|
| 708 |
|
| 709 |
sendToTelegram(
|
| 710 |
'📊 التقرير النهائي\n\n' +
|
| 711 |
+
'⏱️ مدة البث: ' + streamDuration + '\n' +
|
| 712 |
+
'👥 أعلى مشاهدات: ' + formatK(maxViewers) + '\n' +
|
| 713 |
'❤️ إعجابات: ' + formatK(totalLikes) + '\n' +
|
| 714 |
+
'↪️ مشاركات: ' + formatK(totalShares) + '\n' +
|
| 715 |
+
'👥 إجمالي الداعمين: ' + Object.keys(giftersMap).length + '\n\n' +
|
| 716 |
+
'🏆 أقوى داعمين:\n\n' + topText + '\n\n' +
|
| 717 |
+
'🌍 توزيع حسب الدولة:\n' + countryRanking + '\n\n💤 بانتظار البث القادم...'
|
| 718 |
);
|
| 719 |
|
| 720 |
setTimeout(startRadar, 60000);
|
| 721 |
});
|
| 722 |
|
| 723 |
+
// ====== الخادم ======
|
| 724 |
const server = http.createServer((req, res) => {
|
| 725 |
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
| 726 |
res.end('OK');
|