Reooiq commited on
Commit
efc5ee2
ยท
verified ยท
1 Parent(s): 9b96688

Update bot.js

Browse files
Files changed (1) hide show
  1. bot.js +308 -266
bot.js CHANGED
@@ -1,8 +1,9 @@
1
  /**
2
  * โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
3
- * โ•‘ TikTok Radar Pro - Telegram Bot v4.0 (Royal Edition) โ•‘
4
  * โ•‘ ุจูˆุช ุงุญุชุฑุงููŠ ูŠุฑุงู‚ุจ ุจุซ TikTok ูˆูŠุฑุณู„ ุงู„ุฅุญุตุงุฆูŠุงุช ู„ู„ู‚ู†ุงุฉ โ•‘
5
  * โ•‘ ู…ุน ุฃุฒุฑุงุฑ ุชูุงุนู„ูŠุฉ + ุฌุณุฑ Cloudflare + ู†ุจุถ ุฏุงุฎู„ูŠ + ู†ุธุงู… ู…ูƒุงูุญุฉ ุฅุฒุนุงุฌ โ•‘
 
6
  * โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
7
  */
8
 
@@ -25,15 +26,12 @@ const CONFIG = {
25
  TELEGRAM_CHAT_ID: cleanEnv(process.env.TELEGRAM_CHAT_ID, ''),
26
  PROXY_URL: cleanEnv(process.env.PROXY_URL, '').replace(/\/$/, ''),
27
  REPORT_INTERVAL_MINUTES: parseInt(cleanEnv(process.env.REPORT_INTERVAL, '10')) || 10,
28
- // ุฅุนุฏุงุฏุงุช ู…ูƒุงูุญุฉ ุงู„ุฅุฒุนุงุฌ
29
- SILENT_RECONNECT: true, // ุฅุนุงุฏุฉ ุงู„ุงุชุตุงู„ ุจุฏูˆู† ุฅุดุนุงุฑุงุช
30
- NOTIFY_ONLY_ON_LIVE: true, // ุฅุดุนุงุฑ ูู‚ุท ุนู†ุฏ ูุชุญ ุงู„ุจุซ
31
- CHAT_COOLDOWN_MS: 3000, // ูุชุฑุฉ ุชุจุฑูŠุฏ ุงู„ุฏุฑุฏุดุฉ (3 ุซูˆุงู†ูŠ)
32
- GIFT_COOLDOWN_MS: 2000, // ูุชุฑุฉ ุชุจุฑูŠุฏ ุงู„ู‡ุฏุงูŠุง (2 ุซุงู†ูŠุฉ)
33
- LIKE_BATCH_THRESHOLD: 50, // ุชุฌู…ุน ุงู„ุฅุนุฌุงุจุงุช ุญุชู‰ ู‡ุฐุง ุงู„ุนุฏุฏ
34
- SHOW_TOP_CHAT: true, // ุนุฑุถ ุฃู‡ู… ุฑุณุงุฆู„ ุงู„ุฏุฑุฏุดุฉ
35
- TOP_CHAT_LIMIT: 5, // ุนุฏุฏ ุฑุณุงุฆู„ ุงู„ุฏุฑุฏุดุฉ ุงู„ู…ู‡ู…ุฉ
36
- SPAM_KEYWORDS: ['spam', 'scam', 'fake', 'click', 'link'], // ูƒู„ู…ุงุช ุณุจุงู…
37
  };
38
 
39
  console.log('[Config] Loaded settings:');
@@ -54,12 +52,16 @@ let totalShares = 0;
54
  let totalGifts = 0;
55
  let totalFollows = 0;
56
  let totalComments = 0;
 
 
57
  let giftersMap = {};
58
  let likersMap = {};
59
  let sharersMap = {};
60
  let followersMap = {};
61
  let chattersMap = {};
62
  let moderatorsMap = {};
 
 
63
  let topChatMessages = [];
64
  let startTime = null;
65
  let lastReportTime = null;
@@ -67,7 +69,6 @@ let tiktokConnection = null;
67
  let reconnectTimer = null;
68
  let reportInterval = null;
69
  let lastMessageId = null;
70
- let lastStatusMessageId = null;
71
  let reconnectAttempt = 0;
72
  let lastReconnectNotify = 0;
73
 
@@ -78,14 +79,15 @@ const cooldowns = {
78
  like: new Map(),
79
  follow: new Map(),
80
  share: new Map(),
 
 
81
  };
82
 
83
- // PK System
84
  let pkActive = false;
85
  let pkInfo = {
86
  opponentName: '',
87
- ourLikes: 0,
88
- theirLikes: 0,
89
  ourScore: 0,
90
  theirScore: 0,
91
  startTime: null,
@@ -162,14 +164,14 @@ function isOnCooldown(type, key, duration) {
162
  function isSpamMessage(text) {
163
  if (!text) return true;
164
  const lower = text.toLowerCase();
165
- return CONFIG.SPAM_KEYWORDS.some(kw => lower.includes(kw));
166
  }
167
 
168
  function cleanChatMessage(text) {
169
  if (!text) return '';
170
  let cleaned = text.replace(/https?:\/\/\S+/gi, '[ุฑุงุจุท]');
171
- cleaned = cleaned.replace(/(.)(?=\1{4,})/g, '');
172
- if (cleaned.length > 100) cleaned = cleaned.substring(0, 100) + '...';
173
  return cleaned.trim();
174
  }
175
 
@@ -226,9 +228,7 @@ async function sendTelegram(text, photoUrl = null, buttons = null, parseMode = '
226
  function getMainButtons() {
227
  const liveLink = `https://www.tiktok.com/@${CONFIG.TIKTOK_USERNAME}/live`;
228
  return [
229
- [
230
- { text: '๐Ÿ”ด ุฏุฎูˆู„ ุงู„ุจุซ ุงู„ู…ุจุงุดุฑ', url: liveLink },
231
- ],
232
  [
233
  { text: '๐Ÿ“Š ุชู‚ุฑูŠุฑ ููˆุฑูŠ', callback_data: 'report_now' },
234
  { text: '๐Ÿ† ู„ูˆุญุฉ ุงู„ุดุฑู', callback_data: 'top_gifters' },
@@ -241,6 +241,10 @@ function getMainButtons() {
241
  { text: 'โš”๏ธ ุญุงู„ุฉ PK', callback_data: 'pk_status' },
242
  { text: '๐Ÿ“ˆ ุฅุญุตุงุฆูŠุงุช', callback_data: 'deep_stats' },
243
  ],
 
 
 
 
244
  [
245
  { text: isConnected ? '๐Ÿ”ด ู‚ุทุน ุงู„ุงุชุตุงู„' : '๐ŸŸข ุงู„ุงุชุตุงู„', callback_data: isConnected ? 'disconnect' : 'connect' },
246
  ],
@@ -250,27 +254,19 @@ function getMainButtons() {
250
  function getPKButtons() {
251
  const liveLink = `https://www.tiktok.com/@${CONFIG.TIKTOK_USERNAME}/live`;
252
  return [
253
- [
254
- { text: '๐Ÿš€ ุฏุนู… ุงู„ุจุซ ุงู„ุขู†', url: liveLink },
255
- ],
256
  [
257
  { text: '๐Ÿ“Š ุชุญุฏูŠุซ ุงู„ู†ุชูŠุฌุฉ', callback_data: 'pk_update' },
258
  { text: '๐Ÿ† ุฏุงุนู…ูŠู† ุงู„ุฌูˆู„ุฉ', callback_data: 'pk_gifters' },
259
  ],
260
- [
261
- { text: 'โฌ…๏ธ ุงู„ู‚ุงุฆู…ุฉ ุงู„ุฑุฆูŠุณูŠุฉ', callback_data: 'main_menu' },
262
- ],
263
  ];
264
  }
265
 
266
  function getDisconnectedButtons() {
267
  return [
268
- [
269
- { text: '๐Ÿ”„ ุฅุนุงุฏุฉ ุงู„ุงุชุตุงู„', callback_data: 'connect' },
270
- ],
271
- [
272
- { text: '๐Ÿ“Š ุงู„ุชู‚ุฑูŠุฑ ุงู„ู†ู‡ุงุฆูŠ', callback_data: 'final_report' },
273
- ],
274
  ];
275
  }
276
 
@@ -289,8 +285,10 @@ function getStatsButtons() {
289
  { text: '๐Ÿ‘ฎ ุงู„ู…ุดุฑููŠู†', callback_data: 'moderators' },
290
  ],
291
  [
292
- { text: 'โฌ…๏ธ ุงู„ู‚ุงุฆู…ุฉ ุงู„ุฑุฆูŠุณูŠุฉ', callback_data: 'main_menu' },
 
293
  ],
 
294
  ];
295
  }
296
 
@@ -314,11 +312,7 @@ async function handleCallback(callbackData) {
314
  disconnectRadar();
315
  await sendTelegram(
316
  `๐Ÿ”ด ุชู… ู‚ุทุน ุงู„ุงุชุตุงู„\nโฐ ${getBaghdadTime()}`,
317
- null,
318
- getDisconnectedButtons(),
319
- 'HTML',
320
- null,
321
- true
322
  );
323
  }
324
  break;
@@ -331,72 +325,37 @@ async function handleCallback(callbackData) {
331
  }
332
  break;
333
 
334
- case 'top_gifters':
335
- await sendTopGiftersReport();
336
- break;
337
-
338
- case 'top_likers':
339
- await sendTopLikersReport();
340
- break;
 
341
 
342
- case 'top_sharers':
343
- await sendTopSharersReport();
344
- break;
345
-
346
- case 'top_followers':
347
- await sendTopFollowersReport();
348
- break;
349
-
350
- case 'top_chat':
351
- await sendTopChatReport();
352
- break;
353
-
354
- case 'top_active':
355
- await sendTopActiveReport();
356
- break;
357
-
358
- case 'moderators':
359
- await sendModeratorsReport();
360
- break;
361
-
362
- case 'deep_stats':
363
- await sendDeepStatsReport();
364
- break;
365
 
366
  case 'pk_update':
367
- if (pkActive) {
368
- await sendPKUpdate();
369
- } else {
370
- await sendTelegram('โš”๏ธ ู„ุง ูŠูˆุฌุฏ ุฌูˆู„ุฉ PK ู†ุดุทุฉ ุญุงู„ูŠุงู‹', null, getMainButtons(), 'HTML', null, true);
371
- }
372
  break;
373
 
374
  case 'pk_status':
375
- if (pkActive) {
376
- await sendPKUpdate();
377
- } else {
378
- await sendTelegram('โš”๏ธ ู„ุง ูŠูˆุฌุฏ ุฌูˆู„ุฉ PK ู†ุดุทุฉ ุญุงู„ูŠุงู‹', null, getMainButtons(), 'HTML', null, true);
379
- }
380
- break;
381
-
382
- case 'pk_gifters':
383
- await sendPKGiftersReport();
384
  break;
385
 
386
- case 'final_report':
387
- await sendFinalReport();
388
- break;
389
-
390
- case 'main_menu':
391
- await sendTelegram('๐Ÿ“ก ุงู„ู‚ุงุฆู…ุฉ ุงู„ุฑุฆูŠุณูŠุฉ', null, getMainButtons(), 'HTML', null, true);
392
- break;
393
 
394
  default:
395
  console.log(`[Callback] Unknown: ${callbackData}`);
396
  }
397
  }
398
 
399
- // Poll for callback queries
400
  async function pollCallbacks() {
401
  let offset = 0;
402
  const urls = [];
@@ -441,33 +400,23 @@ async function pollCallbacks() {
441
  // โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ๏ธ ุงู„ุชู‚ุงุฑูŠุฑ ูˆุงู„ุฅุดุนุงุฑุงุช โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
442
  // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
443
  function getTopGifters(top = 5) {
444
- return Object.values(giftersMap)
445
- .sort((a, b) => b.coins - a.coins)
446
- .slice(0, top);
447
  }
448
 
449
  function getTopLikers(top = 5) {
450
- return Object.values(likersMap)
451
- .sort((a, b) => b.count - a.count)
452
- .slice(0, top);
453
  }
454
 
455
  function getTopSharers(top = 5) {
456
- return Object.values(sharersMap)
457
- .sort((a, b) => b.count - a.count)
458
- .slice(0, top);
459
  }
460
 
461
  function getTopFollowers(top = 5) {
462
- return Object.values(followersMap)
463
- .sort((a, b) => b.count - a.count)
464
- .slice(0, top);
465
  }
466
 
467
  function getTopChatters(top = 5) {
468
- return Object.values(chattersMap)
469
- .sort((a, b) => b.count - a.count)
470
- .slice(0, top);
471
  }
472
 
473
  function getTopActiveUsers(top = 5) {
@@ -475,95 +424,93 @@ function getTopActiveUsers(top = 5) {
475
  [...Object.values(giftersMap), ...Object.values(likersMap), ...Object.values(sharersMap),
476
  ...Object.values(followersMap), ...Object.values(chattersMap)].forEach(u => {
477
  const key = u.id || u.name;
478
- if (!allUsers[key]) {
479
- allUsers[key] = { name: u.name, score: 0 };
480
- }
481
  allUsers[key].score += (u.coins || 0) + (u.count || 0) * 10;
482
  });
483
- return Object.values(allUsers)
484
- .sort((a, b) => b.score - a.score)
485
- .slice(0, top);
486
  }
487
 
488
  function getModeratorsList() {
489
- return Object.values(moderatorsMap)
490
- .sort((a, b) => b.joinTime - a.joinTime);
 
 
 
 
 
 
 
491
  }
492
 
493
  function formatTopGifters(top = 5) {
494
  const list = getTopGifters(top);
495
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ุฏุงุนู…ูŠู† ููŠ ู‡ุฐุง ุงู„ุจุซ';
496
  const medals = ['๐Ÿฅ‡', '๐Ÿฅˆ', '๐Ÿฅ‰', '๐Ÿ…', '๐ŸŽ–', '๐ŸŽ—', '๐Ÿต', '๐ŸŽ–', '๐Ÿ…', '๐Ÿฅ‰'];
497
- return list
498
- .map((g, i) => `${medals[i] || '๐Ÿ…'} ${escapeHtml(g.name)} ยป ${formatK(g.coins)} ๐Ÿ’Ž`)
499
- .join('\n');
500
  }
501
 
502
  function formatTopLikers(top = 5) {
503
  const list = getTopLikers(top);
504
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ู…ุนุฌุจูŠู† ู…ุณุฌู„ูŠู†';
505
  const medals = ['๐Ÿฅ‡', '๐Ÿฅˆ', '๐Ÿฅ‰', '๐Ÿ…', '๐ŸŽ–'];
506
- return list
507
- .map((g, i) => `${medals[i] || 'โค๏ธ'} ${escapeHtml(g.name)} ยป ${formatK(g.count)} โค๏ธ`)
508
- .join('\n');
509
  }
510
 
511
  function formatTopSharers(top = 5) {
512
  const list = getTopSharers(top);
513
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ู…ุดุงุฑูƒูŠู† ู…ุณุฌู„ูŠู†';
514
- return list
515
- .map((g, i) => `${i + 1}. ${escapeHtml(g.name)} ยป ${formatK(g.count)} โ†ช๏ธ`)
516
- .join('\n');
517
  }
518
 
519
  function formatTopFollowers(top = 5) {
520
  const list = getTopFollowers(top);
521
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ู…ุชุงุจุนูŠู† ุฌุฏุฏ ู…ุณุฌู„ูŠู†';
522
- return list
523
- .map((g, i) => `${i + 1}. ${escapeHtml(g.name)} โž•`)
524
- .join('\n');
525
  }
526
 
527
  function formatTopChatters(top = 5) {
528
  const list = getTopChatters(top);
529
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ู…ุชูุงุนู„ูŠู† ููŠ ุงู„ุฏุฑุฏุดุฉ';
530
- return list
531
- .map((g, i) => `${i + 1}. ${escapeHtml(g.name)} ยป ${formatK(g.count)} ๐Ÿ’ฌ`)
532
- .join('\n');
533
  }
534
 
535
  function formatTopActive(top = 5) {
536
  const list = getTopActiveUsers(top);
537
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ู…ุชูุงุนู„ูŠู† ุจุนุฏ';
538
  const medals = ['๐Ÿ‘‘', '๐Ÿฅˆ', '๐Ÿฅ‰', 'โญ', 'โœจ'];
539
- return list
540
- .map((g, i) => `${medals[i] || '๐Ÿ”น'} ${escapeHtml(g.name)} ยป ${formatK(g.score)} ู†ู‚ุทุฉ`)
541
- .join('\n');
542
  }
543
 
544
  function formatModerators() {
545
  const list = getModeratorsList();
546
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ู…ุดุฑููŠู† ุฏุฎู„ูˆุง ุงู„ุจุซ';
547
- return list
548
- .map((m, i) => `${i + 1}. ๐Ÿ‘ฎ ${escapeHtml(m.name)}${m.isModerator ? ' [ู…ุดุฑู]' : ''}`)
549
- .join('\n');
550
  }
551
 
552
  function formatTopChatMessages() {
553
- if (topChatMessages.length === 0) return 'ู„ุง ุชูˆุฌุฏ ุฑุณุงุฆู„ ู…ู‡ู…ุฉ ุจุนุฏ';
554
  return topChatMessages
555
- .map((msg, i) => `${i + 1}. ๐Ÿ’ฌ <b>${escapeHtml(msg.name)}:</b> ${escapeHtml(msg.text)}`)
 
556
  .join('\n');
557
  }
558
 
559
  function formatPKGifters() {
560
- const list = Object.values(pkInfo.pkGifts || {})
561
- .sort((a, b) => b.coins - a.coins)
562
- .slice(0, 5);
563
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ุฏุงุนู…ูŠู† ููŠ ุงู„ุฌูˆู„ุฉ';
564
- return list
565
- .map((g, i) => `${i + 1}. ${escapeHtml(g.name)} ยป ${formatK(g.coins)} ๐Ÿ’Ž`)
566
- .join('\n');
 
 
 
 
 
 
 
 
 
 
567
  }
568
 
569
  // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
@@ -587,7 +534,9 @@ async function sendPeriodicReport() {
587
  `โ†ช๏ธ <b>ุงู„ู…ุดุงุฑูƒุงุช:</b> ${formatK(totalShares)}\n` +
588
  `โž• <b>ุงู„ู…ุชุงุจุนูŠู† ุงู„ุฌุฏุฏ:</b> ${formatK(totalFollows)}\n` +
589
  `๐Ÿ’ฌ <b>ุงู„ุชุนู„ูŠู‚ุงุช:</b> ${formatK(totalComments)}\n` +
590
- `๐ŸŽ <b>ุงู„ู‡ุฏุงูŠุง:</b> ${formatK(totalGifts)} ๐Ÿ’Ž\n\n` +
 
 
591
  `๐Ÿ† <b>ุฃูุถู„ ุงู„ุฏุงุนู…ูŠู†:</b>\n${topGifterText}\n\n` +
592
  `โค๏ธ <b>ุฃูƒุซุฑ ุงู„ู…ุนุฌุจูŠู†:</b>\n${topLikerText}\n` +
593
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”`;
@@ -597,40 +546,36 @@ async function sendPeriodicReport() {
597
  }
598
 
599
  async function sendTopGiftersReport() {
600
- const topGifterText = formatTopGifters(10);
601
  const msg = `๐Ÿ‘‘ <b>ู„ูˆุญุฉ ุงู„ุดุฑู - ุฃูุถู„ ุงู„ุฏุงุนู…ูŠู†</b>\n` +
602
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
603
- `${topGifterText}\n` +
604
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
605
  `๐Ÿ’Ž ุฅุฌู…ุงู„ูŠ ุงู„ู‡ุฏุงูŠุง: ${formatK(totalGifts)}`;
606
  await sendTelegram(msg, null, getStatsButtons(), 'HTML', null, true);
607
  }
608
 
609
  async function sendTopLikersReport() {
610
- const text = formatTopLikers(10);
611
  const msg = `โค๏ธ <b>ุฃูƒุซุฑ ุงู„ู…ุนุฌุจูŠู†</b>\n` +
612
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
613
- `${text}\n` +
614
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
615
  `โค๏ธ ุฅุฌู…ุงู„ูŠ ุงู„ุฅุนุฌุงุจุงุช: ${formatK(totalLikes)}`;
616
  await sendTelegram(msg, null, getStatsButtons(), 'HTML', null, true);
617
  }
618
 
619
  async function sendTopSharersReport() {
620
- const text = formatTopSharers(10);
621
  const msg = `โ†ช๏ธ <b>ุฃูƒุซุฑ ุงู„ู…ุดุงุฑูƒูŠู†</b>\n` +
622
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
623
- `${text}\n` +
624
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
625
  `โ†ช๏ธ ุฅุฌู…ุงู„ูŠ ุงู„ู…ุดุงุฑูƒุงุช: ${formatK(totalShares)}`;
626
  await sendTelegram(msg, null, getStatsButtons(), 'HTML', null, true);
627
  }
628
 
629
  async function sendTopFollowersReport() {
630
- const text = formatTopFollowers(10);
631
  const msg = `โž• <b>ุงู„ู…ุชุงุจุนูŠู† ุงู„ุฌุฏุฏ</b>\n` +
632
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
633
- `${text}\n` +
634
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
635
  `โž• ุฅุฌู…ุงู„ูŠ ุงู„ู…ุชุงุจุนูŠู†: ${formatK(totalFollows)}`;
636
  await sendTelegram(msg, null, getStatsButtons(), 'HTML', null, true);
@@ -641,7 +586,7 @@ async function sendTopChatReport() {
641
  const messages = formatTopChatMessages();
642
  const msg = `๐Ÿ’ฌ <b>ุฃู‡ู… ุงู„ุชุนู„ูŠู‚ุงุช ูˆุงู„ู…ุชูุงุนู„ูŠู†</b>\n` +
643
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
644
- `<b>๐Ÿ“Œ ุฃู‡ู… ุงู„ุฑุณุงุฆู„:</b>\n${messages}\n\n` +
645
  `<b>๐Ÿ‘ฅ ุฃูƒุซุฑ ุงู„ู…ุชูุงุนู„ูŠู†:</b>\n${chatters}\n` +
646
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
647
  `๐Ÿ’ฌ ุฅุฌู…ุงู„ูŠ ุงู„ุชุนู„ูŠู‚ุงุช: ${formatK(totalComments)}`;
@@ -649,20 +594,18 @@ async function sendTopChatReport() {
649
  }
650
 
651
  async function sendTopActiveReport() {
652
- const text = formatTopActive(10);
653
  const msg = `๐Ÿ”ฅ <b>ุฃูƒุซุฑ ุงู„ู…ุชูุงุนู„ูŠู† ุดุงู…ู„ุงู‹</b>\n` +
654
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
655
- `${text}\n` +
656
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
657
  `๐Ÿ“Š ุงู„ู†ู‚ุงุท = ุงู„ู‡ุฏุงูŠุง + ุงู„ุชูุงุนู„ุงุช ร— 10`;
658
  await sendTelegram(msg, null, getStatsButtons(), 'HTML', null, true);
659
  }
660
 
661
  async function sendModeratorsReport() {
662
- const text = formatModerators();
663
  const msg = `๐Ÿ‘ฎ <b>ุงู„ู…ุดุฑููŠู† ูˆุงู„ุฃุนุถุงุก ุงู„ู…ู…ูŠุฒูŠู†</b>\n` +
664
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
665
- `${text}\n` +
666
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”`;
667
  await sendTelegram(msg, null, getStatsButtons(), 'HTML', null, true);
668
  }
@@ -684,11 +627,15 @@ async function sendDeepStatsReport() {
684
  `โ†ช๏ธ <b>ุงู„ู…ุดุงุฑูƒุงุช:</b> ${formatK(totalShares)}\n` +
685
  `โž• <b>ุงู„ู…ุชุงุจุนูŠู†:</b> ${formatK(totalFollows)}\n` +
686
  `๐Ÿ’ฌ <b>ุงู„ุชุนู„ูŠู‚ุงุช:</b> ${formatK(totalComments)}\n` +
687
- `๐ŸŽ <b>ุงู„ู‡ุฏุงูŠุง:</b> ${formatK(totalGifts)} ๐Ÿ’Ž\n\n` +
 
 
688
  `๐Ÿ‘ฅ <b>ุนุฏุฏ ุงู„ุฏุงุนู…ูŠู†:</b> ${Object.keys(giftersMap).length}\n` +
689
  `๐Ÿ’ฌ <b>ุนุฏุฏ ุงู„ู…ุชูุงุนู„ูŠู†:</b> ${Object.keys(chattersMap).length}\n` +
690
  `โž• <b>ุนุฏุฏ ุงู„ู…ุชุงุจุนูŠู† ุงู„ุฌุฏุฏ:</b> ${Object.keys(followersMap).length}\n` +
691
  `๐Ÿ‘ฎ <b>ุนุฏุฏ ุงู„ู…ุดุฑููŠู†:</b> ${Object.keys(moderatorsMap).length}\n` +
 
 
692
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”`;
693
  await sendTelegram(msg, null, getMainButtons(), 'HTML', null, true);
694
  }
@@ -714,18 +661,41 @@ async function sendPKGiftersReport() {
714
  await sendTelegram('โš”๏ธ ู„ุง ูŠูˆุฌุฏ ุฌูˆู„ุฉ PK ู†ุดุทุฉ', null, getMainButtons(), 'HTML', null, true);
715
  return;
716
  }
717
- const pkGifters = formatPKGifters();
718
  const msg = `โš”๏ธ <b>ุฏุงุนู…ูŠู† ุฌูˆู„ุฉ PK</b>\n` +
719
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
720
- `${pkGifters}\n` +
721
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”`;
722
  await sendTelegram(msg, null, getPKButtons(), 'HTML', null, true);
723
  }
724
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
725
  async function sendFinalReport() {
726
  const duration = formatDuration(startTime);
727
  const topGifterText = formatTopGifters(5);
728
  const topActiveText = formatTopActive(5);
 
 
729
  const avgViewers = viewerHistory.length > 0
730
  ? Math.round(viewerHistory.reduce((a, b) => a + b.viewers, 0) / viewerHistory.length)
731
  : 0;
@@ -741,9 +711,13 @@ async function sendFinalReport() {
741
  `โ†ช๏ธ <b>ุฅุฌู…ุงู„ูŠ ุงู„ู…ุดุงุฑูƒุงุช:</b> ${formatK(totalShares)}\n` +
742
  `โž• <b>ุฅุฌู…ุงู„ูŠ ุงู„ู…ุชุงุจุนูŠู†:</b> ${formatK(totalFollows)}\n` +
743
  `๐Ÿ’ฌ <b>ุฅุฌู…ุงู„ูŠ ุงู„ุชุนู„ูŠู‚ุงุช:</b> ${formatK(totalComments)}\n` +
744
- `๐ŸŽ <b>ุฅุฌู…ุงู„ูŠ ุงู„ู‡ุฏุงูŠุง:</b> ${formatK(totalGifts)} ๐Ÿ’Ž\n\n` +
 
 
745
  `๐Ÿ† <b>ู„ูˆุญุฉ ุงู„ุดุฑู (ุงู„ุฏุงุนู…ูŠู†):</b>\n${topGifterText}\n\n` +
746
- `๐Ÿ”ฅ <b>ุฃูƒุซุฑ ุงู„ู…ุชูุงุนู„ูŠู†:</b>\n${topActiveText}\n` +
 
 
747
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
748
  `๐Ÿ’ค ุงู„ุจูˆุช ููŠ ูˆุถุน ุงู„ุงู†ุชุธุงุฑ...`;
749
  await sendTelegram(msg, null, getDisconnectedButtons(), 'HTML', null, false);
@@ -754,13 +728,11 @@ async function sendFinalReport() {
754
  // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
755
  async function notifyLike(nickname, likeCount, userId) {
756
  if (!isLive) return;
757
- if (!likersMap[userId]) {
758
- likersMap[userId] = { id: userId, name: nickname, count: 0 };
759
- }
760
  likersMap[userId].count += likeCount;
761
  totalLikes += likeCount;
762
 
763
- if (pkActive) pkInfo.ourLikes += likeCount;
764
 
765
  if (likeCount >= CONFIG.LIKE_BATCH_THRESHOLD) {
766
  if (isOnCooldown('like', userId, 30000)) return;
@@ -775,17 +747,13 @@ async function notifyGift(nickname, giftName, diamondCount, repeatCount, totalCo
775
  if (!isLive) return;
776
  if (isOnCooldown('gift', userId, CONFIG.GIFT_COOLDOWN_MS)) return;
777
 
778
- if (!giftersMap[userId]) {
779
- giftersMap[userId] = { id: userId, name: nickname, coins: 0 };
780
- }
781
  giftersMap[userId].coins += totalCost;
782
  totalGifts += totalCost;
783
 
784
  if (pkActive) {
785
  pkInfo.ourScore += totalCost;
786
- if (!pkInfo.pkGifts[userId]) {
787
- pkInfo.pkGifts[userId] = { id: userId, name: nickname, coins: 0 };
788
- }
789
  pkInfo.pkGifts[userId].coins += totalCost;
790
  }
791
 
@@ -805,9 +773,7 @@ async function notifyFollow(nickname, userId) {
805
  if (!isLive) return;
806
  if (isOnCooldown('follow', userId, 10000)) return;
807
 
808
- if (!followersMap[userId]) {
809
- followersMap[userId] = { id: userId, name: nickname, count: 0 };
810
- }
811
  followersMap[userId].count++;
812
  totalFollows++;
813
 
@@ -821,9 +787,7 @@ async function notifyShare(nickname, userId) {
821
  if (!isLive) return;
822
  if (isOnCooldown('share', userId, 15000)) return;
823
 
824
- if (!sharersMap[userId]) {
825
- sharersMap[userId] = { id: userId, name: nickname, count: 0 };
826
- }
827
  sharersMap[userId].count++;
828
  totalShares++;
829
 
@@ -833,31 +797,32 @@ async function notifyShare(nickname, userId) {
833
  );
834
  }
835
 
836
- async function notifyChat(data) {
837
- if (!isLive || !CONFIG.SHOW_TOP_CHAT) return;
838
- const userId = data.userId;
839
- const nickname = data.nickname || data.uniqueId;
840
- const text = cleanChatMessage(data.comment);
 
 
 
 
841
 
842
  if (!text || isSpamMessage(text)) return;
843
- if (isOnCooldown('chat', userId, CONFIG.CHAT_COOLDOWN_MS)) return;
844
 
845
- if (!chattersMap[userId]) {
846
- chattersMap[userId] = { id: userId, name: nickname, count: 0 };
847
- }
848
  chattersMap[userId].count++;
849
  totalComments++;
850
 
851
  topChatMessages.unshift({ name: nickname, text: text, time: Date.now() });
852
- if (topChatMessages.length > CONFIG.TOP_CHAT_LIMIT * 2) {
853
- topChatMessages = topChatMessages.slice(0, CONFIG.TOP_CHAT_LIMIT * 2);
854
- }
855
 
856
- const importantKeywords = ['ุณุคุงู„', 'ุฌูˆุงุจ', 'ู‡ู„ุง', 'ู…ุฑุญุจุง', 'ุชูุงุนู„', 'ุฏุนู…', 'pk', 'ุจูƒุฌ'];
857
- const isImportant = text.length > 20 || importantKeywords.some(kw => text.toLowerCase().includes(kw));
858
 
859
- if (isImportant && !isOnCooldown('chat_important', userId, 30000)) {
860
- await sendTelegram(
 
 
 
861
  `๐Ÿ’ฌ <b>${escapeHtml(nickname)}:</b> ${escapeHtml(text)}`,
862
  null, null, 'HTML', null, true
863
  );
@@ -866,15 +831,15 @@ async function notifyChat(data) {
866
 
867
  async function notifyModerator(data) {
868
  if (!isLive) return;
869
- const userId = data.userId;
870
- const nickname = data.nickname || data.uniqueId;
871
 
872
  if (!moderatorsMap[userId]) {
873
  moderatorsMap[userId] = {
874
  id: userId,
875
  name: nickname,
876
  joinTime: Date.now(),
877
- isModerator: data.isModerator || false,
878
  };
879
  await sendTelegram(
880
  `๐Ÿ‘ฎโœจ <b>${escapeHtml(nickname)}</b> ุฏุฎู„ ุงู„ุจุซ${data.isModerator ? ' [ู…ุดุฑู]' : ''}!`,
@@ -883,12 +848,73 @@ async function notifyModerator(data) {
883
  }
884
  }
885
 
886
- async function notifyPKStart(opponentName) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
887
  pkActive = true;
888
  pkInfo = {
889
- opponentName: opponentName || 'Unknown',
890
- ourLikes: totalLikes,
891
- theirLikes: 0,
892
  ourScore: 0,
893
  theirScore: 0,
894
  startTime: new Date(),
@@ -899,13 +925,17 @@ async function notifyPKStart(opponentName) {
899
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
900
  `๐ŸฅŠ <b>ุงู„ู…ู†ุงูุณ:</b> ${escapeHtml(opponentName)}\n\n` +
901
  `ุงู„ููˆุฒ ูŠุญุชุงุฌ ูˆู‚ูุฉ ุงู„ุฃุจุทุงู„! ุดุฏูˆุง ุงู„ุญูŠู„.. ๐Ÿš€`;
902
- await sendTelegram(msg, null, getPKButtons(), 'HTML', null, false);
903
  }
904
 
905
- async function notifyPKEnd(winner) {
 
906
  pkActive = false;
 
907
  const bar = generateProgressBar(pkInfo.ourScore, pkInfo.theirScore, 10);
908
- const result = winner === 'Draw' ? '๐Ÿค ุชุนุงุฏู„' : `๐Ÿ† ุงู„ูุงุฆุฒ: ${escapeHtml(winner)}`;
 
 
909
  const pkGifters = formatPKGifters();
910
 
911
  const msg = `๐Ÿ†โš”๏ธ <b>ุงู†ุชู‡ุช ุงู„ู…ูˆุงุฌู‡ุฉ!</b>\n` +
@@ -916,19 +946,25 @@ async function notifyPKEnd(winner) {
916
  `${bar}\n\n` +
917
  `๐Ÿ† <b>ุฏุงุนู…ูŠู† ุงู„ุฌูˆู„ุฉ:</b>\n${pkGifters}\n\n` +
918
  `โœจ ุดูƒุฑุงู‹ ู„ูƒู„ ู…ู† ุณุงู‡ู… ููŠ ู‡ุฐู‡ ุงู„ุฌูˆู„ุฉ!`;
919
- await sendTelegram(msg, null, getMainButtons(), 'HTML', null, false);
920
 
921
- pkInfo = {
922
- opponentName: '',
923
- ourLikes: 0,
924
- theirLikes: 0,
925
- ourScore: 0,
926
- theirScore: 0,
927
- startTime: null,
928
- pkGifts: {},
929
- };
 
 
 
930
  }
931
 
 
 
 
932
  async function notifyStreamStart(state) {
933
  isLive = true;
934
  const roomInfo = state.roomInfo || {};
@@ -978,6 +1014,7 @@ function createConnection() {
978
  }
979
 
980
  function setupEvents(connection) {
 
981
  connection.on('roomUser', (data) => {
982
  const viewers = data.viewerCount || 0;
983
  if (viewers > maxViewers) maxViewers = viewers;
@@ -985,56 +1022,72 @@ function setupEvents(connection) {
985
  if (viewerHistory.length > 200) viewerHistory.shift();
986
  });
987
 
 
988
  connection.on('like', (data) => {
989
  const likes = data.likeCount || 0;
990
- const userId = data.userId;
991
  const nickname = data.nickname || data.uniqueId;
992
  notifyLike(nickname, likes, userId);
993
  });
994
 
995
- connection.on('share', (data) => {
996
- const userId = data.userId;
 
997
  const nickname = data.nickname || data.uniqueId;
998
- notifyShare(nickname, userId);
999
- });
1000
-
1001
- connection.on('follow', (data) => {
1002
- const userId = data.userId;
1003
- const nickname = data.nickname || data.uniqueId;
1004
- notifyFollow(nickname, userId);
 
 
1005
  });
1006
 
 
1007
  connection.on('chat', (data) => {
1008
- notifyChat(data);
1009
  });
1010
 
 
1011
  connection.on('member', (data) => {
1012
  notifyModerator(data);
1013
  });
1014
 
 
1015
  connection.on('gift', (data) => {
1016
  if (data.giftType === 1 && !data.repeatEnd) return;
1017
  const giftCost = (data.diamondCount || 0) * (data.repeatCount || 1);
1018
- const userId = data.userId;
1019
  const nickname = data.nickname || data.uniqueId;
1020
  notifyGift(nickname, data.giftName || 'Gift', data.diamondCount || 0, data.repeatCount || 1, giftCost, userId);
1021
  });
1022
 
1023
- connection.on('pkStart', (data) => {
1024
- notifyPKStart(data.opponentNickname);
 
 
1025
  });
1026
 
1027
- connection.on('pkUpdate', (data) => {
1028
- if (!pkActive) return;
1029
- pkInfo.ourLikes = data.ourLikes || pkInfo.ourLikes;
1030
- pkInfo.theirLikes = data.theirLikes || 0;
1031
  });
1032
 
1033
- connection.on('pkEnd', (data) => {
1034
- const winner = data.winnerNickname || 'Draw';
1035
- notifyPKEnd(winner);
 
1036
  });
1037
 
 
 
 
 
 
 
1038
  connection.on('streamEnd', async () => {
1039
  isConnected = false;
1040
  isLive = false;
@@ -1043,6 +1096,14 @@ function setupEvents(connection) {
1043
  reconnectTimer = setTimeout(startRadar, 60000);
1044
  });
1045
 
 
 
 
 
 
 
 
 
1046
  connection.on('error', (err) => {
1047
  console.error(`[TikTok] Error: ${err.message}`);
1048
  });
@@ -1058,35 +1119,16 @@ async function startRadar() {
1058
  reconnectAttempt++;
1059
 
1060
  if (reconnectAttempt === 1) {
1061
- maxViewers = 0;
1062
- totalLikes = 0;
1063
- totalShares = 0;
1064
- totalGifts = 0;
1065
- totalFollows = 0;
1066
- totalComments = 0;
1067
- giftersMap = {};
1068
- likersMap = {};
1069
- sharersMap = {};
1070
- followersMap = {};
1071
- chattersMap = {};
1072
- moderatorsMap = {};
1073
  topChatMessages = [];
1074
  viewerHistory = [];
1075
  pkActive = false;
1076
- pkInfo = {
1077
- opponentName: '',
1078
- ourLikes: 0,
1079
- theirLikes: 0,
1080
- ourScore: 0,
1081
- theirScore: 0,
1082
- startTime: null,
1083
- pkGifts: {},
1084
- };
1085
- cooldowns.chat.clear();
1086
- cooldowns.gift.clear();
1087
- cooldowns.like.clear();
1088
- cooldowns.follow.clear();
1089
- cooldowns.share.clear();
1090
  }
1091
 
1092
  try {
@@ -1152,9 +1194,7 @@ function disconnectRadar() {
1152
  isConnected = false;
1153
  isLive = false;
1154
  if (tiktokConnection) {
1155
- try {
1156
- tiktokConnection.disconnect();
1157
- } catch (e) {}
1158
  tiktokConnection = null;
1159
  }
1160
  if (reconnectTimer) clearTimeout(reconnectTimer);
@@ -1177,19 +1217,20 @@ function startPeriodicReports() {
1177
  }
1178
 
1179
  // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1180
- // โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ๏ธ ุงู„ุฎุงุฏู… ุงู„ุฏุงุฎู„ูŠ (ู„ู„ู†ุจุถ + ุตุญุฉ ุงู„ุจูˆุช) โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
1181
  // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1182
  const server = http.createServer((req, res) => {
1183
  res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
1184
  const status = isLive ? '๐Ÿ”ด LIVE' : isConnected ? '๐ŸŸก CONNECTING' : 'โšช WAITING';
1185
- res.end(`TikTok Radar Bot v4.0 | ${status} | ${getBaghdadTime()} | @${CONFIG.TIKTOK_USERNAME}`);
 
1186
  });
1187
 
1188
  const PORT = process.env.PORT || 7860;
1189
  server.listen(PORT, '0.0.0.0', () => {
1190
  console.log(`
1191
  โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
1192
- โ•‘ TikTok Radar Pro - Telegram Bot v4.0 (Royal Edition) โ•‘
1193
  โ•‘ โ•‘
1194
  โ•‘ ๐Ÿค– Telegram Bot Active โ•‘
1195
  โ•‘ ๐Ÿ“ก TikTok: @${CONFIG.TIKTOK_USERNAME.padEnd(45, ' ')}โ•‘
@@ -1199,6 +1240,7 @@ server.listen(PORT, '0.0.0.0', () => {
1199
  โ•‘ โ•‘
1200
  โ•‘ โœจ Features: Anti-Spam | Smart Cooldowns | Deep Stats โ•‘
1201
  โ•‘ ๐ŸŽ Gifts | โค๏ธ Likes | โ†ช๏ธ Shares | ๐Ÿ’ฌ Chat | โš”๏ธ PK โ•‘
 
1202
  โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1203
  `);
1204
 
 
1
  /**
2
  * โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
3
+ * โ•‘ TikTok Radar Pro - Telegram Bot v4.2 (Royal Edition) โ•‘
4
  * โ•‘ ุจูˆุช ุงุญุชุฑุงููŠ ูŠุฑุงู‚ุจ ุจุซ TikTok ูˆูŠุฑุณู„ ุงู„ุฅุญุตุงุฆูŠุงุช ู„ู„ู‚ู†ุงุฉ โ•‘
5
  * โ•‘ ู…ุน ุฃุฒุฑุงุฑ ุชูุงุนู„ูŠุฉ + ุฌุณุฑ Cloudflare + ู†ุจุถ ุฏุงุฎู„ูŠ + ู†ุธุงู… ู…ูƒุงูุญุฉ ุฅุฒุนุงุฌ โ•‘
6
+ * โ•‘ + ุตู†ุงุฏูŠู‚ ุงู„ู‡ุฏุงูŠุง + ุญู‚ูŠุจุฉ ุงู„ุนู…ู„ุงุช โ•‘
7
  * โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
8
  */
9
 
 
26
  TELEGRAM_CHAT_ID: cleanEnv(process.env.TELEGRAM_CHAT_ID, ''),
27
  PROXY_URL: cleanEnv(process.env.PROXY_URL, '').replace(/\/$/, ''),
28
  REPORT_INTERVAL_MINUTES: parseInt(cleanEnv(process.env.REPORT_INTERVAL, '10')) || 10,
29
+ CHAT_COOLDOWN_MS: 3000,
30
+ GIFT_COOLDOWN_MS: 2000,
31
+ LIKE_BATCH_THRESHOLD: 50,
32
+ SHOW_TOP_CHAT: true,
33
+ TOP_CHAT_LIMIT: 10,
34
+ SPAM_KEYWORDS: ['spam', 'scam', 'fake', 'click here', 'free money', 'earn now'],
 
 
 
35
  };
36
 
37
  console.log('[Config] Loaded settings:');
 
52
  let totalGifts = 0;
53
  let totalFollows = 0;
54
  let totalComments = 0;
55
+ let totalEnvelopes = 0;
56
+ let totalCoinsGiven = 0;
57
  let giftersMap = {};
58
  let likersMap = {};
59
  let sharersMap = {};
60
  let followersMap = {};
61
  let chattersMap = {};
62
  let moderatorsMap = {};
63
+ let envelopeWinnersMap = {};
64
+ let coinPurseWinnersMap = {};
65
  let topChatMessages = [];
66
  let startTime = null;
67
  let lastReportTime = null;
 
69
  let reconnectTimer = null;
70
  let reportInterval = null;
71
  let lastMessageId = null;
 
72
  let reconnectAttempt = 0;
73
  let lastReconnectNotify = 0;
74
 
 
79
  like: new Map(),
80
  follow: new Map(),
81
  share: new Map(),
82
+ envelope: new Map(),
83
+ coinPurse: new Map(),
84
  };
85
 
86
+ // PK / Battle System
87
  let pkActive = false;
88
  let pkInfo = {
89
  opponentName: '',
90
+ opponentUserId: '',
 
91
  ourScore: 0,
92
  theirScore: 0,
93
  startTime: null,
 
164
  function isSpamMessage(text) {
165
  if (!text) return true;
166
  const lower = text.toLowerCase();
167
+ return CONFIG.SPAM_KEYWORDS.some(kw => lower.includes(kw.toLowerCase()));
168
  }
169
 
170
  function cleanChatMessage(text) {
171
  if (!text) return '';
172
  let cleaned = text.replace(/https?:\/\/\S+/gi, '[ุฑุงุจุท]');
173
+ cleaned = cleaned.replace(/(.)(?=\1{5,})/g, '');
174
+ if (cleaned.length > 120) cleaned = cleaned.substring(0, 120) + '...';
175
  return cleaned.trim();
176
  }
177
 
 
228
  function getMainButtons() {
229
  const liveLink = `https://www.tiktok.com/@${CONFIG.TIKTOK_USERNAME}/live`;
230
  return [
231
+ [{ text: '๐Ÿ”ด ุฏุฎูˆู„ ุงู„ุจุซ ุงู„ู…ุจุงุดุฑ', url: liveLink }],
 
 
232
  [
233
  { text: '๐Ÿ“Š ุชู‚ุฑูŠุฑ ููˆุฑูŠ', callback_data: 'report_now' },
234
  { text: '๐Ÿ† ู„ูˆุญุฉ ุงู„ุดุฑู', callback_data: 'top_gifters' },
 
241
  { text: 'โš”๏ธ ุญุงู„ุฉ PK', callback_data: 'pk_status' },
242
  { text: '๐Ÿ“ˆ ุฅุญุตุงุฆูŠุงุช', callback_data: 'deep_stats' },
243
  ],
244
+ [
245
+ { text: '๐ŸŽ ุงู„ุตู†ุงุฏูŠู‚', callback_data: 'envelopes' },
246
+ { text: '๐Ÿ’ฐ ุงู„ุนู…ู„ุงุช', callback_data: 'coin_purse' },
247
+ ],
248
  [
249
  { text: isConnected ? '๐Ÿ”ด ู‚ุทุน ุงู„ุงุชุตุงู„' : '๐ŸŸข ุงู„ุงุชุตุงู„', callback_data: isConnected ? 'disconnect' : 'connect' },
250
  ],
 
254
  function getPKButtons() {
255
  const liveLink = `https://www.tiktok.com/@${CONFIG.TIKTOK_USERNAME}/live`;
256
  return [
257
+ [{ text: '๐Ÿš€ ุฏุนู… ุงู„ุจุซ ุงู„ุขู†', url: liveLink }],
 
 
258
  [
259
  { text: '๐Ÿ“Š ุชุญุฏูŠุซ ุงู„ู†ุชูŠุฌุฉ', callback_data: 'pk_update' },
260
  { text: '๐Ÿ† ุฏุงุนู…ูŠู† ุงู„ุฌูˆู„ุฉ', callback_data: 'pk_gifters' },
261
  ],
262
+ [{ text: 'โฌ…๏ธ ุงู„ู‚ุงุฆู…ุฉ ุงู„ุฑุฆูŠุณูŠุฉ', callback_data: 'main_menu' }],
 
 
263
  ];
264
  }
265
 
266
  function getDisconnectedButtons() {
267
  return [
268
+ [{ text: '๐Ÿ”„ ุฅุนุงุฏุฉ ุงู„ุงุชุตุงู„', callback_data: 'connect' }],
269
+ [{ text: '๐Ÿ“Š ุงู„ุชู‚ุฑูŠุฑ ุงู„ู†ู‡ุงุฆูŠ', callback_data: 'final_report' }],
 
 
 
 
270
  ];
271
  }
272
 
 
285
  { text: '๐Ÿ‘ฎ ุงู„ู…ุดุฑููŠู†', callback_data: 'moderators' },
286
  ],
287
  [
288
+ { text: '๐ŸŽ ุงู„ุตู†ุงุฏูŠู‚', callback_data: 'envelopes' },
289
+ { text: '๐Ÿ’ฐ ุงู„ุนู…ู„ุงุช', callback_data: 'coin_purse' },
290
  ],
291
+ [{ text: 'โฌ…๏ธ ุงู„ู‚ุงุฆู…ุฉ ุงู„ุฑุฆูŠุณูŠุฉ', callback_data: 'main_menu' }],
292
  ];
293
  }
294
 
 
312
  disconnectRadar();
313
  await sendTelegram(
314
  `๐Ÿ”ด ุชู… ู‚ุทุน ุงู„ุงุชุตุงู„\nโฐ ${getBaghdadTime()}`,
315
+ null, getDisconnectedButtons(), 'HTML', null, true
 
 
 
 
316
  );
317
  }
318
  break;
 
325
  }
326
  break;
327
 
328
+ case 'top_gifters': await sendTopGiftersReport(); break;
329
+ case 'top_likers': await sendTopLikersReport(); break;
330
+ case 'top_sharers': await sendTopSharersReport(); break;
331
+ case 'top_followers': await sendTopFollowersReport(); break;
332
+ case 'top_chat': await sendTopChatReport(); break;
333
+ case 'top_active': await sendTopActiveReport(); break;
334
+ case 'moderators': await sendModeratorsReport(); break;
335
+ case 'deep_stats': await sendDeepStatsReport(); break;
336
 
337
+ case 'envelopes': await sendEnvelopesReport(); break;
338
+ case 'coin_purse': await sendCoinPurseReport(); break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
 
340
  case 'pk_update':
341
+ if (pkActive) { await sendPKUpdate(); }
342
+ else { await sendTelegram('โš”๏ธ ู„ุง ูŠูˆุฌุฏ ุฌูˆู„ุฉ PK ู†ุดุทุฉ ุญุงู„ูŠุงู‹', null, getMainButtons(), 'HTML', null, true); }
 
 
 
343
  break;
344
 
345
  case 'pk_status':
346
+ if (pkActive) { await sendPKUpdate(); }
347
+ else { await sendTelegram('โš”๏ธ ู„ุง ูŠูˆุฌุฏ ุฌูˆู„ุฉ PK ู†ุดุทุฉ ุญุงู„ูŠุงู‹', null, getMainButtons(), 'HTML', null, true); }
 
 
 
 
 
 
 
348
  break;
349
 
350
+ case 'pk_gifters': await sendPKGiftersReport(); break;
351
+ case 'final_report': await sendFinalReport(); break;
352
+ case 'main_menu': await sendTelegram('๐Ÿ“ก ุงู„ู‚ุงุฆู…ุฉ ุงู„ุฑุฆูŠุณูŠุฉ', null, getMainButtons(), 'HTML', null, true); break;
 
 
 
 
353
 
354
  default:
355
  console.log(`[Callback] Unknown: ${callbackData}`);
356
  }
357
  }
358
 
 
359
  async function pollCallbacks() {
360
  let offset = 0;
361
  const urls = [];
 
400
  // โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ๏ธ ุงู„ุชู‚ุงุฑูŠุฑ ูˆุงู„ุฅุดุนุงุฑุงุช โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
401
  // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
402
  function getTopGifters(top = 5) {
403
+ return Object.values(giftersMap).sort((a, b) => b.coins - a.coins).slice(0, top);
 
 
404
  }
405
 
406
  function getTopLikers(top = 5) {
407
+ return Object.values(likersMap).sort((a, b) => b.count - a.count).slice(0, top);
 
 
408
  }
409
 
410
  function getTopSharers(top = 5) {
411
+ return Object.values(sharersMap).sort((a, b) => b.count - a.count).slice(0, top);
 
 
412
  }
413
 
414
  function getTopFollowers(top = 5) {
415
+ return Object.values(followersMap).sort((a, b) => b.count - a.count).slice(0, top);
 
 
416
  }
417
 
418
  function getTopChatters(top = 5) {
419
+ return Object.values(chattersMap).sort((a, b) => b.count - a.count).slice(0, top);
 
 
420
  }
421
 
422
  function getTopActiveUsers(top = 5) {
 
424
  [...Object.values(giftersMap), ...Object.values(likersMap), ...Object.values(sharersMap),
425
  ...Object.values(followersMap), ...Object.values(chattersMap)].forEach(u => {
426
  const key = u.id || u.name;
427
+ if (!allUsers[key]) allUsers[key] = { name: u.name, score: 0 };
 
 
428
  allUsers[key].score += (u.coins || 0) + (u.count || 0) * 10;
429
  });
430
+ return Object.values(allUsers).sort((a, b) => b.score - a.score).slice(0, top);
 
 
431
  }
432
 
433
  function getModeratorsList() {
434
+ return Object.values(moderatorsMap).sort((a, b) => b.joinTime - a.joinTime);
435
+ }
436
+
437
+ function getTopEnvelopeWinners(top = 5) {
438
+ return Object.values(envelopeWinnersMap).sort((a, b) => b.count - a.count).slice(0, top);
439
+ }
440
+
441
+ function getTopCoinPurseWinners(top = 5) {
442
+ return Object.values(coinPurseWinnersMap).sort((a, b) => b.coins - a.coins).slice(0, top);
443
  }
444
 
445
  function formatTopGifters(top = 5) {
446
  const list = getTopGifters(top);
447
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ุฏุงุนู…ูŠู† ููŠ ู‡ุฐุง ุงู„ุจุซ';
448
  const medals = ['๐Ÿฅ‡', '๐Ÿฅˆ', '๐Ÿฅ‰', '๐Ÿ…', '๐ŸŽ–', '๐ŸŽ—', '๐Ÿต', '๐ŸŽ–', '๐Ÿ…', '๐Ÿฅ‰'];
449
+ return list.map((g, i) => `${medals[i] || '๐Ÿ…'} ${escapeHtml(g.name)} ยป ${formatK(g.coins)} ๐Ÿ’Ž`).join('\n');
 
 
450
  }
451
 
452
  function formatTopLikers(top = 5) {
453
  const list = getTopLikers(top);
454
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ู…ุนุฌุจูŠู† ู…ุณุฌู„ูŠู†';
455
  const medals = ['๐Ÿฅ‡', '๐Ÿฅˆ', '๐Ÿฅ‰', '๐Ÿ…', '๐ŸŽ–'];
456
+ return list.map((g, i) => `${medals[i] || 'โค๏ธ'} ${escapeHtml(g.name)} ยป ${formatK(g.count)} โค๏ธ`).join('\n');
 
 
457
  }
458
 
459
  function formatTopSharers(top = 5) {
460
  const list = getTopSharers(top);
461
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ู…ุดุงุฑูƒูŠู† ู…ุณุฌู„ูŠู†';
462
+ return list.map((g, i) => `${i + 1}. ${escapeHtml(g.name)} ยป ${formatK(g.count)} โ†ช๏ธ`).join('\n');
 
 
463
  }
464
 
465
  function formatTopFollowers(top = 5) {
466
  const list = getTopFollowers(top);
467
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ู…ุชุงุจุนูŠู† ุฌุฏุฏ ู…ุณุฌู„ูŠู†';
468
+ return list.map((g, i) => `${i + 1}. ${escapeHtml(g.name)} โž•`).join('\n');
 
 
469
  }
470
 
471
  function formatTopChatters(top = 5) {
472
  const list = getTopChatters(top);
473
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ู…ุชูุงุนู„ูŠู† ููŠ ุงู„ุฏุฑุฏุดุฉ';
474
+ return list.map((g, i) => `${i + 1}. ${escapeHtml(g.name)} ยป ${formatK(g.count)} ๐Ÿ’ฌ`).join('\n');
 
 
475
  }
476
 
477
  function formatTopActive(top = 5) {
478
  const list = getTopActiveUsers(top);
479
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ู…ุชูุงุนู„ูŠู† ุจุนุฏ';
480
  const medals = ['๐Ÿ‘‘', '๐Ÿฅˆ', '๐Ÿฅ‰', 'โญ', 'โœจ'];
481
+ return list.map((g, i) => `${medals[i] || '๐Ÿ”น'} ${escapeHtml(g.name)} ยป ${formatK(g.score)} ู†ู‚ุทุฉ`).join('\n');
 
 
482
  }
483
 
484
  function formatModerators() {
485
  const list = getModeratorsList();
486
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ู…ุดุฑููŠู† ุฏุฎู„ูˆุง ุงู„ุจุซ';
487
+ return list.map((m, i) => `${i + 1}. ๐Ÿ‘ฎ ${escapeHtml(m.name)}${m.isModerator ? ' [ู…ุดุฑู]' : ''}`).join('\n');
 
 
488
  }
489
 
490
  function formatTopChatMessages() {
491
+ if (topChatMessages.length === 0) return 'ู„ุง ุชูˆุฌุฏ ุฑุณุงุฆู„ ุจุนุฏ';
492
  return topChatMessages
493
+ .slice(0, CONFIG.TOP_CHAT_LIMIT)
494
+ .map((msg, i) => `๐Ÿ’ฌ <b>${escapeHtml(msg.name)}:</b> ${escapeHtml(msg.text)}`)
495
  .join('\n');
496
  }
497
 
498
  function formatPKGifters() {
499
+ const list = Object.values(pkInfo.pkGifts || {}).sort((a, b) => b.coins - a.coins).slice(0, 5);
 
 
500
  if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ุฏุงุนู…ูŠู† ููŠ ุงู„ุฌูˆู„ุฉ';
501
+ return list.map((g, i) => `${i + 1}. ${escapeHtml(g.name)} ยป ${formatK(g.coins)} ๐Ÿ’Ž`).join('\n');
502
+ }
503
+
504
+ function formatEnvelopeWinners(top = 10) {
505
+ const list = getTopEnvelopeWinners(top);
506
+ if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ูุงุฆุฒูŠู† ุจุงู„ุตู†ุงุฏูŠู‚ ุจุนุฏ';
507
+ return list.map((w, i) => `${i + 1}. ๐ŸŽ ${escapeHtml(w.name)} ยป ${formatK(w.count)} ุตู†ุฏูˆู‚`).join('\n');
508
+ }
509
+
510
+ function formatCoinPurseWinners(top = 10) {
511
+ const list = getTopCoinPurseWinners(top);
512
+ if (list.length === 0) return 'ู„ุง ูŠูˆุฌุฏ ูุงุฆุฒูŠู† ุจุญู‚ูŠุจุฉ ุงู„ุนู…ู„ุงุช ุจุนุฏ';
513
+ return list.map((w, i) => `${i + 1}. ๐Ÿ’ฐ ${escapeHtml(w.name)} ยป ${formatK(w.coins)} ุนู…ู„ุฉ`).join('\n');
514
  }
515
 
516
  // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 
534
  `โ†ช๏ธ <b>ุงู„ู…ุดุงุฑูƒุงุช:</b> ${formatK(totalShares)}\n` +
535
  `โž• <b>ุงู„ู…ุชุงุจุนูŠู† ุงู„ุฌุฏุฏ:</b> ${formatK(totalFollows)}\n` +
536
  `๐Ÿ’ฌ <b>ุงู„ุชุนู„ูŠู‚ุงุช:</b> ${formatK(totalComments)}\n` +
537
+ `๐ŸŽ <b>ุงู„ู‡ุฏุงูŠุง:</b> ${formatK(totalGifts)} ๐Ÿ’Ž\n` +
538
+ `๐Ÿ“ฆ <b>ุงู„ุตู†ุงุฏูŠู‚:</b> ${formatK(totalEnvelopes)}\n` +
539
+ `๐Ÿ’ฐ <b>ุงู„ุนู…ู„ุงุช ุงู„ู…ูˆุฒุนุฉ:</b> ${formatK(totalCoinsGiven)}\n\n` +
540
  `๐Ÿ† <b>ุฃูุถู„ ุงู„ุฏุงุนู…ูŠู†:</b>\n${topGifterText}\n\n` +
541
  `โค๏ธ <b>ุฃูƒุซุฑ ุงู„ู…ุนุฌุจูŠู†:</b>\n${topLikerText}\n` +
542
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”`;
 
546
  }
547
 
548
  async function sendTopGiftersReport() {
 
549
  const msg = `๐Ÿ‘‘ <b>ู„ูˆุญุฉ ุงู„ุดุฑู - ุฃูุถู„ ุงู„ุฏุงุนู…ูŠู†</b>\n` +
550
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
551
+ `${formatTopGifters(10)}\n` +
552
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
553
  `๐Ÿ’Ž ุฅุฌู…ุงู„ูŠ ุงู„ู‡ุฏุงูŠุง: ${formatK(totalGifts)}`;
554
  await sendTelegram(msg, null, getStatsButtons(), 'HTML', null, true);
555
  }
556
 
557
  async function sendTopLikersReport() {
 
558
  const msg = `โค๏ธ <b>ุฃูƒุซุฑ ุงู„ู…ุนุฌุจูŠู†</b>\n` +
559
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
560
+ `${formatTopLikers(10)}\n` +
561
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
562
  `โค๏ธ ุฅุฌู…ุงู„ูŠ ุงู„ุฅุนุฌุงุจุงุช: ${formatK(totalLikes)}`;
563
  await sendTelegram(msg, null, getStatsButtons(), 'HTML', null, true);
564
  }
565
 
566
  async function sendTopSharersReport() {
 
567
  const msg = `โ†ช๏ธ <b>ุฃูƒุซุฑ ุงู„ู…ุดุงุฑูƒูŠู†</b>\n` +
568
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
569
+ `${formatTopSharers(10)}\n` +
570
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
571
  `โ†ช๏ธ ุฅุฌู…ุงู„ูŠ ุงู„ู…ุดุงุฑูƒุงุช: ${formatK(totalShares)}`;
572
  await sendTelegram(msg, null, getStatsButtons(), 'HTML', null, true);
573
  }
574
 
575
  async function sendTopFollowersReport() {
 
576
  const msg = `โž• <b>ุงู„ู…ุชุงุจุนูŠู† ุงู„ุฌุฏุฏ</b>\n` +
577
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
578
+ `${formatTopFollowers(10)}\n` +
579
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
580
  `โž• ุฅุฌู…ุงู„ูŠ ุงู„ู…ุชุงุจุนูŠู†: ${formatK(totalFollows)}`;
581
  await sendTelegram(msg, null, getStatsButtons(), 'HTML', null, true);
 
586
  const messages = formatTopChatMessages();
587
  const msg = `๐Ÿ’ฌ <b>ุฃู‡ู… ุงู„ุชุนู„ูŠู‚ุงุช ูˆุงู„ู…ุชูุงุนู„ูŠู†</b>\n` +
588
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
589
+ `<b>๐Ÿ“Œ ุฃุญุฏุซ ุงู„ุฑุณุงุฆู„:</b>\n${messages}\n\n` +
590
  `<b>๐Ÿ‘ฅ ุฃูƒุซุฑ ุงู„ู…ุชูุงุนู„ูŠู†:</b>\n${chatters}\n` +
591
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
592
  `๐Ÿ’ฌ ุฅุฌู…ุงู„ูŠ ุงู„ุชุนู„ูŠู‚ุงุช: ${formatK(totalComments)}`;
 
594
  }
595
 
596
  async function sendTopActiveReport() {
 
597
  const msg = `๐Ÿ”ฅ <b>ุฃูƒุซุฑ ุงู„ู…ุชูุงุนู„ูŠู† ุดุงู…ู„ุงู‹</b>\n` +
598
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
599
+ `${formatTopActive(10)}\n` +
600
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
601
  `๐Ÿ“Š ุงู„ู†ู‚ุงุท = ุงู„ู‡ุฏุงูŠุง + ุงู„ุชูุงุนู„ุงุช ร— 10`;
602
  await sendTelegram(msg, null, getStatsButtons(), 'HTML', null, true);
603
  }
604
 
605
  async function sendModeratorsReport() {
 
606
  const msg = `๐Ÿ‘ฎ <b>ุงู„ู…ุดุฑููŠู† ูˆุงู„ุฃุนุถุงุก ุงู„ู…ู…ูŠุฒูŠู†</b>\n` +
607
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
608
+ `${formatModerators()}\n` +
609
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”`;
610
  await sendTelegram(msg, null, getStatsButtons(), 'HTML', null, true);
611
  }
 
627
  `โ†ช๏ธ <b>ุงู„ู…ุดุงุฑูƒุงุช:</b> ${formatK(totalShares)}\n` +
628
  `โž• <b>ุงู„ู…ุชุงุจุนูŠู†:</b> ${formatK(totalFollows)}\n` +
629
  `๐Ÿ’ฌ <b>ุงู„ุชุนู„ูŠู‚ุงุช:</b> ${formatK(totalComments)}\n` +
630
+ `๐ŸŽ <b>ุงู„ู‡ุฏุงูŠุง:</b> ${formatK(totalGifts)} ๐Ÿ’Ž\n` +
631
+ `๐Ÿ“ฆ <b>ุงู„ุตู†ุงุฏูŠู‚:</b> ${formatK(totalEnvelopes)}\n` +
632
+ `๐Ÿ’ฐ <b>ุงู„ุนู…ู„ุงุช ุงู„ู…ูˆุฒุนุฉ:</b> ${formatK(totalCoinsGiven)}\n\n` +
633
  `๐Ÿ‘ฅ <b>ุนุฏุฏ ุงู„ุฏุงุนู…ูŠู†:</b> ${Object.keys(giftersMap).length}\n` +
634
  `๐Ÿ’ฌ <b>ุนุฏุฏ ุงู„ู…ุชูุงุนู„ูŠู†:</b> ${Object.keys(chattersMap).length}\n` +
635
  `โž• <b>ุนุฏุฏ ุงู„ู…ุชุงุจุนูŠู† ุงู„ุฌุฏุฏ:</b> ${Object.keys(followersMap).length}\n` +
636
  `๐Ÿ‘ฎ <b>ุนุฏุฏ ุงู„ู…ุดุฑููŠู†:</b> ${Object.keys(moderatorsMap).length}\n` +
637
+ `๐ŸŽ <b>ูุงุฆุฒูŠู† ุงู„ุตู†ุงุฏูŠู‚:</b> ${Object.keys(envelopeWinnersMap).length}\n` +
638
+ `๐Ÿ’ฐ <b>ูุงุฆุฒูŠู† ุงู„ุนู…ู„ุงุช:</b> ${Object.keys(coinPurseWinnersMap).length}\n` +
639
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”`;
640
  await sendTelegram(msg, null, getMainButtons(), 'HTML', null, true);
641
  }
 
661
  await sendTelegram('โš”๏ธ ู„ุง ูŠูˆุฌุฏ ุฌูˆู„ุฉ PK ู†ุดุทุฉ', null, getMainButtons(), 'HTML', null, true);
662
  return;
663
  }
 
664
  const msg = `โš”๏ธ <b>ุฏุงุนู…ูŠู† ุฌูˆู„ุฉ PK</b>\n` +
665
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
666
+ `${formatPKGifters()}\n` +
667
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”`;
668
  await sendTelegram(msg, null, getPKButtons(), 'HTML', null, true);
669
  }
670
 
671
+ async function sendEnvelopesReport() {
672
+ const winners = formatEnvelopeWinners(10);
673
+ const msg = `๐ŸŽ๐Ÿ“ฆ <b>ุตู†ุงุฏูŠู‚ ุงู„ู‡ุฏุงูŠุง - ุงู„ูุงุฆุฒูŠู†</b>\n` +
674
+ `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
675
+ `${winners}\n` +
676
+ `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
677
+ `๐Ÿ“ฆ ุฅุฌู…ุงู„ูŠ ุงู„ุตู†ุงุฏูŠู‚: ${formatK(totalEnvelopes)}\n` +
678
+ `๐Ÿ‘ฅ ุนุฏุฏ ุงู„ูุงุฆุฒูŠู†: ${Object.keys(envelopeWinnersMap).length}`;
679
+ await sendTelegram(msg, null, getStatsButtons(), 'HTML', null, true);
680
+ }
681
+
682
+ async function sendCoinPurseReport() {
683
+ const winners = formatCoinPurseWinners(10);
684
+ const msg = `๐Ÿ’ฐ๐ŸŽ’ <b>ุญู‚ูŠุจุฉ ุงู„ุนู…ู„ุงุช - ุงู„ูุงุฆุฒูŠู†</b>\n` +
685
+ `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
686
+ `${winners}\n` +
687
+ `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
688
+ `๐Ÿ’ฐ ุฅุฌู…ุงู„ูŠ ุงู„ุนู…ู„ุงุช ุงู„ู…ูˆุฒุนุฉ: ${formatK(totalCoinsGiven)}\n` +
689
+ `๐Ÿ‘ฅ ุนุฏุฏ ุงู„ูุงุฆุฒูŠู†: ${Object.keys(coinPurseWinnersMap).length}`;
690
+ await sendTelegram(msg, null, getStatsButtons(), 'HTML', null, true);
691
+ }
692
+
693
  async function sendFinalReport() {
694
  const duration = formatDuration(startTime);
695
  const topGifterText = formatTopGifters(5);
696
  const topActiveText = formatTopActive(5);
697
+ const envelopeWinners = formatEnvelopeWinners(5);
698
+ const coinWinners = formatCoinPurseWinners(5);
699
  const avgViewers = viewerHistory.length > 0
700
  ? Math.round(viewerHistory.reduce((a, b) => a + b.viewers, 0) / viewerHistory.length)
701
  : 0;
 
711
  `โ†ช๏ธ <b>ุฅุฌู…ุงู„ูŠ ุงู„ู…ุดุงุฑูƒุงุช:</b> ${formatK(totalShares)}\n` +
712
  `โž• <b>ุฅุฌู…ุงู„ูŠ ุงู„ู…ุชุงุจุนูŠู†:</b> ${formatK(totalFollows)}\n` +
713
  `๐Ÿ’ฌ <b>ุฅุฌู…ุงู„ูŠ ุงู„ุชุนู„ูŠู‚ุงุช:</b> ${formatK(totalComments)}\n` +
714
+ `๐ŸŽ <b>ุฅุฌู…ุงู„ูŠ ุงู„ู‡ุฏุงูŠุง:</b> ${formatK(totalGifts)} ๐Ÿ’Ž\n` +
715
+ `๐Ÿ“ฆ <b>ุฅุฌู…ุงู„ูŠ ุงู„ุตู†ุงุฏูŠ๏ฟฝ๏ฟฝ:</b> ${formatK(totalEnvelopes)}\n` +
716
+ `๐Ÿ’ฐ <b>ุฅุฌู…ุงู„ูŠ ุงู„ุนู…ู„ุงุช:</b> ${formatK(totalCoinsGiven)}\n\n` +
717
  `๐Ÿ† <b>ู„ูˆุญุฉ ุงู„ุดุฑู (ุงู„ุฏุงุนู…ูŠู†):</b>\n${topGifterText}\n\n` +
718
+ `๐Ÿ”ฅ <b>ุฃูƒุซุฑ ุงู„ู…ุชูุงุนู„ูŠู†:</b>\n${topActiveText}\n\n` +
719
+ `๐ŸŽ <b>ูุงุฆุฒูŠู† ุงู„ุตู†ุงุฏูŠู‚:</b>\n${envelopeWinners}\n\n` +
720
+ `๐Ÿ’ฐ <b>ูุงุฆุฒูŠู† ุงู„ุนู…ู„ุงุช:</b>\n${coinWinners}\n` +
721
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
722
  `๐Ÿ’ค ุงู„ุจูˆุช ููŠ ูˆุถุน ุงู„ุงู†ุชุธุงุฑ...`;
723
  await sendTelegram(msg, null, getDisconnectedButtons(), 'HTML', null, false);
 
728
  // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
729
  async function notifyLike(nickname, likeCount, userId) {
730
  if (!isLive) return;
731
+ if (!likersMap[userId]) likersMap[userId] = { id: userId, name: nickname, count: 0 };
 
 
732
  likersMap[userId].count += likeCount;
733
  totalLikes += likeCount;
734
 
735
+ if (pkActive) pkInfo.ourScore += likeCount;
736
 
737
  if (likeCount >= CONFIG.LIKE_BATCH_THRESHOLD) {
738
  if (isOnCooldown('like', userId, 30000)) return;
 
747
  if (!isLive) return;
748
  if (isOnCooldown('gift', userId, CONFIG.GIFT_COOLDOWN_MS)) return;
749
 
750
+ if (!giftersMap[userId]) giftersMap[userId] = { id: userId, name: nickname, coins: 0 };
 
 
751
  giftersMap[userId].coins += totalCost;
752
  totalGifts += totalCost;
753
 
754
  if (pkActive) {
755
  pkInfo.ourScore += totalCost;
756
+ if (!pkInfo.pkGifts[userId]) pkInfo.pkGifts[userId] = { id: userId, name: nickname, coins: 0 };
 
 
757
  pkInfo.pkGifts[userId].coins += totalCost;
758
  }
759
 
 
773
  if (!isLive) return;
774
  if (isOnCooldown('follow', userId, 10000)) return;
775
 
776
+ if (!followersMap[userId]) followersMap[userId] = { id: userId, name: nickname, count: 0 };
 
 
777
  followersMap[userId].count++;
778
  totalFollows++;
779
 
 
787
  if (!isLive) return;
788
  if (isOnCooldown('share', userId, 15000)) return;
789
 
790
+ if (!sharersMap[userId]) sharersMap[userId] = { id: userId, name: nickname, count: 0 };
 
 
791
  sharersMap[userId].count++;
792
  totalShares++;
793
 
 
797
  );
798
  }
799
 
800
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
801
+ // โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ๏ธ ู…ุนุงู„ุฌุฉ ุงู„ุฏุฑุฏุดุฉ - ุจุฏูˆู† ุญุธุฑ ู„ู„ุชุฎุฒูŠู† โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
802
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
803
+ function handleChat(data) {
804
+ if (!isLive) return;
805
+ const userId = data.userId || data.uniqueId;
806
+ const nickname = data.nickname || data.uniqueId || 'ู…ุฌู‡ูˆู„';
807
+ const rawText = data.comment || data.message || '';
808
+ const text = cleanChatMessage(rawText);
809
 
810
  if (!text || isSpamMessage(text)) return;
 
811
 
812
+ if (!chattersMap[userId]) chattersMap[userId] = { id: userId, name: nickname, count: 0 };
 
 
813
  chattersMap[userId].count++;
814
  totalComments++;
815
 
816
  topChatMessages.unshift({ name: nickname, text: text, time: Date.now() });
817
+ if (topChatMessages.length > 50) topChatMessages = topChatMessages.slice(0, 50);
 
 
818
 
819
+ if (isOnCooldown('chat', userId, CONFIG.CHAT_COOLDOWN_MS)) return;
 
820
 
821
+ const importantKeywords = ['ุณุคุงู„', 'ุฌูˆุงุจ', 'ู‡ู„ุง', 'ู…ุฑุญุจุง', 'ุชูุงุนู„', 'ุฏุนู…', 'pk', 'ุจูƒุฌ', 'ุชุตูˆูŠุช'];
822
+ const isImportant = text.length > 25 || importantKeywords.some(kw => text.toLowerCase().includes(kw));
823
+
824
+ if (isImportant) {
825
+ sendTelegram(
826
  `๐Ÿ’ฌ <b>${escapeHtml(nickname)}:</b> ${escapeHtml(text)}`,
827
  null, null, 'HTML', null, true
828
  );
 
831
 
832
  async function notifyModerator(data) {
833
  if (!isLive) return;
834
+ const userId = data.userId || data.uniqueId;
835
+ const nickname = data.nickname || data.uniqueId || 'ู…ุฌู‡ูˆู„';
836
 
837
  if (!moderatorsMap[userId]) {
838
  moderatorsMap[userId] = {
839
  id: userId,
840
  name: nickname,
841
  joinTime: Date.now(),
842
+ isModerator: data.isModerator || data.userDetails?.isModerator || false,
843
  };
844
  await sendTelegram(
845
  `๐Ÿ‘ฎโœจ <b>${escapeHtml(nickname)}</b> ุฏุฎู„ ุงู„ุจุซ${data.isModerator ? ' [ู…ุดุฑู]' : ''}!`,
 
848
  }
849
  }
850
 
851
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
852
+ // โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ๏ธ ู…ุนุงู„ุฌุฉ ุงู„ุตู†ุงุฏูŠู‚ (Envelope) โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
853
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
854
+ function handleEnvelope(data) {
855
+ if (!isLive) return;
856
+ const userId = data.userId || data.uniqueId;
857
+ const nickname = data.nickname || data.uniqueId || 'ู…ุฌู‡ูˆู„';
858
+ const envelopeId = data.envelopeId || data.giftId || 'unknown';
859
+
860
+ totalEnvelopes++;
861
+
862
+ if (!envelopeWinnersMap[userId]) {
863
+ envelopeWinnersMap[userId] = { id: userId, name: nickname, count: 0 };
864
+ }
865
+ envelopeWinnersMap[userId].count++;
866
+
867
+ if (isOnCooldown('envelope', envelopeId, 5000)) return;
868
+
869
+ sendTelegram(
870
+ `๐ŸŽ๐Ÿ“ฆ <b>${escapeHtml(nickname)}</b> ุญุตู„ ุนู„ู‰ ุตู†ุฏูˆู‚ ู‡ุฏุงูŠุง! ๐ŸŽ‰`,
871
+ null, null, 'HTML', null, true
872
+ );
873
+ }
874
+
875
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
876
+ // โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ๏ธ ู…ุนุงู„ุฌุฉ ุญู‚ูŠุจุฉ ุงู„ุนู…ู„ุงุช (Coin Purse) โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
877
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
878
+ function handleCoinPurse(data) {
879
+ if (!isLive) return;
880
+ const userId = data.userId || data.uniqueId;
881
+ const nickname = data.nickname || data.uniqueId || 'ู…ุฌู‡ูˆู„';
882
+ const coins = data.coins || data.diamondCount || data.amount || 1;
883
+ const purseId = data.purseId || data.giftId || 'unknown';
884
+
885
+ totalCoinsGiven += coins;
886
+
887
+ if (!coinPurseWinnersMap[userId]) {
888
+ coinPurseWinnersMap[userId] = { id: userId, name: nickname, coins: 0 };
889
+ }
890
+ coinPurseWinnersMap[userId].coins += coins;
891
+
892
+ if (isOnCooldown('coinPurse', purseId, 5000)) return;
893
+
894
+ sendTelegram(
895
+ `๐Ÿ’ฐ๐ŸŽ’ <b>${escapeHtml(nickname)}</b> ุญุตู„ ุนู„ู‰ <b>${formatK(coins)}</b> ุนู…ู„ุฉ ู…ู† ุญู‚ูŠุจุฉ ุงู„ุนู…ู„ุงุช! โœจ`,
896
+ null, null, 'HTML', null, true
897
+ );
898
+ }
899
+
900
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
901
+ // โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ๏ธ ู…ุนุงู„ุฌุฉ PK / Battle โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
902
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
903
+ function handleBattleStart(data) {
904
+ const battleUsers = data.battleUsers || [];
905
+ let opponentName = 'ู…ุฌู‡ูˆู„';
906
+ let opponentUserId = '';
907
+
908
+ if (battleUsers.length >= 2) {
909
+ const opponent = battleUsers.find(u => u.userId !== data.userId) || battleUsers[1];
910
+ opponentName = opponent.nickname || opponent.uniqueId || 'ู…ุฌู‡ูˆู„';
911
+ opponentUserId = opponent.userId || '';
912
+ }
913
+
914
  pkActive = true;
915
  pkInfo = {
916
+ opponentName: opponentName,
917
+ opponentUserId: opponentUserId,
 
918
  ourScore: 0,
919
  theirScore: 0,
920
  startTime: new Date(),
 
925
  `โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n` +
926
  `๐ŸฅŠ <b>ุงู„ู…ู†ุงูุณ:</b> ${escapeHtml(opponentName)}\n\n` +
927
  `ุงู„ููˆุฒ ูŠุญุชุงุฌ ูˆู‚ูุฉ ุงู„ุฃุจุทุงู„! ุดุฏูˆุง ุงู„ุญูŠู„.. ๐Ÿš€`;
928
+ sendTelegram(msg, null, getPKButtons(), 'HTML', null, false);
929
  }
930
 
931
+ function handleBattleEnd(data) {
932
+ if (!pkActive) return;
933
  pkActive = false;
934
+
935
  const bar = generateProgressBar(pkInfo.ourScore, pkInfo.theirScore, 10);
936
+ const winner = data.winnerUserId === CONFIG.TIKTOK_USERNAME ? 'ู†ุญู†' :
937
+ data.winnerUserId === pkInfo.opponentUserId ? pkInfo.opponentName : 'ุชุนุงุฏู„';
938
+ const result = winner === 'ุชุนุงุฏู„' ? '๐Ÿค ุชุนุงุฏู„' : winner === 'ู†ุญู†' ? '๐Ÿ† ูุฒู†ุง!' : '๐Ÿ˜ข ุฎุณุฑู†ุง';
939
  const pkGifters = formatPKGifters();
940
 
941
  const msg = `๐Ÿ†โš”๏ธ <b>ุงู†ุชู‡ุช ุงู„ู…ูˆุงุฌู‡ุฉ!</b>\n` +
 
946
  `${bar}\n\n` +
947
  `๐Ÿ† <b>ุฏุงุนู…ูŠู† ุงู„ุฌูˆู„ุฉ:</b>\n${pkGifters}\n\n` +
948
  `โœจ ุดูƒุฑุงู‹ ู„ูƒู„ ู…ู† ุณุงู‡ู… ููŠ ู‡ุฐู‡ ุงู„ุฌูˆู„ุฉ!`;
949
+ sendTelegram(msg, null, getMainButtons(), 'HTML', null, false);
950
 
951
+ pkInfo = { opponentName: '', opponentUserId: '', ourScore: 0, theirScore: 0, startTime: null, pkGifts: {} };
952
+ }
953
+
954
+ function handleBattleUpdate(data) {
955
+ if (!pkActive) return;
956
+ const battleUsers = data.battleUsers || [];
957
+ if (battleUsers.length >= 2) {
958
+ const us = battleUsers.find(u => u.userId !== pkInfo.opponentUserId) || battleUsers[0];
959
+ const them = battleUsers.find(u => u.userId === pkInfo.opponentUserId) || battleUsers[1];
960
+ pkInfo.ourScore = us?.points || pkInfo.ourScore;
961
+ pkInfo.theirScore = them?.points || pkInfo.theirScore;
962
+ }
963
  }
964
 
965
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
966
+ // โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ๏ธ ุฅุดุนุงุฑุงุช ุงู„ุจุซ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
967
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
968
  async function notifyStreamStart(state) {
969
  isLive = true;
970
  const roomInfo = state.roomInfo || {};
 
1014
  }
1015
 
1016
  function setupEvents(connection) {
1017
+ // Room User
1018
  connection.on('roomUser', (data) => {
1019
  const viewers = data.viewerCount || 0;
1020
  if (viewers > maxViewers) maxViewers = viewers;
 
1022
  if (viewerHistory.length > 200) viewerHistory.shift();
1023
  });
1024
 
1025
+ // Like
1026
  connection.on('like', (data) => {
1027
  const likes = data.likeCount || 0;
1028
+ const userId = data.userId || data.uniqueId;
1029
  const nickname = data.nickname || data.uniqueId;
1030
  notifyLike(nickname, likes, userId);
1031
  });
1032
 
1033
+ // Social = Follow + Share
1034
+ connection.on('social', (data) => {
1035
+ const userId = data.userId || data.uniqueId;
1036
  const nickname = data.nickname || data.uniqueId;
1037
+ const displayType = data.displayType || '';
1038
+
1039
+ if (displayType.includes('follow') || displayType.includes('ู…ุชุงุจ')) {
1040
+ notifyFollow(nickname, userId);
1041
+ } else if (displayType.includes('share') || displayType.includes('ู…ุดุงุฑ')) {
1042
+ notifyShare(nickname, userId);
1043
+ } else {
1044
+ notifyFollow(nickname, userId);
1045
+ }
1046
  });
1047
 
1048
+ // Chat
1049
  connection.on('chat', (data) => {
1050
+ handleChat(data);
1051
  });
1052
 
1053
+ // Member
1054
  connection.on('member', (data) => {
1055
  notifyModerator(data);
1056
  });
1057
 
1058
+ // Gift
1059
  connection.on('gift', (data) => {
1060
  if (data.giftType === 1 && !data.repeatEnd) return;
1061
  const giftCost = (data.diamondCount || 0) * (data.repeatCount || 1);
1062
+ const userId = data.userId || data.uniqueId;
1063
  const nickname = data.nickname || data.uniqueId;
1064
  notifyGift(nickname, data.giftName || 'Gift', data.diamondCount || 0, data.repeatCount || 1, giftCost, userId);
1065
  });
1066
 
1067
+ // Envelope - ุตู†ุงุฏูŠู‚ ุงู„ู‡ุฏุงูŠุง
1068
+ connection.on('envelope', (data) => {
1069
+ console.log('[Envelope] event:', JSON.stringify(data).substring(0, 300));
1070
+ handleEnvelope(data);
1071
  });
1072
 
1073
+ // Subscribe / Coin Purse - ุญู‚ูŠุจุฉ ุงู„ุนู…ู„ุงุช
1074
+ connection.on('subscribe', (data) => {
1075
+ console.log('[Subscribe/CoinPurse] event:', JSON.stringify(data).substring(0, 300));
1076
+ handleCoinPurse(data);
1077
  });
1078
 
1079
+ // PK / Battle events
1080
+ connection.on('linkMicBattle', (data) => {
1081
+ console.log('[Battle] linkMicBattle event:', JSON.stringify(data).substring(0, 200));
1082
+ handleBattleStart(data);
1083
  });
1084
 
1085
+ connection.on('linkMicArmies', (data) => {
1086
+ console.log('[Battle] linkMicArmies event:', JSON.stringify(data).substring(0, 200));
1087
+ handleBattleUpdate(data);
1088
+ });
1089
+
1090
+ // Stream End
1091
  connection.on('streamEnd', async () => {
1092
  isConnected = false;
1093
  isLive = false;
 
1096
  reconnectTimer = setTimeout(startRadar, 60000);
1097
  });
1098
 
1099
+ // Disconnected
1100
+ connection.on('disconnected', () => {
1101
+ console.log('[TikTok] Disconnected from stream');
1102
+ isConnected = false;
1103
+ isLive = false;
1104
+ });
1105
+
1106
+ // Error
1107
  connection.on('error', (err) => {
1108
  console.error(`[TikTok] Error: ${err.message}`);
1109
  });
 
1119
  reconnectAttempt++;
1120
 
1121
  if (reconnectAttempt === 1) {
1122
+ maxViewers = 0; totalLikes = 0; totalShares = 0; totalGifts = 0;
1123
+ totalFollows = 0; totalComments = 0; totalEnvelopes = 0; totalCoinsGiven = 0;
1124
+ giftersMap = {}; likersMap = {}; sharersMap = {}; followersMap = {};
1125
+ chattersMap = {}; moderatorsMap = {}; envelopeWinnersMap = {}; coinPurseWinnersMap = {};
 
 
 
 
 
 
 
 
1126
  topChatMessages = [];
1127
  viewerHistory = [];
1128
  pkActive = false;
1129
+ pkInfo = { opponentName: '', opponentUserId: '', ourScore: 0, theirScore: 0, startTime: null, pkGifts: {} };
1130
+ cooldowns.chat.clear(); cooldowns.gift.clear(); cooldowns.like.clear();
1131
+ cooldowns.follow.clear(); cooldowns.share.clear(); cooldowns.envelope.clear(); cooldowns.coinPurse.clear();
 
 
 
 
 
 
 
 
 
 
 
1132
  }
1133
 
1134
  try {
 
1194
  isConnected = false;
1195
  isLive = false;
1196
  if (tiktokConnection) {
1197
+ try { tiktokConnection.disconnect(); } catch (e) {}
 
 
1198
  tiktokConnection = null;
1199
  }
1200
  if (reconnectTimer) clearTimeout(reconnectTimer);
 
1217
  }
1218
 
1219
  // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1220
+ // โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ๏ธ ุงู„ุฎุงุฏู… ุงู„ุฏุงุฎู„ูŠ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
1221
  // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1222
  const server = http.createServer((req, res) => {
1223
  res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
1224
  const status = isLive ? '๐Ÿ”ด LIVE' : isConnected ? '๐ŸŸก CONNECTING' : 'โšช WAITING';
1225
+ const pkStatus = pkActive ? ' | โš”๏ธ PK' : '';
1226
+ res.end(`TikTok Radar Bot v4.2 | ${status}${pkStatus} | ${getBaghdadTime()} | @${CONFIG.TIKTOK_USERNAME}`);
1227
  });
1228
 
1229
  const PORT = process.env.PORT || 7860;
1230
  server.listen(PORT, '0.0.0.0', () => {
1231
  console.log(`
1232
  โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
1233
+ โ•‘ TikTok Radar Pro - Telegram Bot v4.2 (Royal Edition) โ•‘
1234
  โ•‘ โ•‘
1235
  โ•‘ ๐Ÿค– Telegram Bot Active โ•‘
1236
  โ•‘ ๐Ÿ“ก TikTok: @${CONFIG.TIKTOK_USERNAME.padEnd(45, ' ')}โ•‘
 
1240
  โ•‘ โ•‘
1241
  โ•‘ โœจ Features: Anti-Spam | Smart Cooldowns | Deep Stats โ•‘
1242
  โ•‘ ๐ŸŽ Gifts | โค๏ธ Likes | โ†ช๏ธ Shares | ๐Ÿ’ฌ Chat | โš”๏ธ PK โ•‘
1243
+ โ•‘ ๐Ÿ“ฆ Envelopes | ๐Ÿ’ฐ Coin Purse โ•‘
1244
  โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
1245
  `);
1246