changed to chromium, added popup navigations

This commit is contained in:
TDCroPower 2025-06-06 00:45:57 +02:00
parent b5465e3cb1
commit c8b5723909

View file

@ -1,4 +1,4 @@
// import { firefox } from 'playwright-firefox'; // import { chromium } from 'patchright';
import { chromium } from 'patchright'; import { chromium } from 'patchright';
import { datetime, filenamify, prompt, handleSIGINT } from './src/util.js'; import { datetime, filenamify, prompt, handleSIGINT } from './src/util.js';
import { cfg } from './src/config.js'; import { cfg } from './src/config.js';
@ -14,12 +14,10 @@ const { fingerprint, headers } = new FingerprintGenerator().getFingerprint({
const context = await chromium.launchPersistentContext(cfg.dir.browser, { const context = await chromium.launchPersistentContext(cfg.dir.browser, {
headless: cfg.headless, headless: cfg.headless,
// viewport: { width: cfg.width, height: cfg.height }, locale: 'en-US', // always use English for locator texts
locale: 'en-US', // ignore OS locale to be sure to have english text for locators -> done via /en in URL recordVideo: cfg.record ? { dir: 'data/record/', size: { width: cfg.width, height: cfg.height } } : undefined,
recordVideo: cfg.record ? { dir: 'data/record/', size: { width: cfg.width, height: cfg.height } } : undefined, // will record a .webm video for each page navigated; without size, video would be scaled down to fit 800x800 recordHar: cfg.record ? { path: `data/record/aliexpress-${filenamify(datetime())}.har` } : undefined,
recordHar: cfg.record ? { path: `data/record/aliexpress-${filenamify(datetime())}.har` } : undefined, // will record a HAR file with network requests and responses; can be imported in Chrome devtools handleSIGINT: false,
handleSIGINT: false, // have to handle ourselves and call context.close(), otherwise recordings from above won't be saved
// e.g. for coins, mobile view is needed, otherwise it just says to install the app
userAgent: fingerprint.navigator.userAgent, userAgent: fingerprint.navigator.userAgent,
viewport: { viewport: {
width: fingerprint.screen.width, width: fingerprint.screen.width,
@ -28,7 +26,6 @@ const context = await chromium.launchPersistentContext(cfg.dir.browser, {
extraHTTPHeaders: { extraHTTPHeaders: {
'accept-language': headers['accept-language'], 'accept-language': headers['accept-language'],
}, },
// https://peter.sh/experiments/chromium-command-line-switches/
args: [ args: [
'--hide-crash-restore-bubble', '--hide-crash-restore-bubble',
], ],
@ -44,45 +41,57 @@ const page = context.pages().length ? context.pages()[0] : await context.newPage
const auth = async url => { const auth = async url => {
console.log('auth', url); console.log('auth', url);
await page.goto(url, { waitUntil: 'domcontentloaded' }); await page.goto(url, { waitUntil: 'domcontentloaded' });
// redirects to https://login.aliexpress.com/?return_url=https%3A%2F%2Fwww.aliexpress.com%2Fp%2Fcoin-pc-index%2Findex.html
await Promise.any([page.waitForURL(/.*login\.aliexpress.com.*/).then(async () => { await Promise.any([page.waitForURL(/.*login\.aliexpress.com.*/).then(async () => {
// manual login
console.error('Not logged in! Will wait for 120s for you to login in the browser or terminal...'); console.error('Not logged in! Will wait for 120s for you to login in the browser or terminal...');
context.setDefaultTimeout(120 * 1000); context.setDefaultTimeout(120 * 1000);
// or try automated page.locator('span:has-text("Switch account")').click().catch(_ => {});
page.locator('span:has-text("Switch account")').click().catch(_ => {}); // sometimes no longer logged in, but previous user/email is pre-selected -> in this case we want to go back to the classic login const login = page.locator('#root');
const login = page.locator('#root'); // not universal: .content, .nfm-login
const email = cfg.ae_email || await prompt({ message: 'Enter email' }); const email = cfg.ae_email || await prompt({ message: 'Enter email' });
const emailInput = login.locator('input[label="Email or phone number"]'); const emailInput = login.locator('input[label="Email or phone number"]');
await emailInput.fill(email); await emailInput.fill(email);
await emailInput.blur(); // otherwise Continue button stays disabled await emailInput.blur();
const continueButton = login.locator('button:has-text("Continue")'); const continueButton = login.locator('button:has-text("Continue")');
await continueButton.click({ force: true }); // normal click waits for button to no longer be covered by their suggestion menu, so we have to force click somewhere for the menu to close and then click await continueButton.click({ force: true });
const password = email && (cfg.ae_password || await prompt({ type: 'password', message: 'Enter password' })); const password = email && (cfg.ae_password || await prompt({ type: 'password', message: 'Enter password' }));
await login.locator('input[label="Password"]').fill(password); await login.locator('input[label="Password"]').fill(password);
await login.locator('button:has-text("Sign in")').click(); await login.locator('button:has-text("Sign in")').click();
const error = login.locator('.nfm-login-input-error-text'); const error = login.locator('.nfm-login-input-error-text');
error.waitFor().then(async _ => console.error('Login error (please restart):', await error.innerText())).catch(_ => console.log('No login error.')); error.waitFor().then(async _ => console.error('Login error (please restart):', await error.innerText())).catch(_ => console.log('No login error.'));
await page.waitForURL(u => u.toString().startsWith(url)); // e.g. https://m.aliexpress.com/p/coin-index/index.html?_immersiveMode=true&from=pc302 await page.waitForURL(u => u.toString().startsWith(url));
// TODO the following won't be executed anymore due to the navigation - patchright issue?
context.setDefaultTimeout(cfg.debug ? 0 : cfg.timeout); context.setDefaultTimeout(cfg.debug ? 0 : cfg.timeout);
console.log('Logged in!'); // this should still be printed, but isn't... console.log('Logged in!');
// await page.addLocatorHandler(page.getByRole('button', { name: 'Accept cookies' }), btn => btn.click());
// page.getByRole('button', { name: 'Accept cookies' }).click().then(_ => console.log('Accepted cookies')).catch(_ => { });
}), page.locator('.app-game').waitFor()]); }), page.locator('.app-game').waitFor()]);
}; };
// copied URLs from AliExpress app on tablet which has menu for the used webview // copied URLs from AliExpress app on tablet which has menu for the used webview
const urls = { const urls = {
// only work with mobile view:
coins: 'https://www.aliexpress.com/p/coin-pc-index/index.html', coins: 'https://www.aliexpress.com/p/coin-pc-index/index.html',
grow: 'https://m.aliexpress.com/p/ae_fruit/index.html', // firefox: stuck at 60% loading, chrome: loads, but canvas grow: 'https://m.aliexpress.com/p/ae_fruit/index.html',
gogo: 'https://m.aliexpress.com/p/gogo-match-cc/index.html', // closes firefox?! gogo: 'https://m.aliexpress.com/p/gogo-match-cc/index.html',
// only show notification to install the app euro: 'https://m.aliexpress.com/p/european-cup/index.html',
euro: 'https://m.aliexpress.com/p/european-cup/index.html', // doesn't load
merge: 'https://m.aliexpress.com/p/merge-market/index.html', merge: 'https://m.aliexpress.com/p/merge-market/index.html',
}; };
// Function to check and click the hideDoubleButton inside the modal if present
const clickHideDoubleButtonIfVisible = async () => {
try {
const modal = page.locator('.DoubleSignSelectModal');
if (await modal.isVisible({ timeout: 3000 })) {
const btn = modal.locator('.hideDoubleButton');
if (await btn.isVisible()) {
await btn.click();
console.log('hideDoubleButton was found and clicked.');
} else {
console.log('hideDoubleButton is not visible inside the modal.');
}
} else {
console.log('DoubleSignSelectModal is not visible.');
}
} catch (err) {
console.error('Error while clicking hideDoubleButton:', err);
}
};
const coins = async () => { const coins = async () => {
console.log('Checking coins...'); console.log('Checking coins...');
const collectBtn = page.locator('.signVersion-panel div:has-text("Collect")').first(); const collectBtn = page.locator('.signVersion-panel div:has-text("Collect")').first();
@ -90,50 +99,32 @@ const coins = async () => {
await Promise.any([ await Promise.any([
collectBtn.click().then(_ => console.log('Collected coins for today!')), collectBtn.click().then(_ => console.log('Collected coins for today!')),
moreBtn.waitFor().then(_ => console.log('No more coins to collect today!')), moreBtn.waitFor().then(_ => console.log('No more coins to collect today!')),
]); // sometimes did not make it click the collect button... moreBtn.isVisible() as alternative also didn't work ]);
// await collectBtn.click().catch(_ => moreBtn.waitFor()); // TODO change this since it's going to delay by timeout if already collected
console.log(await page.locator('.marquee-content:has-text(" coins")').first().innerText()); console.log(await page.locator('.marquee-content:has-text(" coins")').first().innerText());
const n = (await page.locator('.marquee-item:has-text(" coins")').first().innerText()).replace(' coins', ''); const n = (await page.locator('.marquee-item:has-text(" coins")').first().innerText()).replace(' coins', '');
console.log('Coins:', n); console.log('Coins:', n);
// console.log('Streak:', await page.locator('.title-box').innerText());
// console.log('Tomorrow:', await page.locator('.addcoin').innerText());
}; };
// const grow = async () => {
// await page.pause();
// };
//
// const gogo = async () => {
// await page.pause();
// };
//
// const euro = async () => {
// await page.pause();
// };
//
// const merge = async () => {
// await page.pause();
// };
try { try {
// await coins();
await [ await [
coins, async () => {
await auth(urls.coins);
await clickHideDoubleButtonIfVisible();
await coins();
},
// grow, // grow,
// gogo, // gogo,
// euro, // euro,
// merge, // merge,
].reduce((a, f) => a.then(async _ => { ].reduce((a, f) => a.then(async () => {
await auth(urls[f.name]);
await f(); await f();
console.log(); console.log();
}), Promise.resolve()); }), Promise.resolve());
// await page.pause();
} catch (error) { } catch (error) {
process.exitCode ||= 1; process.exitCode ||= 1;
console.error('--- Exception:'); console.error('--- Exception:');
console.error(error); // .toString()? console.error(error);
} }
if (page.video()) console.log('Recorded video:', await page.video().path()); if (page.video()) console.log('Recorded video:', await page.video().path());
await context.close(); await context.close();