Automatically extracts red packet codes from Telegram and claims them on Binance
Size
27.7 KB
Version
1.1.6
Created
Mar 20, 2026
Updated
25 days ago
1// ==UserScript==
2// @name Binance Red Packet Auto Claimer
3// @description Automatically extracts red packet codes from Telegram and claims them on Binance
4// @version 1.1.6
5// @match https://*.web.telegram.org/*
6// @match https://www.binance.com/*/my/wallet/account/payment/cryptobox
7// @icon https://web.telegram.org/a/favicon.ico
8// @grant GM.getValue
9// @grant GM.setValue
10// @grant GM.deleteValue
11// ==/UserScript==
12(function() {
13 'use strict';
14
15 console.log('Binance Red Packet Auto Claimer - Extension loaded');
16
17 // Utility function to wait for element
18 function waitForElement(selector, timeout = 10000) {
19 return new Promise((resolve, reject) => {
20 const element = document.querySelector(selector);
21 if (element) {
22 return resolve(element);
23 }
24
25 const observer = new MutationObserver((mutations, obs) => {
26 const element = document.querySelector(selector);
27 if (element) {
28 obs.disconnect();
29 resolve(element);
30 }
31 });
32
33 observer.observe(document.body, {
34 childList: true,
35 subtree: true
36 });
37
38 setTimeout(() => {
39 observer.disconnect();
40 reject(new Error(`Element ${selector} not found within ${timeout}ms`));
41 }, timeout);
42 });
43 }
44
45 // Utility function to delay
46 function delay(ms) {
47 return new Promise(resolve => setTimeout(resolve, ms));
48 }
49
50 // ==================== TELEGRAM FUNCTIONALITY ====================
51 async function extractCodesFromTelegram() {
52 console.log('Starting code extraction from Telegram...');
53
54 // Find all code elements in messages - updated selector
55 const codeElements = document.querySelectorAll('code.monospace-text, code.text-entity-code[data-entity-type="MessageEntityCode"]');
56 console.log(`Found ${codeElements.length} code elements`);
57
58 const codes = [];
59 const seen = new Set();
60 codeElements.forEach(codeEl => {
61 const code = codeEl.textContent.trim();
62 if (code && code.length > 0 && !seen.has(code)) {
63 seen.add(code);
64 codes.push(code);
65 }
66 });
67
68 console.log(`Extracted ${codes.length} red packet codes:`, codes);
69
70 // Store codes in GM storage
71 if (codes.length > 0) {
72 await GM.setValue('redPacketCodes', JSON.stringify(codes));
73 console.log('Codes saved to storage');
74 }
75
76 return codes;
77 }
78
79 async function scrollAndExtractAllCodes() {
80 console.log('Starting full extraction with scrolling...');
81
82 const scrollableContainer = document.querySelector('.scrollable.scrollable-y');
83 if (!scrollableContainer) {
84 console.error('Scrollable container not found');
85 return await extractCodesFromTelegram();
86 }
87
88 let previousCodeCount = 0;
89 let noNewCodesCount = 0;
90 const maxScrollAttempts = 50; // Maximum scroll attempts
91 let scrollAttempts = 0;
92
93 while (scrollAttempts < maxScrollAttempts && noNewCodesCount < 3) {
94 // Scroll to top
95 scrollableContainer.scrollTop = 0;
96 await delay(1000); // Wait for messages to load
97
98 // Extract current codes
99 const codeElements = document.querySelectorAll('code.monospace-text, code.text-entity-code[data-entity-type="MessageEntityCode"]');
100 const currentCodeCount = codeElements.length;
101
102 console.log(`Scroll attempt ${scrollAttempts + 1}: Found ${currentCodeCount} code elements`);
103
104 // Check if we found new codes
105 if (currentCodeCount === previousCodeCount) {
106 noNewCodesCount++;
107 console.log(`No new codes found (${noNewCodesCount}/3)`);
108 } else {
109 noNewCodesCount = 0;
110 previousCodeCount = currentCodeCount;
111 }
112
113 scrollAttempts++;
114 }
115
116 console.log(`Finished scrolling after ${scrollAttempts} attempts`);
117
118 // Final extraction
119 return await extractCodesFromTelegram();
120 }
121
122 async function setupTelegramExtractor() {
123 console.log('Setting up Telegram code extractor...');
124
125 // Create control panel
126 const controlPanel = document.createElement('div');
127 controlPanel.id = 'telegram-extractor-panel';
128 controlPanel.style.cssText = `
129 position: fixed;
130 top: 80px;
131 right: 20px;
132 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
133 color: white;
134 padding: 20px;
135 border-radius: 12px;
136 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
137 z-index: 10000;
138 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
139 min-width: 280px;
140 backdrop-filter: blur(10px);
141 `;
142
143 controlPanel.innerHTML = `
144 <div style="font-size: 16px; font-weight: bold; margin-bottom: 15px; display: flex; align-items: center; gap: 8px;">
145 <span style="font-size: 20px;">🎁</span>
146 Red Packet Extractor
147 </div>
148 <div style="font-size: 13px; margin-bottom: 15px; opacity: 0.9; line-height: 1.4;">
149 Extract codes from all your groups
150 </div>
151 <div id="codes-count" style="font-size: 14px; margin-bottom: 15px; padding: 10px; background: rgba(255,255,255,0.2); border-radius: 8px; text-align: center;">
152 <span style="font-size: 24px; font-weight: bold;" id="count-number">0</span>
153 <div style="font-size: 12px; margin-top: 4px; opacity: 0.9;">codes found</div>
154 </div>
155 <div id="group-progress" style="font-size: 12px; margin-bottom: 15px; padding: 8px; background: rgba(255,255,255,0.15); border-radius: 6px; text-align: center; display: none;">
156 <div id="group-progress-text">Processing groups...</div>
157 </div>
158 <button id="extract-all-btn" style="
159 width: 100%;
160 padding: 12px;
161 background: white;
162 color: #667eea;
163 border: none;
164 border-radius: 8px;
165 font-weight: bold;
166 cursor: pointer;
167 font-size: 14px;
168 transition: all 0.3s;
169 margin-bottom: 10px;
170 ">Extract From All Groups</button>
171 <button id="extract-btn" style="
172 width: 100%;
173 padding: 12px;
174 background: rgba(255,255,255,0.2);
175 color: white;
176 border: 1px solid rgba(255,255,255,0.3);
177 border-radius: 8px;
178 font-weight: 500;
179 cursor: pointer;
180 font-size: 13px;
181 transition: all 0.3s;
182 margin-bottom: 10px;
183 ">Extract Current Group Only</button>
184 <button id="clear-codes-btn" style="
185 width: 100%;
186 padding: 10px;
187 background: rgba(255,255,255,0.2);
188 color: white;
189 border: 1px solid rgba(255,255,255,0.3);
190 border-radius: 8px;
191 font-weight: 500;
192 cursor: pointer;
193 font-size: 13px;
194 transition: all 0.3s;
195 ">Clear Stored Codes</button>
196 <div id="status-message" style="font-size: 12px; margin-top: 12px; padding: 8px; background: rgba(255,255,255,0.15); border-radius: 6px; text-align: center; display: none;"></div>
197 `;
198
199 document.body.appendChild(controlPanel);
200
201 // Add hover effects
202 const extractAllBtn = document.getElementById('extract-all-btn');
203 const extractBtn = document.getElementById('extract-btn');
204 const clearBtn = document.getElementById('clear-codes-btn');
205
206 extractAllBtn.addEventListener('mouseenter', () => {
207 extractAllBtn.style.transform = 'translateY(-2px)';
208 extractAllBtn.style.boxShadow = '0 4px 12px rgba(0,0,0,0.2)';
209 });
210 extractAllBtn.addEventListener('mouseleave', () => {
211 extractAllBtn.style.transform = 'translateY(0)';
212 extractAllBtn.style.boxShadow = 'none';
213 });
214
215 extractBtn.addEventListener('mouseenter', () => {
216 extractBtn.style.transform = 'translateY(-2px)';
217 extractBtn.style.boxShadow = '0 4px 12px rgba(0,0,0,0.2)';
218 });
219 extractBtn.addEventListener('mouseleave', () => {
220 extractBtn.style.transform = 'translateY(0)';
221 extractBtn.style.boxShadow = 'none';
222 });
223
224 clearBtn.addEventListener('mouseenter', () => {
225 clearBtn.style.background = 'rgba(255,255,255,0.3)';
226 });
227 clearBtn.addEventListener('mouseleave', () => {
228 clearBtn.style.background = 'rgba(255,255,255,0.2)';
229 });
230
231 // Update count on load
232 const storedCodes = await GM.getValue('redPacketCodes', '[]');
233 const codes = JSON.parse(storedCodes);
234 document.getElementById('count-number').textContent = codes.length;
235
236 // Extract from all groups button handler
237 extractAllBtn.addEventListener('click', async () => {
238 extractAllBtn.disabled = true;
239 extractAllBtn.textContent = 'Extracting...';
240
241 const groupProgress = document.getElementById('group-progress');
242 const groupProgressText = document.getElementById('group-progress-text');
243 groupProgress.style.display = 'block';
244
245 try {
246 // Get all chat items from sidebar
247 const chatItems = document.querySelectorAll('.chatlist-chat');
248 console.log(`Found ${chatItems.length} chats in sidebar`);
249
250 groupProgressText.textContent = `Found ${chatItems.length} groups. Starting extraction...`;
251 await delay(1000);
252
253 // Store all codes from all groups
254 let allCodes = new Set();
255
256 // Get existing codes
257 const existingCodes = await GM.getValue('redPacketCodes', '[]');
258 const existingCodesArray = JSON.parse(existingCodes);
259 existingCodesArray.forEach(code => allCodes.add(code));
260
261 for (let i = 0; i < chatItems.length; i++) {
262 const chatItem = chatItems[i];
263 groupProgressText.textContent = `Processing group ${i + 1}/${chatItems.length}...`;
264
265 // Click on the chat to open it
266 chatItem.click();
267 await delay(2000); // Wait for chat to load
268
269 // Extract codes from this group
270 const codes = await scrollAndExtractAllCodes();
271 codes.forEach(code => allCodes.add(code));
272
273 // Update count
274 document.getElementById('count-number').textContent = allCodes.size;
275 groupProgressText.textContent = `Group ${i + 1}/${chatItems.length}: Found ${codes.length} codes (Total: ${allCodes.size})`;
276
277 console.log(`Extracted ${codes.length} codes from group ${i + 1}. Total unique codes: ${allCodes.size}`);
278 }
279
280 // Save all codes
281 await GM.setValue('redPacketCodes', JSON.stringify(Array.from(allCodes)));
282
283 const statusMsg = document.getElementById('status-message');
284 statusMsg.style.display = 'block';
285 statusMsg.style.background = 'rgba(76, 175, 80, 0.3)';
286 statusMsg.textContent = `✓ Extracted ${allCodes.size} total codes from ${chatItems.length} groups!`;
287
288 groupProgress.style.display = 'none';
289
290 setTimeout(() => {
291 statusMsg.style.display = 'none';
292 }, 5000);
293 } catch (error) {
294 console.error('Error extracting codes from all groups:', error);
295 const statusMsg = document.getElementById('status-message');
296 statusMsg.style.display = 'block';
297 statusMsg.style.background = 'rgba(244, 67, 54, 0.3)';
298 statusMsg.textContent = '✗ Error extracting codes';
299 groupProgress.style.display = 'none';
300 } finally {
301 extractAllBtn.disabled = false;
302 extractAllBtn.textContent = 'Extract From All Groups';
303 }
304 });
305
306 // Extract button handler (current group only)
307 extractBtn.addEventListener('click', async () => {
308 extractBtn.disabled = true;
309 extractBtn.textContent = 'Extracting...';
310
311 try {
312 const codes = await scrollAndExtractAllCodes();
313 document.getElementById('count-number').textContent = codes.length;
314
315 const statusMsg = document.getElementById('status-message');
316 statusMsg.style.display = 'block';
317 statusMsg.style.background = 'rgba(76, 175, 80, 0.3)';
318 statusMsg.textContent = `✓ Extracted ${codes.length} codes successfully!`;
319
320 setTimeout(() => {
321 statusMsg.style.display = 'none';
322 }, 3000);
323 } catch (error) {
324 console.error('Error extracting codes:', error);
325 const statusMsg = document.getElementById('status-message');
326 statusMsg.style.display = 'block';
327 statusMsg.style.background = 'rgba(244, 67, 54, 0.3)';
328 statusMsg.textContent = '✗ Error extracting codes';
329 } finally {
330 extractBtn.disabled = false;
331 extractBtn.textContent = 'Extract Current Group Only';
332 }
333 });
334
335 // Clear button handler
336 clearBtn.addEventListener('click', async () => {
337 await GM.setValue('redPacketCodes', '[]');
338 await GM.deleteValue('currentCodeIndex');
339 document.getElementById('count-number').textContent = '0';
340
341 const statusMsg = document.getElementById('status-message');
342 statusMsg.style.display = 'block';
343 statusMsg.style.background = 'rgba(255, 152, 0, 0.3)';
344 statusMsg.textContent = '✓ All codes cleared';
345
346 setTimeout(() => {
347 statusMsg.style.display = 'none';
348 }, 2000);
349 });
350
351 // Auto-extract on page load
352 await delay(2000);
353 console.log('Auto-extracting codes on page load...');
354 const extractedCodes = await scrollAndExtractAllCodes();
355 document.getElementById('count-number').textContent = extractedCodes.length;
356 }
357
358 // ==================== BINANCE FUNCTIONALITY ====================
359 async function claimRedPacketOnBinance() {
360 console.log('Starting Binance red packet claiming process...');
361
362 // Get stored codes
363 const storedCodes = await GM.getValue('redPacketCodes', '[]');
364 const codes = JSON.parse(storedCodes);
365
366 if (codes.length === 0) {
367 console.log('No codes available to claim');
368 return { success: false, message: 'No codes available' };
369 }
370
371 // Get current index
372 let currentIndex = await GM.getValue('currentCodeIndex', 0);
373
374 if (currentIndex >= codes.length) {
375 console.log('All codes have been processed');
376 return { success: false, message: 'All codes claimed' };
377 }
378
379 const currentCode = codes[currentIndex];
380 console.log(`Claiming code ${currentIndex + 1}/${codes.length}: ${currentCode}`);
381
382 try {
383 // Find input field
384 const input = document.querySelector('input[placeholder="Enter red packet code"]');
385 if (!input) {
386 throw new Error('Input field not found');
387 }
388
389 // Clear and enter code
390 input.value = '';
391 input.focus();
392 input.value = currentCode;
393
394 // Trigger input event to enable the button
395 input.dispatchEvent(new Event('input', { bubbles: true }));
396 input.dispatchEvent(new Event('change', { bubbles: true }));
397
398 console.log('Code entered into input field');
399 await delay(500);
400
401 // Find and click Claim button
402 const claimButton = Array.from(document.querySelectorAll('button')).find(btn =>
403 btn.textContent.trim() === 'Claim' &&
404 !btn.classList.contains('inactive') &&
405 btn.getAttribute('aria-disabled') !== 'true'
406 );
407
408 if (!claimButton) {
409 throw new Error('Claim button not found or not enabled');
410 }
411
412 console.log('Clicking Claim button...');
413 claimButton.click();
414
415 // Wait for popup to appear
416 await delay(2000);
417
418 // Look for "Open" button in popup
419 const openButton = Array.from(document.querySelectorAll('button')).find(btn =>
420 btn.textContent.trim() === 'Open'
421 );
422
423 if (openButton) {
424 console.log('Found Open button, clicking...');
425 openButton.click();
426 await delay(2000);
427
428 // Look for close button (X button)
429 const closeButton = document.querySelector('button[aria-label="Close"], button.bn-modal-close, button[class*="close"]');
430 if (closeButton) {
431 console.log('Found close button, clicking...');
432 closeButton.click();
433 await delay(1000);
434 }
435 }
436
437 // Move to next code
438 currentIndex++;
439 await GM.setValue('currentCodeIndex', currentIndex);
440
441 console.log(`Successfully processed code. Moving to next (${currentIndex}/${codes.length})`);
442 return { success: true, message: `Claimed ${currentIndex}/${codes.length}`, currentIndex, total: codes.length };
443
444 } catch (error) {
445 console.error('Error claiming red packet:', error);
446 // Still move to next code even if there's an error
447 currentIndex++;
448 await GM.setValue('currentCodeIndex', currentIndex);
449 return { success: false, message: error.message, currentIndex, total: codes.length };
450 }
451 }
452
453 async function setupBinanceClaimer() {
454 console.log('Setting up Binance auto claimer...');
455
456 // Create control panel
457 const controlPanel = document.createElement('div');
458 controlPanel.id = 'binance-claimer-panel';
459 controlPanel.style.cssText = `
460 position: fixed;
461 top: 80px;
462 right: 20px;
463 background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
464 color: white;
465 padding: 20px;
466 border-radius: 12px;
467 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
468 z-index: 10000;
469 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
470 min-width: 300px;
471 backdrop-filter: blur(10px);
472 `;
473
474 controlPanel.innerHTML = `
475 <div style="font-size: 16px; font-weight: bold; margin-bottom: 15px; display: flex; align-items: center; gap: 8px;">
476 <span style="font-size: 20px;">🎁</span>
477 Auto Claimer
478 </div>
479 <div style="font-size: 13px; margin-bottom: 15px; opacity: 0.9; line-height: 1.4;">
480 Automatically claim red packets one by one
481 </div>
482 <div id="progress-info" style="font-size: 14px; margin-bottom: 15px; padding: 12px; background: rgba(255,255,255,0.2); border-radius: 8px;">
483 <div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
484 <span>Progress:</span>
485 <span id="progress-text" style="font-weight: bold;">0/0</span>
486 </div>
487 <div style="width: 100%; height: 8px; background: rgba(255,255,255,0.3); border-radius: 4px; overflow: hidden;">
488 <div id="progress-bar" style="width: 0%; height: 100%; background: white; transition: width 0.3s;"></div>
489 </div>
490 </div>
491 <button id="start-claiming-btn" style="
492 width: 100%;
493 padding: 12px;
494 background: white;
495 color: #f5576c;
496 border: none;
497 border-radius: 8px;
498 font-weight: bold;
499 cursor: pointer;
500 font-size: 14px;
501 transition: all 0.3s;
502 margin-bottom: 10px;
503 ">Start Auto Claiming</button>
504 <button id="stop-claiming-btn" style="
505 width: 100%;
506 padding: 10px;
507 background: rgba(255,255,255,0.2);
508 color: white;
509 border: 1px solid rgba(255,255,255,0.3);
510 border-radius: 8px;
511 font-weight: 500;
512 cursor: pointer;
513 font-size: 13px;
514 transition: all 0.3s;
515 display: none;
516 ">Stop Claiming</button>
517 <button id="reset-progress-btn" style="
518 width: 100%;
519 padding: 10px;
520 background: rgba(255,255,255,0.2);
521 color: white;
522 border: 1px solid rgba(255,255,255,0.3);
523 border-radius: 8px;
524 font-weight: 500;
525 cursor: pointer;
526 font-size: 13px;
527 transition: all 0.3s;
528 ">Reset Progress</button>
529 <div id="claim-status" style="font-size: 12px; margin-top: 12px; padding: 8px; background: rgba(255,255,255,0.15); border-radius: 6px; text-align: center; display: none;"></div>
530 `;
531
532 document.body.appendChild(controlPanel);
533
534 let isClaimingActive = false;
535 let claimingInterval = null;
536
537 const startBtn = document.getElementById('start-claiming-btn');
538 const stopBtn = document.getElementById('stop-claiming-btn');
539 const resetBtn = document.getElementById('reset-progress-btn');
540 const progressText = document.getElementById('progress-text');
541 const progressBar = document.getElementById('progress-bar');
542 const statusDiv = document.getElementById('claim-status');
543
544 // Add hover effects
545 startBtn.addEventListener('mouseenter', () => {
546 startBtn.style.transform = 'translateY(-2px)';
547 startBtn.style.boxShadow = '0 4px 12px rgba(0,0,0,0.2)';
548 });
549 startBtn.addEventListener('mouseleave', () => {
550 startBtn.style.transform = 'translateY(0)';
551 startBtn.style.boxShadow = 'none';
552 });
553
554 // Update progress display
555 async function updateProgress() {
556 const storedCodes = await GM.getValue('redPacketCodes', '[]');
557 const codes = JSON.parse(storedCodes);
558 const currentIndex = await GM.getValue('currentCodeIndex', 0);
559
560 progressText.textContent = `${currentIndex}/${codes.length}`;
561 const percentage = codes.length > 0 ? (currentIndex / codes.length) * 100 : 0;
562 progressBar.style.width = `${percentage}%`;
563 }
564
565 // Initial progress update
566 await updateProgress();
567
568 // Start claiming
569 startBtn.addEventListener('click', async () => {
570 isClaimingActive = true;
571 startBtn.style.display = 'none';
572 stopBtn.style.display = 'block';
573 resetBtn.disabled = true;
574 resetBtn.style.opacity = '0.5';
575
576 statusDiv.style.display = 'block';
577 statusDiv.style.background = 'rgba(33, 150, 243, 0.3)';
578 statusDiv.textContent = '⏳ Auto claiming started...';
579
580 // Claim immediately, then set interval
581 async function claimNext() {
582 if (!isClaimingActive) return;
583
584 const result = await claimRedPacketOnBinance();
585 await updateProgress();
586
587 if (result.success) {
588 statusDiv.style.background = 'rgba(76, 175, 80, 0.3)';
589 statusDiv.textContent = `✓ ${result.message}`;
590 } else {
591 if (result.message === 'All codes claimed') {
592 statusDiv.style.background = 'rgba(255, 152, 0, 0.3)';
593 statusDiv.textContent = '✓ All codes have been claimed!';
594 isClaimingActive = false;
595 startBtn.style.display = 'block';
596 stopBtn.style.display = 'none';
597 resetBtn.disabled = false;
598 resetBtn.style.opacity = '1';
599 if (claimingInterval) clearInterval(claimingInterval);
600 } else {
601 statusDiv.style.background = 'rgba(244, 67, 54, 0.3)';
602 statusDiv.textContent = `⚠ Error: ${result.message}`;
603 }
604 }
605 }
606
607 await claimNext();
608 claimingInterval = setInterval(claimNext, 5000); // Claim every 5 seconds
609 });
610
611 // Stop claiming
612 stopBtn.addEventListener('click', () => {
613 isClaimingActive = false;
614 if (claimingInterval) {
615 clearInterval(claimingInterval);
616 claimingInterval = null;
617 }
618 startBtn.style.display = 'block';
619 stopBtn.style.display = 'none';
620 resetBtn.disabled = false;
621 resetBtn.style.opacity = '1';
622
623 statusDiv.style.background = 'rgba(255, 152, 0, 0.3)';
624 statusDiv.textContent = '⏸ Claiming stopped';
625 });
626
627 // Reset progress
628 resetBtn.addEventListener('click', async () => {
629 await GM.deleteValue('currentCodeIndex');
630 await updateProgress();
631
632 statusDiv.style.display = 'block';
633 statusDiv.style.background = 'rgba(255, 152, 0, 0.3)';
634 statusDiv.textContent = '✓ Progress reset';
635
636 setTimeout(() => {
637 statusDiv.style.display = 'none';
638 }, 2000);
639 });
640 }
641
642 // ==================== INITIALIZATION ====================
643 async function init() {
644 console.log('Initializing extension...');
645 console.log('Current URL:', window.location.href);
646
647 // Wait for page to be ready
648 await delay(1500);
649
650 if (window.location.href.includes('web.telegram.org')) {
651 console.log('Running on Telegram');
652 await setupTelegramExtractor();
653 } else if (window.location.href.includes('binance.com') && window.location.href.includes('/my/wallet/account/payment/cryptobox')) {
654 console.log('Running on Binance');
655 await setupBinanceClaimer();
656 }
657 }
658
659 // Start the extension
660 if (document.readyState === 'loading') {
661 document.addEventListener('DOMContentLoaded', init);
662 } else {
663 init();
664 }
665})();