From c12131e0c29a2e3a752ec6faf5dbaa79d3e813f8 Mon Sep 17 00:00:00 2001 From: Casey Timm Date: Thu, 24 Jul 2025 20:17:11 -0400 Subject: [PATCH] Moved to playwright --- .gitignore | 2 + package-lock.json | 45 +++++++++++++++++++ package.json | 1 + src/lib/united.js | 109 +++++++++++++++------------------------------- 4 files changed, 82 insertions(+), 75 deletions(-) diff --git a/.gitignore b/.gitignore index 3b462cb..df41f74 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ Thumbs.db # Vite vite.config.js.timestamp-* vite.config.ts.timestamp-* + +context \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 81df001..c84a229 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@auth/sveltekit": "^1.10.0", "@tailwindcss/vite": "^4.1.11", "cron": "^4.3.2", + "patchright": "^1.52.5", "postgres": "^3.4.7", "puppeteer": "^24.14.0", "puppeteer-extra": "^3.3.6", @@ -6113,6 +6114,50 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/patchright": { + "version": "1.52.5", + "resolved": "https://registry.npmjs.org/patchright/-/patchright-1.52.5.tgz", + "integrity": "sha512-wmRpsF9n02j0S+YDk0U3ouuWipHbUowwxbf/4K4G9ng311vvugoo8WndbU/fCsGtme8gYNfcEGcpfF6/L1NXHg==", + "license": "Apache-2.0", + "dependencies": { + "patchright-core": "1.52.5" + }, + "bin": { + "patchright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/patchright-core": { + "version": "1.52.5", + "resolved": "https://registry.npmjs.org/patchright-core/-/patchright-core-1.52.5.tgz", + "integrity": "sha512-8rnLVEK9jDZWzFPy2hCQrp4xhU7zgE8IqseZyjGkgxf+jpAWTuGNgIAlcsKZMfQrDL8j1mZgRIGNAQT00nk6QA==", + "license": "Apache-2.0", + "bin": { + "patchright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/patchright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", diff --git a/package.json b/package.json index 47974ce..4401fa6 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "@auth/sveltekit": "^1.10.0", "@tailwindcss/vite": "^4.1.11", "cron": "^4.3.2", + "patchright": "^1.52.5", "postgres": "^3.4.7", "puppeteer": "^24.14.0", "puppeteer-extra": "^3.3.6", diff --git a/src/lib/united.js b/src/lib/united.js index 5171e6d..0658405 100644 --- a/src/lib/united.js +++ b/src/lib/united.js @@ -1,9 +1,7 @@ -import puppeteer from 'puppeteer-extra'; -import StealthPlugin from 'puppeteer-extra-plugin-stealth'; import db from './db.js'; -import { json } from '@sveltejs/kit'; +import { chromium } from 'patchright'; -puppeteer.use(StealthPlugin()); +//const browser = await chromium.launch(); let state = []; let code = null; let needCode = false; @@ -31,67 +29,40 @@ export async function pullData(amount = 100) { code = null; needCode = false; state.push('Starting Browser'); - const browser = await puppeteer.launch({ - args: ['--no-sandbox'], - headless: true - //defaultViewport: null, - //args: ['--disable-blink-features=PrettyPrintJSONDocument'] + const browser = await chromium.launchPersistentContext('context', { + channel: 'chrome', + headless: true, + viewport: null + // do NOT add custom browser headers or userAgent }); const page = await browser.newPage(); - state.push('Loading Cookies'); - - const cookiesdb = await db`SELECT - name, - value, - domain, - path, - expires, - size, - httpOnly, - secure, - session, - priority, - sameParty, - sourceScheme, - sourcePort - FROM cookies`; - - cookiesdb.forEach((cookie) => { - cookie.expires = cookie.expires ? Number(cookie.expires) : undefined; - cookie.size = cookie.size ? Number(cookie.size) : undefined; - cookie.sourcePort = cookie.sourcePort ? Number(cookie.sourcePort) : undefined; - state.push('Loading cookie: ' + cookie.name); - browser.setCookie(cookie); - }); - state.push('Navigating to United FCU'); - - // Navigate the page to a URL. await page.goto('https://online.unitedfcu.com/unitedfederalcredituniononline/uux.aspx#/login'); - state.push(`Current URL: ${page.url()}`); if (page.url().includes('interstitial')) { - await page.waitForNavigation(); - state.push('Already logged in, navigating to dashboard'); + await page.waitForLoadState(); } if (!page.url().includes('dashboard')) { state.push('Logging in to United FCU'); - // Type into search box using accessible input name. await new Promise((resolve) => setTimeout(resolve, Math.random() * 5000)); - await page.locator('aria/Login ID').fill('92830'); + await page.getByLabel('Login ID').fill('92830'); await new Promise((resolve) => setTimeout(resolve, Math.random() * 5000)); - - await page.locator('aria/Password').fill('Cmtjlt13'); + await page.getByLabel('Password').fill('Cmtjlt13'); await new Promise((resolve) => setTimeout(resolve, Math.random() * 5000)); - - state.push('Submitting login form'); await page.keyboard.press('Enter'); - await page.waitForNavigation(); + //await page.waitForLoadState(); + //await new Promise((resolve) => setTimeout(resolve, 5 * 5000)); + + while (page.url().includes('interstitial')) { + await new Promise((resolve) => setTimeout(resolve, 1000)); + await page.waitForLoadState(); + } + const url = page.url(); state.push(`Current URL after login: ${url}`); @@ -99,12 +70,9 @@ export async function pullData(amount = 100) { state.push('MFA required, selecting SMS option'); console.log('MFA required, please complete the authentication process.'); await new Promise((resolve) => setTimeout(resolve, Math.random() * 5000)); - - await page.locator('aria/SMS: (XXX) XXX-4029').click(); - await page.waitForNavigation(); - //need to do some stuff ehre - await new Promise((resolve) => setTimeout(resolve, Math.random() * 5000)); - + await page.getByText('SMS: (XXX) XXX-4029').click(); + await page.waitForURL('**/mfa/entertarget'); + await page.getByPlaceholder('Secure Access Code'); //await page.keyboard.press('Tab'); state.push('Waiting for code input'); needCode = true; @@ -118,32 +86,20 @@ export async function pullData(amount = 100) { throw new Error('Code not provided within 5 minutes'); } state.push(`Got code: ${code}`); - - await page.locator('>>> [placeholder="Secure Access Code"]').fill(code); - //await page.keyboard.type(code); - code = null; - needCode = false; + await page.getByPlaceholder('Secure Access Code').fill(code); + await new Promise((resolve) => setTimeout(resolve, Math.random() * 5000)); await page.keyboard.press('Enter'); - await page.locator('aria/Register Device').click(); - await page.waitForNavigation(); + + await page.getByText('Access Code Accepted.').waitFor(); + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await page.keyboard.press('Enter'); + await page.waitForURL('**/dashboard'); + await page.screenshot({ path: 'united-login.png' }); + state.push('Logged in successfully'); } - - state.push('Saving cookies'); - let cookies = await browser.cookies(); - - cookies.forEach(async (cookie) => { - state.push('Saving cookie: ' + cookie.name); - - // Insert or update the cookie in the database - await db`DELETE FROM cookies WHERE name = ${cookie.name}`; - - await db`INSERT INTO cookies (name, value, domain, path, expires, size, httpOnly, secure, session, priority, sameParty, sourceScheme, sourcePort) - VALUES (${cookie.name}, ${cookie.value}, ${cookie.domain}, ${cookie.path}, ${cookie.expires}, ${cookie.size}, ${cookie.httpOnly}, ${cookie.secure}, ${cookie.session}, ${cookie.priority}, ${cookie.sameParty}, ${cookie.sourceScheme}, ${cookie.sourcePort})`; - }); } - //await new Promise((resolve) => setTimeout(resolve, 60000)); - state.push('Fetching q2token'); const q2token = (await browser.cookies()).find((cookie) => cookie.name === 'q2token')?.value; @@ -302,6 +258,9 @@ export async function pullData(amount = 100) { state.push(`Orphaning transaction: ${orphan.id}`); await db`UPDATE budget_transaction set transaction_id = null where id = ${orphan.id}`; } + + state.push('Done'); + await browser.close(); } catch (error) { console.error('Error in pullData:', error); state.push(`Error: ${error.message}`);