Reoo88 commited on
Commit
8aeebca
·
verified ·
1 Parent(s): 4e49293

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +18 -570
index.js CHANGED
@@ -1,573 +1,21 @@
1
- const { TikTokLiveConnection, WebcastEvent } = require('tiktok-live-connector');
2
- const https = require('https');
3
- const http = require('http');
4
- const dns = require('dns');
5
- const { URL } = require('url');
6
-
7
- const TIKTOK_USERNAME = process.env.TIKTOK_USERNAME || process.env.TIKTOK_USERNAMES?.split(',')[0]?.trim() || '';
8
- const TELEGRAM_TOKEN = process.env.TELEGRAM_TOKEN;
9
- const TELEGRAM_CHAT_ID = process.env.TELEGRAM_CHAT_ID;
10
- const SESSION_ID = process.env.SESSION_ID || undefined;
11
- const MY_PROXY_URL = "https://tg-proxy.dr-toktok00.workers.dev";
12
-
13
- dns.setServers(['8.8.8.8']);
14
-
15
- // ====== قاموس الدول ======
16
- const COUNTRY_FLAGS = {
17
- 'SA': { name: 'السعودية', flag: '🇸🇦' }, 'EG': { name: 'مصر', flag: '🇪🇬' },
18
- 'AE': { name: 'الإمارات', flag: '🇦🇪' }, 'KW': { name: 'الكويت', flag: '🇰🇼' },
19
- 'QA': { name: 'قطر', flag: '🇶🇦' }, 'BH': { name: 'البحرين', flag: '🇧🇭' },
20
- 'OM': { name: 'عُمان', flag: '🇴🇲' }, 'JO': { name: 'الأردن', flag: '🇯🇴' },
21
- 'LB': { name: 'لبنان', flag: '🇱🇧' }, 'SY': { name: 'سوريا', flag: '🇸🇾' },
22
- 'IQ': { name: 'العراق', flag: '🇮🇶' }, 'YE': { name: 'اليمن', flag: '🇾🇪' },
23
- 'LY': { name: 'ليبيا', flag: '🇱🇾' }, 'TN': { name: 'تونس', flag: '🇹🇳' },
24
- 'DZ': { name: 'الجزائر', flag: '🇩🇿' }, 'MA': { name: 'المغرب', flag: '🇲🇦' },
25
- 'SD': { name: 'السودان', flag: '🇸🇩' }, 'SO': { name: 'الصومال', flag: '🇸🇴' },
26
- 'DJ': { name: 'جيبوتي', flag: '🇩🇯' }, 'MR': { name: 'موريتانيا', flag: '🇲🇷' },
27
- 'PS': { name: 'فلسطين', flag: '🇵🇸' }, 'IL': { name: 'إسرائيل', flag: '🇮🇱' },
28
- 'US': { name: 'الولايات المتحدة', flag: '🇺🇸' }, 'GB': { name: 'المملكة المتحدة', flag: '🇬🇧' },
29
- 'TR': { name: 'تركيا', flag: '🇹🇷' }, 'FR': { name: 'فرنسا', flag: '🇫🇷' },
30
- 'DE': { name: 'ألمانيا', flag: '🇩🇪' }, 'IT': { name: 'إيطاليا', flag: '🇮🇹' },
31
- 'ES': { name: 'إسبانيا', flag: '🇪🇸' }, 'BR': { name: 'البرازيل', flag: '🇧🇷' },
32
- 'RU': { name: 'روسيا', flag: '🇷🇺' }, 'CN': { name: 'الصين', flag: '🇨🇳' },
33
- 'JP': { name: 'اليابان', flag: '🇯🇵' }, 'KR': { name: 'كوريا الجنوبية', flag: '🇰🇷' },
34
- 'IN': { name: 'الهند', flag: '🇮🇳' }, 'ID': { name: 'إندونيسيا', flag: '🇮🇩' },
35
- 'TH': { name: 'تايلاند', flag: '🇹🇭' }, 'VN': { name: 'فيتنام', flag: '🇻🇳' },
36
- 'PH': { name: 'الفلبين', flag: '🇵🇭' }, 'MY': { name: 'ماليزيا', flag: '🇲🇾' },
37
- 'SG': { name: 'سنغافورة', flag: '🇸🇬' }, 'PK': { name: 'باكستان', flag: '🇵🇰' },
38
- 'BD': { name: 'بنغلاديش', flag: '🇧🇩' }, 'IR': { name: 'إيران', flag: '🇮🇷' },
39
- 'AR': { name: 'الأرجنتين', flag: '🇦🇷' }, 'MX': { name: 'المكسيك', flag: '🇲🇽' },
40
- 'CO': { name: 'كولومبيا', flag: '🇨🇴' }, 'PE': { name: 'بيرو', flag: '🇵🇪' },
41
- 'CL': { name: 'تشيلي', flag: '🇨🇱' }, 'CA': { name: 'كندا', flag: '🇨🇦' },
42
- 'AU': { name: 'أستراليا', flag: '🇦🇺' }, 'NZ': { name: 'نيوزيلندا', flag: '🇳🇿' },
43
- 'ZA': { name: 'جنوب أفريقيا', flag: '🇿🇦' }, 'NG': { name: 'نيجيريا', flag: '🇳🇬' },
44
- 'NL': { name: 'هولندا', flag: '🇳🇱' }, 'BE': { name: 'بلجيكا', flag: '🇧🇪' },
45
- 'CH': { name: 'سويسرا', flag: '🇨🇭' }, 'AT': { name: 'النمسا', flag: '🇦🇹' },
46
- 'PL': { name: 'بولندا', flag: '🇵🇱' }, 'SE': { name: 'السويد', flag: '🇸🇪' },
47
- 'NO': { name: 'النرويج', flag: '🇳🇴' }, 'DK': { name: 'الدنمارك', flag: '🇩🇰' },
48
- 'FI': { name: 'فنلندا', flag: '🇫🇮' }, 'GR': { name: 'اليونان', flag: '🇬🇷' },
49
- 'PT': { name: 'البرتغال', flag: '🇵🇹' }, 'IE': { name: 'أيرلندا', flag: '🇮🇪' },
50
- };
51
-
52
- const GIFT_ARABIC = {
53
- 'rose':'وردة','heart':'قلب','star':'نجمة','fire':'نار','crown':'تاج',
54
- 'diamond':'ماسة','tiger':'نمر','dragon':'تنين','rainbow':'قوس قزح',
55
- 'galaxy':'مجرة','rocket':'صاروخ','butterfly':'فراشة','lollipop':'حلوى',
56
- 'cookie':'بسكويت','donut':'دونات','ice cream':'آيس كريم','champagne':'شمبانيا',
57
- 'wine':'نبيذ','ring':'خاتم','gem':'جوهرة','pearl':'لؤلؤ','teddy bear':'دب',
58
- 'cat':'قطة','dog':'كلب','lion':'أسد','panda':'باندا','castle':'قصر',
59
- 'angel':'ملاك','phoenix':'فينيخ','mermaid':'مرأة البحر','fairy':'جنية',
60
- 'robot':'روبوت','dinosaur':'ديناصور','penguin':'بطريق',
61
- 'blue heart':'قلب أزرق','purple heart':'قلب بنفسجي','golden crown':'تاج ذهبي',
62
- 'silver crown':'تاج فضي','firework':'ألعاب نار��ة','fireworks':'ألعاب نارية',
63
- 'nevalyashka doll':'دمية','dancing bear':'دب راقص','lucky cat':'قطة محظوظة',
64
- 'magic':'سحر','rainbow rose':'وردة قوس قزح','pink rose':'وردة وردية',
65
- 'red rose':'وردة حمراء','white rose':'وردة بيضاء','blue rose':'وردة زرقاء',
66
- 'golden ticket':'تذكرة ذهبية','golden star':'نجمة ذهبية','diamond ring':'خاتم ماسي',
67
- 'diamond necklace':'عقد ماسي','golden egg':'بيضة ذهبية','golden heart':'قلب ذهبي',
68
- 'dragon flame':'لهب التنين','sword':'سيف','shield':'درع','halo':'هالة',
69
- 'wings':'أجنحة','balloon':'بالون','gift':'هدية','present':'هدية',
70
- 'music':'موسيقى','trophy':'كأس','medal':'وسام','flag':'علم','wizard':'ساحر',
71
- 'ninja':'نينجا','pirate':'قراصنة','santa':'سانتا','flower':'زهرة',
72
- 'crown prince':'ولي عهد','neon heart':'قلب نيون','silver heart':'قلب فضي',
73
- 'black heart':'قلب أسود','broken heart':'قلب مكسور','fire heart':'قلب محترق',
74
- 'sparkling heart':'قلب لامع','yellow heart':'قلب أصفر','green heart':'قلب أخضر',
75
- 'money':'مال','cash':'كاش','money rain':'مطر مال','viking':'فايكنغ',
76
- 'pharaoh':'فرعون','thumbs up':'إبهام','smile':'ابتسامة','love':'حب',
77
- 'kiss':'قبلة','bandage':'ضماد','ghost':'شبح','skull':'جمجمة','sun':'شمس',
78
- 'moon':'قمر','comet':'مذنب','planet':'كوكب','wolf':'ذئب','fox':'ثعلب',
79
- 'rabbit':'أرنب','gold bar':'سبيكة ذهب','gold coin':'عملة ذهبية',
80
- 'money bag':'كيس مال','diamond crown':'تاج ماسي','diamond heart':'قلب ماسي',
81
- 'super star':'نجمة خارقة','mega star':'نجمة عملاقة','legend':'أسطوري',
82
- 'epic':'ملحمي','bow':'قوس','arrow':'سهم','cherry':'كرز','apple':'تفاحة',
83
- 'grape':'عنب','watermelon':'بطيخ','strawberry':'فراولة','banana':'موز',
84
- 'peach':'خوخ','cupcake':'كعكة صغيرة','sparkler':'شرارة','lantern':'مصباح',
85
- 'candle':'شمعة','ribbon':'شريط','gift box':'صندوق هدية','party hat':'قبعة حفلة',
86
- 'confetti':'قصاصات ورقية','knight':'فرس','snowman':'رجل ثلج',
87
- 'flower garden':'حديقة زهور','treasure':'كنز','crystal ball':'كرة بلورية',
88
- 'tropical island':'جزيرة استوائية','royal carriage':'عربة ملكية',
89
- 'treasure chest':'كنز','100':'100',
90
- };
91
-
92
- const GIFT_VALUES = {
93
- 5953:25, 5952:1, 5951:5, 5950:10, 5949:15, 5948:20,
94
- 5947:30, 5946:50, 5945:100, 5944:200, 5943:500,
95
- 5942:1000, 5941:2000, 5940:5000, 5939:10000,
96
- 5938:25000, 5937:50000, 5936:100000,
97
- 5935:1, 5934:3, 5933:5, 5932:10, 5931:15,
98
- 5930:20, 5929:30, 5928:40, 5927:50, 5926:60,
99
- 5925:80, 5924:100, 5923:150, 5922:200, 5921:300,
100
- 5920:400, 5919:500, 5918:700, 5917:1000,
101
- 5916:1500, 5915:2000, 5914:3000, 5913:5000,
102
- 5912:10000, 5911:20000, 5910:50000, 5909:100000,
103
- 5908:250000, 5907:500000, 5906:1000000,
104
- 5905:10, 5904:25, 5903:50, 5902:100,
105
- 5901:200, 5900:500, 5899:1000, 5898:2000,
106
- 5897:5000, 5896:10000, 5895:20000, 5894:50000,
107
- 5893:100000, 5892:250000, 5891:500000, 5890:1000000,
108
- 5729:1, 5728:5, 5727:10, 5726:15, 5725:20,
109
- 5724:30, 5723:50, 5722:100, 5721:200, 5720:500,
110
- 5719:1000, 5718:2000, 5717:5000, 5716:10000,
111
- 5715:25000, 5714:50000, 5713:100000,
112
- 5712:250000, 5711:500000, 5710:1000000,
113
- 5972:1, 5971:1, 5970:1, 5969:1, 5968:3,
114
- 5967:5, 5966:10, 5965:15, 5964:20, 5963:30,
115
- 5962:40, 5961:50, 5960:100,
116
- 1817:1, 1816:1, 1815:1, 1814:1, 1813:1,
117
- 1812:1, 1811:1, 1810:5, 1809:5, 1808:5,
118
- 1807:5, 1806:10, 1805:10, 1804:10,
119
- 1803:15, 1802:20, 1801:25, 1800:30,
120
- 1799:35, 1798:40, 1797:50, 1796:60,
121
- 1795:70, 1794:80, 1793:90, 1792:100,
122
- 1791:150, 1790:200, 1789:250, 1788:300,
123
- 1787:400, 1786:500, 1785:700, 1784:1000,
124
- 1783:1500, 1782:2000, 1781:3000, 1780:5000,
125
- 1779:7000, 1778:10000, 1777:15000, 1776:20000,
126
- 1775:30000, 1774:50000, 1773:75000, 1772:100000,
127
- 7934:1, 7933:1, 7932:1, 7931:1, 7930:1,
128
- 7929:1, 7928:1, 7927:1, 7926:1, 7925:1,
129
- 7924:5, 7923:5, 7922:5, 7921:5, 7920:10,
130
- 7919:10, 7918:10, 7917:15, 7916:15, 7915:20,
131
- 7914:20, 7913:25, 7912:25, 7911:30, 7910:50,
132
- 7909:50, 7908:50, 7907:50, 7906:50, 7905:100,
133
- 7904:100, 7903:100, 7902:150, 7901:200, 7900:200,
134
- 7899:300, 7898:300, 7897:300, 7896:500, 7895:500,
135
- 7894:500, 7893:700, 7892:1000, 7891:1000, 7890:1000,
136
- 7889:1500, 7888:1500, 7887:2000, 7886:2000, 7885:2000,
137
- 7884:3000, 7883:3000, 7882:5000, 7881:5000, 7880:5000,
138
- 7879:7000, 7878:7000, 7877:10000, 7876:10000, 7875:10000,
139
- 7874:15000, 7873:20000, 7872:20000, 7871:25000, 7870:25000,
140
- 7869:50000, 7868:50000, 7867:75000, 7866:75000, 7865:100000,
141
- 7864:100000, 7863:250000, 7862:250000, 7861:500000, 7860:500000,
142
- 7859:1000000, 7858:1000000,
143
- };
144
-
145
- function getCountryInfo(code) {
146
- if (!code) return { name: 'غير محدد', flag: '���' };
147
- return COUNTRY_FLAGS[code.toUpperCase()] || { name: code, flag: '🌍' };
148
- }
149
-
150
- function getGiftArabicName(englishName, giftId) {
151
- if (!englishName) {
152
- const val = GIFT_VALUES[giftId];
153
- return val ? '💎' + val : 'هدية';
154
- }
155
- const lower = englishName.toLowerCase().trim();
156
- return GIFT_ARABIC[lower] || englishName;
157
- }
158
-
159
- function getGiftDiamonds(data) {
160
- if (data.diamondCount && data.diamondCount > 0) return data.diamondCount;
161
- const ext = data.extendedGiftInfo || {};
162
- if (ext.diamond_count && ext.diamond_count > 0) return ext.diamond_count;
163
- if (ext.diamondCount && ext.diamondCount > 0) return ext.diamondCount;
164
- const gid = data.giftId;
165
- if (GIFT_VALUES[gid]) return GIFT_VALUES[gid];
166
- if (data.gift?.coinValue && data.gift.coinValue > 0) return data.gift.coinValue;
167
- if (data.gift?.diamondCount && data.gift.diamondCount > 0) return data.gift.diamondCount;
168
- return 1;
169
- }
170
-
171
- function formatK(num) {
172
- if (!num || isNaN(num) || num === 0) return '0';
173
- if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M';
174
- if (num >= 1000) return (num / 1000).toFixed(1) + 'K';
175
- return num.toString();
176
- }
177
-
178
- function formatDuration(seconds) {
179
- if (!seconds || seconds <= 0) return 'دقيقة واحدة';
180
- const h = Math.floor(seconds / 3600);
181
- const m = Math.floor((seconds % 3600) / 60);
182
- const s = seconds % 60;
183
- const parts = [];
184
- if (h > 0) parts.push(h + ' ساعة');
185
- if (m > 0) parts.push(m + ' دقيقة');
186
- if (s > 0 && h === 0) parts.push(s + ' ثانية');
187
- return parts.join(' و ') || 'أقل من دقيقة';
188
- }
189
-
190
- function getBaghdadTime() {
191
- return new Intl.DateTimeFormat('ar-IQ', {
192
- timeZone: 'Asia/Baghdad', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: true
193
- }).format(new Date());
194
- }
195
-
196
- function generateProgressBar(ourScore, theirScore, length = 10) {
197
- const total = (ourScore || 0) + (theirScore || 0);
198
- if (total === 0) return "⬜".repeat(length);
199
- const ourPercent = Math.min(Math.max(Math.round(((ourScore || 0) / total) * length), 0), length);
200
- return "🟦".repeat(ourPercent) + "🟥".repeat(length - ourPercent);
201
- }
202
-
203
- // ====== HTTP POST ======
204
- function httpPost(url, payload, timeout = 15000) {
205
- return new Promise((resolve, reject) => {
206
- const urlObj = new URL(url);
207
- const protocol = urlObj.protocol === 'https:' ? https : http;
208
- const body = JSON.stringify(payload);
209
- const req = protocol.request({
210
- hostname: urlObj.hostname,
211
- port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
212
- path: urlObj.pathname + urlObj.search,
213
- method: 'POST',
214
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
215
- timeout: timeout
216
- }, (res) => {
217
- let responseData = '';
218
- res.on('data', chunk => responseData += chunk);
219
- res.on('end', () => resolve({ statusCode: res.statusCode, data: responseData }));
220
- });
221
- req.on('error', reject);
222
- req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
223
- req.write(body);
224
- req.end();
225
- });
226
- }
227
-
228
- // ====== ✅ جلب الدولة (fetch يدعم gzip تلقائياً) ======
229
- async function getRegionFromPage(username) {
230
- try {
231
- const response = await fetch('https://www.tiktok.com/@' + username, {
232
- headers: {
233
- 'User-Agent': 'Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36',
234
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
235
- 'Accept-Language': 'en-US,en;q=0.9,ar;q=0.8',
236
- },
237
- redirect: 'follow',
238
- signal: AbortSignal.timeout(15000)
239
  });
240
- if (!response.ok) return null;
241
- const html = await response.text();
242
-
243
- const pattern = /<script id="__UNIVERSAL_DATA_FOR_REHYDRATION__" type="application\/json">(.*?)<\/script>/s;
244
- const match = html.match(pattern);
245
- if (!match) return null;
246
-
247
- const data = JSON.parse(match[1]);
248
- const scope = data['__DEFAULT_SCOPE__'] || {};
249
- const webapp = scope['webapp.user-detail'] || {};
250
- const userInfo = webapp['userInfo'] || {};
251
- const user = userInfo['user'] || {};
252
-
253
- return {
254
- region: user['region'] || null,
255
- nickname: user['nickname'] || username,
256
- verified: user['verified'] || false
257
- };
258
- } catch (e) {
259
- return null;
260
- }
261
- }
262
-
263
- // ====== تليقرام ======
264
- async function sendToTelegram(text, photoUrl = null, buttons = null) {
265
- try {
266
- const payload = {
267
- chat_id: TELEGRAM_CHAT_ID,
268
- parse_mode: 'HTML',
269
- disable_web_page_preview: true
270
- };
271
- if (buttons) payload.reply_markup = JSON.stringify({ inline_keyboard: buttons });
272
- if (photoUrl) {
273
- payload.photo = photoUrl;
274
- payload.caption = text;
275
- await httpPost(`${MY_PROXY_URL}/bot${TELEGRAM_TOKEN}/sendPhoto`, payload);
276
- } else {
277
- payload.text = text;
278
- await httpPost(`${MY_PROXY_URL}/bot${TELEGRAM_TOKEN}/sendMessage`, payload);
279
- }
280
- } catch (e) {
281
- console.log("⚠️ فشل الإرسال:", e.message);
282
- }
283
- }
284
-
285
- // ====== كاش (تم تصحيحه من const إلى let) ======
286
- let regionCache = {};
287
-
288
- async function fetchUserRegion(username) {
289
- if (!username) return null;
290
- if (regionCache[username]) return regionCache[username];
291
- const result = await getRegionFromPage(username);
292
- if (result) regionCache[username] = result;
293
- return result;
294
- }
295
-
296
- // ====== متغيرات ======
297
- let maxViewers = 0, totalLikes = 0, totalShares = 0;
298
- let streamStartTime = null;
299
- let giftersMap = {};
300
- let pkActive = false;
301
- let pkInfo = { opponentName: "", ourLikes: 0, theirLikes: 0 };
302
- let totalGiftCount = 0;
303
- let totalGiftValue = 0;
304
- let isEndingStream = false; // ✅ لمنع التكرار
305
-
306
- function getTopGifters(map, top = 5) {
307
- return Object.values(map).sort((a, b) => b.coins - a.coins).slice(0, top);
308
- }
309
-
310
- // ====== إرسال إشعار بداية البث ======
311
- async function sendStartNotification(state) {
312
- const userRegion = await getRegionFromPage(TIKTOK_USERNAME);
313
- const countryInfo = getCountryInfo(userRegion?.region || null);
314
- const liveLink = 'https://www.tiktok.com/@' + TIKTOK_USERNAME + '/live';
315
-
316
- let avatarUrl = '';
317
- const owner = state.roomInfo?.owner || null;
318
- if (owner) {
319
- if (owner.profilePicture) avatarUrl = owner.profilePicture;
320
- else if (owner.profilePictureUrl) avatarUrl = owner.profilePictureUrl;
321
- else if (owner.profileThumbnail) avatarUrl = owner.profileThumbnail;
322
- else if (owner.avatarLarge?.urlList?.[0]) avatarUrl = owner.avatarLarge.urlList[0];
323
- else if (owner.avatarLarge?.url?.[0]) avatarUrl = owner.avatarLarge.url[0];
324
- else if (owner.avatarThumb?.urlList?.[0]) avatarUrl = owner.avatarThumb.urlList[0];
325
- else if (owner.avatarThumb?.url?.[0]) avatarUrl = owner.avatarThumb.url[0];
326
- else if (owner.avatar?.urlList?.[0]) avatarUrl = owner.avatar.urlList[0];
327
- else if (owner.avatar?.url?.[0]) avatarUrl = owner.avatar.url[0];
328
- }
329
- if (!avatarUrl) {
330
- avatarUrl = state.roomInfo?.sharedCover || state.roomInfo?.coverUrl || '';
331
- }
332
-
333
- const displayName = userRegion?.nickname || TIKTOK_USERNAME;
334
- const verifiedBadge = userRegion?.verified ? ' ✅' : '';
335
-
336
- const msg = '🔴 ' + displayName + verifiedBadge + '\n\n' +
337
- '🌐 @' + TIKTOK_USERNAME + '\n' +
338
- countryInfo.flag + ' ' + countryInfo.name + '\n' +
339
- '⏰ ' + getBaghdadTime();
340
-
341
- sendToTelegram(msg, avatarUrl || undefined, [[{ text: "📺 دخول البث المباشر", url: liveLink }]]);
342
- console.log('📸 صورة: ' + (avatarUrl ? '✅' : '❌'));
343
- }
344
-
345
- // ====== ربط الأحداث ======
346
- function attachEvents(conn) {
347
- conn.on(WebcastEvent.ROOM_USER, data => {
348
- if (data.viewerCount > maxViewers) maxViewers = data.viewerCount;
349
- });
350
-
351
- conn.on(WebcastEvent.LIKE, data => {
352
- const lc = data.likeCount || 0;
353
- if (lc > 0) totalLikes += lc;
354
- if (pkActive) pkInfo.ourLikes += lc;
355
- });
356
-
357
- conn.on(WebcastEvent.SOCIAL, () => { totalShares++; });
358
-
359
- conn.on(WebcastEvent.GIFT, async data => {
360
- const userObj = data.user || {};
361
- const giftObj = data.gift || {};
362
- const extObj = data.extendedGiftInfo || {};
363
-
364
- const gifterUsername = data.uniqueId || userObj.uniqueId || userObj.userName || '';
365
- const nickname = data.nickname || userObj.nickname || userObj.displayName || '';
366
- const giftId = data.giftId || giftObj.giftId || giftObj.id || extObj.id || 0;
367
- const giftNameEn = data.giftName || giftObj.name || extObj.name || '';
368
- const repeatCount = data.repeatCount || giftObj.repeatCount || 1;
369
- const giftType = data.giftType || giftObj.giftType || 0;
370
- const repeatEnd = data.repeatEnd !== undefined ? data.repeatEnd : (giftObj.repeatEnd ? true : false);
371
-
372
- if (giftType === 1 && !repeatEnd) return;
373
-
374
- const baseDiamonds = getGiftDiamonds(data);
375
- const giftCost = baseDiamonds * repeatCount;
376
- totalGiftCount++;
377
- totalGiftValue += giftCost;
378
- const arabicName = getGiftArabicName(giftNameEn, giftId);
379
-
380
- if (!gifterUsername) return;
381
-
382
- const userId = data.userId || userObj.userId || gifterUsername;
383
- if (!giftersMap[userId]) {
384
- giftersMap[userId] = {
385
- name: nickname || gifterUsername,
386
- username: gifterUsername,
387
- coins: 0, region: null, flag: '🌍', gifts: 0
388
- };
389
- }
390
- giftersMap[userId].coins += giftCost;
391
- giftersMap[userId].gifts += repeatCount;
392
-
393
- const gifterData = await fetchUserRegion(gifterUsername);
394
- let region = null;
395
- let displayName = nickname || gifterUsername;
396
- if (gifterData && gifterData.region) {
397
- region = gifterData.region;
398
- displayName = gifterData.nickname || displayName;
399
- giftersMap[userId].region = region;
400
- }
401
-
402
- const gc = getCountryInfo(region);
403
- giftersMap[userId].flag = gc.flag;
404
-
405
- const repeatText = repeatCount > 1 ? ' ×' + repeatCount : '';
406
- const msg = '🎁 ' + arabicName + repeatText + '\n\n' +
407
- gc.flag + ' ' + displayName + '\n' +
408
- '📱 @' + gifterUsername + '\n' +
409
- '💎 ' + formatK(giftCost) + '\n' +
410
- '🌍 ' + gc.name;
411
-
412
- sendToTelegram(msg, null, [[
413
- { text: "👤 حسابه", url: 'https://www.tiktok.com/@' + gifterUsername },
414
- { text: "📺 دخول البث", url: 'https://www.tiktok.com/@' + TIKTOK_USERNAME + '/live' }
415
- ]]);
416
- });
417
-
418
- conn.on(WebcastEvent.FOLLOW, async data => {
419
- const fu = data.uniqueId || '';
420
- if (!fu) return;
421
- const fd = await fetchUserRegion(fu);
422
- let region = null;
423
- let dn = data.nickname || fu;
424
- if (fd && fd.region) { region = fd.region; dn = fd.nickname || dn; }
425
- const gc = getCountryInfo(region);
426
- sendToTelegram('➕ متابع جديد!\n\n' + gc.flag + ' ' + dn + '\n📱 @' + fu + '\n🌍 ' + gc.name);
427
- });
428
-
429
- conn.on(WebcastEvent.ROOM_STATS, data => {
430
- if (data.totalLikeCount) totalLikes = data.totalLikeCount;
431
- });
432
-
433
- conn.on('pkStart', data => {
434
- pkActive = true;
435
- pkInfo = { opponentName: data.opponentNickname || 'منافس', ourLikes: 0, theirLikes: 0 };
436
- sendToTelegram('⚔️ ' + pkInfo.opponentName, null, [[{ text: "🚀 دعم البث", url: 'https://www.tiktok.com/@' + TIKTOK_USERNAME + '/live' }]]);
437
- });
438
-
439
- conn.on('pkUpdate', data => {
440
- pkInfo.ourLikes = data.ourLikes || 0;
441
- pkInfo.theirLikes = data.theirLikes || 0;
442
- });
443
-
444
- conn.on('pkEnd', data => {
445
- pkActive = false;
446
- sendToTelegram('🏆 ' + (data.winnerNickname || 'تعادل') + '\n\n' +
447
- '🟦 نحن: ' + formatK(pkInfo.ourLikes) + ' | ' + formatK(pkInfo.theirLikes) + ' 🟥\n\n' +
448
- generateProgressBar(pkInfo.ourLikes, pkInfo.theirLikes));
449
- });
450
- }
451
-
452
- // ====== نهاية البث ======
453
- function onStreamEnd() {
454
- if (isEndingStream) return; // ✅ منع التكرار
455
- isEndingStream = true;
456
-
457
- const duration = streamStartTime ? formatDuration(Math.floor((Date.now() - streamStartTime) / 1000)) : 'غير معروف';
458
- const top = getTopGifters(giftersMap, 5);
459
- const medals = ['🥇', '🥈', '🥉', '4️⃣', '5️⃣'];
460
- let topText = top.map((g, i) =>
461
- medals[i] + ' ' + (g.flag || '🌍') + ' ' + g.name + '\n @' + g.username + ' → ' + formatK(g.coins) + ' 💎 | ' + g.gifts + ' 🎁'
462
- ).join('\n\n') || 'لا يوجد';
463
-
464
- const countryStats = {};
465
- Object.values(giftersMap).forEach(g => {
466
- const r = g.region || 'XX';
467
- countryStats[r] = (countryStats[r] || 0) + g.coins;
468
- });
469
- let countryRanking = Object.entries(countryStats)
470
- .sort((a, b) => b[1] - a[1]).slice(0, 3)
471
- .map(([r, c], i) =>
472
- ['👑', '🥈', '🥉'][i] + ' ' + getCountryInfo(r).flag + ' ' + getCountryInfo(r).name + ' → ' + formatK(c) + ' 💎'
473
- ).join('\n') || 'لا بيانات';
474
-
475
- sendToTelegram('📊 التقرير النهائي\n\n' +
476
- '━━━━━━━━━━━━━━━━━━━━\n' +
477
- '⏱️ مدة البث: ' + duration + '\n' +
478
- '👥 أعلى مشاهدات: ' + formatK(maxViewers) + '\n' +
479
- '❤️ إعجابات: ' + formatK(totalLikes) + '\n' +
480
- '↪️ مشاركات: ' + formatK(totalShares) + '\n' +
481
- '🎁 إجمالي الهدايا: ' + formatK(totalGiftCount) + '\n' +
482
- '💎 إجمالي القيم: ' + formatK(totalGiftValue) + '\n' +
483
- '👥 عدد الداعمين: ' + Object.keys(giftersMap).length + '\n' +
484
- '━━━━━━━━━━━━━━━━━━━━\n\n' +
485
- '🏆 أقوى داعمين:\n\n' + topText + '\n\n' +
486
- '🌍 توزيع حسب الدولة:\n' + countryRanking + '\n\n💤 بانتظار البث القادم...');
487
-
488
- console.log('📴 انتهى البث');
489
- }
490
-
491
- // ====== إعادة الاتصال ======
492
- let reconnectTimer = null;
493
-
494
- function scheduleReconnect(delay) {
495
- if (reconnectTimer) clearTimeout(reconnectTimer);
496
- reconnectTimer = setTimeout(tryConnect, delay);
497
- }
498
-
499
- // ====== الاتصال ======
500
- let activeConn = null;
501
-
502
- async function tryConnect() {
503
- // ✅ تنظيف أي اتصال سابق
504
- if (activeConn) {
505
- try { activeConn.disconnect(); } catch (e) {}
506
- activeConn = null;
507
- }
508
-
509
- // إعادة ضبط المتغيرات
510
- isEndingStream = false;
511
- maxViewers = 0;
512
- totalLikes = 0;
513
- totalShares = 0;
514
- totalGiftCount = 0;
515
- totalGiftValue = 0;
516
- giftersMap = {};
517
- regionCache = {};
518
- pkActive = false;
519
- pkInfo = { opponentName: "", ourLikes: 0, theirLikes: 0 };
520
- streamStartTime = Date.now();
521
-
522
- console.log('⏳ محاولة الاتصال: @' + TIKTOK_USERNAME);
523
-
524
- activeConn = new TikTokLiveConnection(TIKTOK_USERNAME, {
525
- processInitialData: true,
526
- enableExtendedGiftInfo: true,
527
- fetchRoomInfoOnConnect: true,
528
- clientParams: { app_language: "ar-IQ", device_platform: "web" }
529
- });
530
-
531
- attachEvents(activeConn);
532
-
533
- // ✅ ربط أحداث الانقطاع
534
- activeConn.on(WebcastEvent.DISCONNECTED, () => {
535
- console.log('❌ قطع الاتصال');
536
- if (streamStartTime) onStreamEnd();
537
- scheduleReconnect(30000);
538
- });
539
- activeConn.on('streamEnd', () => {
540
- console.log('📴 انتهى البث');
541
- onStreamEnd();
542
- scheduleReconnect(30000);
543
- });
544
-
545
- try {
546
- const state = await activeConn.connect();
547
- console.log('✅ متصل بالغرفة!');
548
- console.log('📡 Room ID:', state.roomId);
549
- await sendStartNotification(state);
550
- } catch (e) {
551
- let errMsg = '';
552
- if (e && typeof e === 'object') errMsg = e.message || e.name || String(e);
553
- else errMsg = String(e);
554
- console.log('🔎 غير متاح: ' + errMsg);
555
- scheduleReconnect(60000);
556
- }
557
- }
558
-
559
- // ====== التشغيل ======
560
- console.log('✅ جاهز | @' + TIKTOK_USERNAME);
561
- setTimeout(tryConnect, 2000);
562
-
563
- const server = http.createServer((req, res) => {
564
- res.writeHead(200, { 'Content-Type': 'text/plain' });
565
- res.end('OK');
566
- });
567
 
568
- server.listen(7860, '0.0.0.0', () => {
569
- console.log('🖥️ الخادم: 7860');
570
- setInterval(() => {
571
- http.get('http://localhost:7860', r => r.resume()).on('error', () => {});
572
- }, 120000);
573
  });
 
1
+ const { WebcastPushConnection } = require('tiktok-live-connector');
2
+ const fs = require('fs');
3
+
4
+ const username = 'osei_iraq'; // اسم المستخدم
5
+ const commentsFile = 'comments.txt';
6
+
7
+ let tiktokConnection = new WebcastPushConnection(username);
8
+
9
+ tiktokConnection.connect().then(state => {
10
+ console.log('متصل بالبث!');
11
+
12
+ tiktokConnection.on('chat', data => {
13
+ const comment = data.uniqueId + ': ' + data.comment;
14
+ console.log(comment);
15
+ // كتابة التعليق في الملف (سيتم إنشاؤه تلقائياً)
16
+ fs.appendFileSync(commentsFile, comment + '\n');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
+ }).catch(err => {
20
+ console.log('خطأ:', err.message);
 
 
 
21
  });