UNPKG

twx-media-scrapper

Version:

Twitter/x.com media content extractor via Puppeteer and TwitterDL

169 lines (140 loc) â€ĸ 5.73 kB
const fs = require("fs"); const path = require("path"); const readline = require("readline"); const puppeteer = require("puppeteer"); const CACHE_PATH = path.join(__dirname, "auth_cache.json"); // Helper sleep const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); // CLI Prompt const prompt = (question) => { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise(resolve => rl.question(question, answer => { rl.close(); resolve(answer); })); }; async function getTwitterAuth(email, username, password) { console.log("📌 Mengecek cache login..."); if (fs.existsSync(CACHE_PATH)) { const cache = JSON.parse(fs.readFileSync(CACHE_PATH, "utf-8")); if (cache.authorization && cache.cookie) { console.log("✅ Menggunakan auth_cache.json (token dan cookie)"); return cache; } } console.log("📌 Membuka browser untuk login..."); const browser = await puppeteer.launch({ headless: true, slowMo: 50, args: ["--no-sandbox", "--disable-setuid-sandbox"] }); const page = await browser.newPage(); await page.setUserAgent( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/123.0.0.0 Safari/537.36" ); let bearerToken = null; await page.setRequestInterception(true); page.on("request", request => request.continue()); page.on("response", async response => { const auth = response.request().headers()["authorization"]; if (auth && auth.startsWith("Bearer ") && !bearerToken) { console.log("✅ Bearer token ditemukan di response header"); bearerToken = auth; } }); try { await page.goto("https://x.com/login", { waitUntil: "networkidle2", timeout: 60000 }); } catch (err) { await browser.close(); throw new Error("❌ Gagal membuka halaman login Twitter."); } // Step 1: Email console.log("📌 Mengisi email..."); try { await page.waitForSelector('input[name="text"]', { visible: true, timeout: 10000 }); await page.type('input[name="text"]', email); await page.keyboard.press("Enter"); await sleep(2000); } catch { await browser.close(); throw new Error("❌ Input email tidak ditemukan. Gagal login."); } // Step 2: Username atau OTP let nextInputHandled = false; try { await page.waitForSelector('input[name="text"]', { visible: true, timeout: 8000 }); try { console.log("📌 Mengisi username..."); await page.type('input[name="text"]', username); await page.keyboard.press("Enter"); await sleep(2000); nextInputHandled = true; } catch { console.log("âš ī¸ Gagal isi username. Cek apakah ini OTP..."); } if (!nextInputHandled) { console.log("🔐 Twitter mungkin meminta kode verifikasi (OTP)"); const code = await prompt("Masukkan OTP dari email/HP: "); await page.type('input[name="text"]', code); await page.keyboard.press("Enter"); console.log("📨 Mengirim kode OTP..."); await sleep(3000); nextInputHandled = true; } } catch { console.log("â„šī¸ Tidak ada input tambahan, lanjut ke password..."); } // Step 3: Password console.log("📌 Mengisi password..."); try { await page.waitForSelector('input[name="password"]', { visible: true, timeout: 10000 }); await page.type('input[name="password"]', password); await page.keyboard.press("Enter"); await sleep(2000); } catch { await browser.close(); throw new Error("❌ Gagal menemukan input password. Mungkin akun butuh verifikasi tambahan atau salah kredensial."); } // Step 4: Tunggu navigasi console.log("📌 Menunggu proses login..."); try { await page.waitForNavigation({ waitUntil: "networkidle2", timeout: 20000 }); } catch { console.warn("âš ī¸ Timeout saat navigasi. Melanjutkan..."); } // Step 5: OTP tambahan const otpInput = await page.$('input[name="text"]'); if (otpInput) { console.log("🔐 Twitter meminta kode verifikasi tambahan (OTP kedua)"); const code = await prompt("Masukkan kode OTP lanjutan: "); await otpInput.type(code); await page.keyboard.press("Enter"); console.log("📨 Mengirim kode..."); try { await page.waitForNavigation({ waitUntil: "networkidle2", timeout: 15000 }); } catch { console.warn("âš ī¸ Timeout saat navigasi OTP. Melanjutkan..."); } await sleep(3000); } // Step 6: Ambil token dan cookie console.log("âŗ Menunggu Bearer Token..."); await sleep(5000); const cookies = await page.cookies(); const cookieHeader = cookies.map(c => `${c.name}=${c.value}`).join("; "); await browser.close(); if (!bearerToken) { console.error("❌ Bearer token tidak ditemukan."); throw new Error("Gagal ambil Bearer token."); } const authData = { authorization: bearerToken, cookie: cookieHeader }; fs.writeFileSync(CACHE_PATH, JSON.stringify(authData, null, 2)); console.log("✅ Token disimpan ke auth_cache.json"); return authData; } module.exports = { getTwitterAuth };