| const express = require("express"); |
| const puppeteer = require("puppeteer-extra"); |
| const StealthPlugin = require("puppeteer-extra-plugin-stealth"); |
| const cheerio = require("cheerio"); |
| require('dotenv').config(); |
|
|
| |
| puppeteer.use(StealthPlugin()); |
|
|
| const app = express(); |
| const PORT = 7860; |
|
|
| |
| let browser = null; |
| let authenticatedPage = null; |
| let isLoggedIn = false; |
|
|
| async function getBrowser() { |
| if (!browser) { |
| browser = await puppeteer.launch({ |
| headless: "new", |
| args: [ |
| '--no-sandbox', |
| '--disable-setuid-sandbox', |
| '--disable-dev-shm-usage', |
| '--disable-accelerated-2d-canvas', |
| '--no-first-run', |
| '--no-zygote', |
| '--disable-gpu', |
| '--disable-web-security', |
| '--disable-features=VizDisplayCompositor' |
| ] |
| }); |
| } |
| return browser; |
| } |
|
|
| |
| async function getAuthenticatedPage() { |
| if (!authenticatedPage || authenticatedPage.isClosed()) { |
| const browser = await getBrowser(); |
| authenticatedPage = await setupPage(browser); |
| isLoggedIn = false; |
| } |
| return authenticatedPage; |
| } |
|
|
| |
| async function performLogin() { |
| if (isLoggedIn) return true; |
| |
| const page = await getAuthenticatedPage(); |
| |
| try { |
| console.log('Performing login...'); |
| console.log('Using email:', process.env.LOGIN_EMAIL ? 'Found' : 'Missing'); |
| console.log('Using password:', process.env.LOGIN_PASSWORD ? 'Found' : 'Missing'); |
| |
| |
| await page.goto('https://getsms.cc/auth/login', { |
| waitUntil: 'networkidle2', |
| timeout: 30000 |
| }); |
| |
| console.log('Loaded login page'); |
| |
| |
| await page.waitForSelector('form#login', { timeout: 10000 }); |
| console.log('Found login form'); |
| |
| |
| |
| |
| |
| await page.evaluate(() => { |
| const emailField = document.querySelector('input[name="mail"]'); |
| if (emailField) emailField.value = ''; |
| }); |
| await page.focus('input[name="mail"]'); |
| await page.type('input[name="mail"]', process.env.LOGIN_EMAIL, { delay: 50 }); |
| |
| |
| await page.evaluate(() => { |
| const passwordField = document.querySelector('input[name="password"]'); |
| if (passwordField) passwordField.value = ''; |
| }); |
| await page.focus('input[name="password"]'); |
| await page.type('input[name="password"]', process.env.LOGIN_PASSWORD, { delay: 50 }); |
| |
| console.log('Filled login form'); |
| |
| |
| await new Promise(resolve => setTimeout(resolve, 1000)); |
| |
| |
| await page.evaluate(() => { |
| const form = document.querySelector('form#login'); |
| if (form) { |
| form.submit(); |
| } |
| }); |
| |
| console.log('Submitted form, waiting for response...'); |
| |
| |
| try { |
| await page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 15000 }); |
| console.log('Navigation detected'); |
| } catch (e) { |
| console.log('No navigation, checking for page updates...'); |
| await new Promise(resolve => setTimeout(resolve, 3000)); |
| } |
| |
| const currentUrl = page.url(); |
| console.log('Current URL after login attempt:', currentUrl); |
| |
| |
| |
| |
| |
| const errorMessage = await page.evaluate(() => { |
| const errorElements = document.querySelectorAll('.alert-danger, .text-danger, .error, .alert-error'); |
| for (let el of errorElements) { |
| if (el.textContent.trim()) { |
| return el.textContent.trim(); |
| } |
| } |
| return null; |
| }); |
| |
| if (errorMessage) { |
| console.log('Login error message:', errorMessage); |
| return false; |
| } |
| |
| |
| const loginCheck = await page.evaluate(() => { |
| const url = window.location.href; |
| const bodyText = document.body.innerText.toLowerCase(); |
| |
| |
| const indicators = { |
| notOnLoginPage: !url.includes('/auth/login'), |
| hasLogoutLink: document.querySelector('[href*="logout"]') !== null, |
| hasUserDropdown: document.querySelector('.dropdown-toggle') !== null, |
| hasUserProfile: document.querySelector('.user-profile') !== null, |
| hasDashboard: bodyText.includes('dashboard'), |
| hasWelcome: bodyText.includes('welcome'), |
| hasMyAccount: bodyText.includes('my account'), |
| urlIndicatesSuccess: url.includes('/dashboard') || url.includes('/account') || url === 'https://getsms.cc/' |
| }; |
| |
| console.log('Login indicators:', indicators); |
| |
| return { |
| success: Object.values(indicators).some(v => v), |
| indicators, |
| url, |
| pageTitle: document.title |
| }; |
| }); |
| |
| console.log('Login check result:', loginCheck); |
| |
| if (loginCheck.success) { |
| isLoggedIn = true; |
| console.log('✅ Login successful - authenticated session established'); |
| return true; |
| } else { |
| console.log('❌ Login failed - no success indicators found'); |
| |
| |
| const pageContent = await page.evaluate(() => { |
| return document.body.innerText.substring(0, 1000); |
| }); |
| console.log('Page content after login:', pageContent); |
| |
| return false; |
| } |
| } catch (error) { |
| console.error('Login error:', error); |
| return false; |
| } |
| } |
|
|
| |
| async function setupPage(browser) { |
| const page = await browser.newPage(); |
| |
| |
| await page.setViewport({ width: 1366, height: 768 }); |
| |
| |
| await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'); |
| |
| |
| await page.setExtraHTTPHeaders({ |
| 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', |
| 'Accept-Language': 'en-US,en;q=0.9', |
| 'Accept-Encoding': 'gzip, deflate, br', |
| 'DNT': '1', |
| 'Connection': 'keep-alive', |
| 'Upgrade-Insecure-Requests': '1', |
| }); |
| |
| return page; |
| } |
|
|
| |
| async function scrapeUK(pageNum = 1) { |
| const browser = await getBrowser(); |
| const page = await setupPage(browser); |
| |
| try { |
| const url = pageNum === 1 |
| ? "https://getsms.cc/temporary-phone-numbers/UK" |
| : `https://getsms.cc/temporary-phone-numbers/UK/${pageNum}`; |
| |
| |
| await page.goto(url, { |
| waitUntil: 'networkidle2', |
| timeout: 30000 |
| }); |
| |
| |
| await new Promise(resolve => setTimeout(resolve, Math.random() * 2000 + 1000)); |
| |
| |
| const content = await page.content(); |
| const $ = cheerio.load(content); |
| |
| const results = []; |
| $(".card").each((i, el) => { |
| const number = $(el).find("p.p-0.m-0.font-weight-bold").text().trim(); |
| const timeAgo = $(el).find("p.p-0.m-0.small").text().trim(); |
| const link = $(el).find("a.btn.btn-primary").attr("href"); |
| |
| if (number && timeAgo && link) { |
| results.push({ |
| number, |
| timeAgo, |
| link: `https://getsms.cc${link}`, |
| }); |
| } |
| }); |
| |
| return results; |
| } catch (error) { |
| console.error('Error scraping UK numbers:', error); |
| throw error; |
| } finally { |
| await page.close(); |
| } |
| } |
|
|
| |
| async function scrapeMessages(number) { |
| const browser = await getBrowser(); |
| const page = await setupPage(browser); |
| |
| try { |
| const url = `https://getsms.cc/info/${number}`; |
| |
| |
| await page.goto(url, { |
| waitUntil: 'networkidle2', |
| timeout: 30000 |
| }); |
| |
| |
| await new Promise(resolve => setTimeout(resolve, Math.random() * 2000 + 1000)); |
| |
| |
| const content = await page.content(); |
| const $ = cheerio.load(content); |
| |
| const messages = []; |
| $(".direct-chat-msg").each((i, el) => { |
| const from = $(el).find(".direct-chat-name").text().trim(); |
| const timeAgo = $(el).find("time").text().trim(); |
| const text = $(el).find(".direct-chat-text").text().trim(); |
| |
| |
| if (from && text) { |
| messages.push({ from, timeAgo, text }); |
| } |
| }); |
| |
| |
| return messages.slice(-3).reverse(); |
| } catch (error) { |
| console.error('Error scraping messages:', error); |
| throw error; |
| } finally { |
| await page.close(); |
| } |
| } |
|
|
| |
| app.get(["/uk", "/uk/:page"], async (req, res) => { |
| try { |
| const page = parseInt(req.params.page) || 1; |
| const numbers = await scrapeUK(page); |
| res.json({ |
| country: "United Kingdom", |
| page, |
| count: numbers.length, |
| numbers, |
| }); |
| } catch (err) { |
| console.error("Error:", err.message); |
| res.status(500).json({ error: "Failed to scrape numbers" }); |
| } |
| }); |
|
|
| |
| app.get("/msg/:number", async (req, res) => { |
| try { |
| const number = req.params.number; |
| const messages = await scrapeMessages(number); |
| res.json({ |
| number, |
| count: messages.length, |
| messages, |
| }); |
| } catch (err) { |
| console.error("Error:", err.message); |
| res.status(500).json({ error: "Failed to scrape messages" }); |
| } |
| }); |
|
|
| |
| process.on('SIGINT', async () => { |
| console.log('Shutting down gracefully...'); |
| if (authenticatedPage && !authenticatedPage.isClosed()) { |
| await authenticatedPage.close(); |
| } |
| if (browser) { |
| await browser.close(); |
| } |
| process.exit(0); |
| }); |
|
|
| process.on('SIGTERM', async () => { |
| console.log('Shutting down gracefully...'); |
| if (authenticatedPage && !authenticatedPage.isClosed()) { |
| await authenticatedPage.close(); |
| } |
| if (browser) { |
| await browser.close(); |
| } |
| process.exit(0); |
| }); |
|
|
| |
| app.listen(PORT, () => { |
| console.log(`Server running on http://localhost:${PORT}`); |
| console.log('Using Puppeteer with stealth mode to avoid detection'); |
| }); |