Created
March 10, 2026 20:29
-
-
Save caioluders/8160a01107a2f730d696da0081a8808b to your computer and use it in GitHub Desktop.
telegram web k
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (async () => { | |
| const DELAY_PROFILE = 800; | |
| const DELAY_BACK = 600; | |
| const DELAY_SEND = 1000; | |
| const MESSAGE_TEMPLATE = (username) => `Hey ${username}, check this out!`; // β customize your message here | |
| const sleep = ms => new Promise(r => setTimeout(r, ms)); | |
| const rightSidebar = document.querySelector('.sidebar-right'); | |
| const container = rightSidebar?.querySelector('.search-super-content-members'); | |
| if (!container) { | |
| console.log('β Open group info β Members in the right sidebar first.'); | |
| return; | |
| } | |
| // Deduplicate members | |
| const seen = new Set(); | |
| const memberRows = []; | |
| container.querySelectorAll('[data-peer-id]').forEach(el => { | |
| const id = Number(el.dataset.peerId); | |
| if (id > 0 && !seen.has(id)) { | |
| seen.add(id); | |
| memberRows.push(el); | |
| } | |
| }); | |
| console.log(`π₯ Found ${memberRows.length} unique members.`); | |
| // Step 1: Resolve from IndexedDB | |
| const resolved = new Map(); | |
| const dbNames = ['tweb-account-1','tweb-account-2','tweb-account-3','tweb-account-4']; | |
| for (const dbName of dbNames) { | |
| try { | |
| const db = await new Promise((res, rej) => { | |
| const req = indexedDB.open(dbName); | |
| req.onsuccess = () => res(req.result); | |
| req.onerror = () => rej(req.error); | |
| }); | |
| if (![...db.objectStoreNames].includes('users')) { db.close(); continue; } | |
| const tx = db.transaction('users', 'readonly'); | |
| const store = tx.objectStore('users'); | |
| const users = await new Promise(r => { const req = store.getAll(); req.onsuccess = () => r(req.result); }); | |
| db.close(); | |
| for (const u of users) { | |
| const id = u.id || u.userId; | |
| if (!seen.has(id)) continue; | |
| const uname = u.username || u.usernames?.[0]?.username; | |
| if (uname) resolved.set(id, uname); | |
| } | |
| if (resolved.size > 0) { console.log(`π¦ Cache: ${resolved.size} usernames`); break; } | |
| } catch {} | |
| } | |
| // Step 2: Click through uncached | |
| const uncachedRows = memberRows.filter(r => !resolved.has(Number(r.dataset.peerId))); | |
| const noUsername = []; | |
| if (uncachedRows.length > 0) { | |
| console.log(`π ${uncachedRows.length} uncached. Clicking through...`); | |
| for (let i = 0; i < uncachedRows.length; i++) { | |
| const row = uncachedRows[i]; | |
| const peerId = Number(row.dataset.peerId); | |
| row.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); | |
| row.dispatchEvent(new MouseEvent('mouseup', { bubbles: true })); | |
| row.dispatchEvent(new MouseEvent('click', { bubbles: true })); | |
| await sleep(DELAY_PROFILE); | |
| let username = null; | |
| for (const r of rightSidebar.querySelectorAll('.row')) { | |
| const sub = r.querySelector('.row-subtitle'); | |
| const title = r.querySelector('.row-title'); | |
| if (sub && /^Username$/i.test(sub.textContent.trim()) && title) { | |
| username = title.textContent.trim(); | |
| break; | |
| } | |
| } | |
| const displayName = rightSidebar.querySelector('.profile-name')?.textContent?.trim() || `id:${peerId}`; | |
| if (username) { | |
| resolved.set(peerId, username); | |
| console.log(` β ${i + 1}/${uncachedRows.length} β ${displayName} β @${username}`); | |
| } else { | |
| noUsername.push(displayName); | |
| console.log(` β ${i + 1}/${uncachedRows.length} β ${displayName} β no username`); | |
| } | |
| const backBtn = rightSidebar.querySelector('.sidebar-back-button'); | |
| if (backBtn) { | |
| backBtn.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); | |
| backBtn.dispatchEvent(new MouseEvent('mouseup', { bubbles: true })); | |
| backBtn.dispatchEvent(new MouseEvent('click', { bubbles: true })); | |
| await sleep(DELAY_BACK); | |
| } | |
| } | |
| } | |
| const usernames = [...new Set(resolved.values())].map(u => '@' + u); | |
| console.log(`\nβ Collected ${usernames.length} usernames.`); | |
| if (noUsername.length) console.log(`β ${noUsername.length} without username: ${noUsername.join(', ')}`); | |
| // Step 3: Send messages | |
| if (usernames.length === 0) { console.log('No usernames to message.'); return; } | |
| console.log(`\nπ¨ Sending ${usernames.length} messages (${DELAY_SEND}ms apart)...`); | |
| console.log(` Message template: "${MESSAGE_TEMPLATE('@example')}"`); | |
| console.log(' β οΈ Type STOP in console to abort: window.__STOP_SENDING = true'); | |
| window.__STOP_SENDING = false; | |
| const input = document.querySelector('.input-message-input[contenteditable="true"]'); | |
| const sendBtn = document.querySelector('.btn-send, .send-btn, .btn-icon.send'); | |
| if (!input) { console.log('β Chat input not found. Make sure the group chat is open.'); return; } | |
| let sent = 0; | |
| for (const username of usernames) { | |
| if (window.__STOP_SENDING) { console.log('π Stopped by user.'); break; } | |
| const msg = MESSAGE_TEMPLATE(username); | |
| // Set message in input | |
| input.focus(); | |
| input.innerHTML = ''; | |
| document.execCommand('insertText', false, msg); | |
| input.dispatchEvent(new Event('input', { bubbles: true })); | |
| await sleep(300); | |
| // Press Enter to send | |
| input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, bubbles: true })); | |
| await sleep(DELAY_SEND); | |
| sent++; | |
| console.log(` π¨ ${sent}/${usernames.length} β sent to ${username}`); | |
| } | |
| console.log(`\nβ Done! Sent ${sent}/${usernames.length} messages.`); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment