This commit is contained in:
jjbrunton committed 2026-01-05 15:04:16 +00:00
1 parent 58bb6b6dfc
commit 9602403f8a
1 file changed
+54 -43
+54 -43
View File
@@ -61,28 +61,45 @@ export class BrowserClient {
const page = await this.getPage();
await page.goto('https://www.reddit.com', { waitUntil: 'domcontentloaded', timeout: 30000 });
// Check for various login indicators
const isLoggedIn = await page.evaluate(() => {
// Check for user menu button
const userButton = document.querySelector('[data-testid="user-drawer-button"]');
if (userButton) return true;
const loginIndicators = [
'[data-testid="user-drawer-button"]',
'button[aria-label*="User menu"]',
'a[href^="https://www.reddit.com/user/"]',
'a[href^="/user/"]',
];
// Check for login/signup buttons which indicate NOT logged in
const loginButton = document.querySelector('a[href*="login"]');
const signupButton = document.querySelector('a[href*="register"]');
for (const selector of loginIndicators) {
const el = page.locator(selector).first();
if (await el.isVisible().catch(() => false)) {
return true;
}
}
// If we find login/signup prominently, we're not logged in
// But we need to be careful - some elements may exist in DOM
return !loginButton && !signupButton;
});
return isLoggedIn;
// Fallback to a direct auth check; this avoids false negatives from hidden login buttons.
return await this.isLoggedInViaApi(page);
} catch (error) {
this.logger.error('Failed to check session validity:', error);
return false;
}
}
private async isLoggedInViaApi(page: Page): Promise<boolean> {
try {
return await page.evaluate(async () => {
try {
const res = await fetch('https://www.reddit.com/api/me.json', { credentials: 'include' });
if (!res.ok) return false;
const data = await res.json();
return Boolean(data && data.name);
} catch {
return false;
}
});
} catch {
return false;
}
}
async login(): Promise<boolean> {
if (!this.config.reddit.username || !this.config.reddit.password) {
this.logger.error('Login failed: Missing credentials in config/env');
@@ -148,39 +165,33 @@ export class BrowserClient {
this.logger.info('Credentials submitted. Waiting for navigation...');
// Wait for successful login indicator
// We can reuse the checks from isSessionValid logic, but with a longer timeout
try {
await page.waitForSelector('[data-testid="user-drawer-button"]', { timeout: 20000 });
// Wait for navigation or a non-login URL
await Promise.race([
page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 20000 }).catch(() => undefined),
page.waitForURL((url) => !url.toString().includes('/login'), { timeout: 20000 }).catch(() => undefined),
]);
// Confirm auth via UI or API check
const isValid = await this.isSessionValid();
if (isValid) {
this.logger.info('Login successful!');
return true;
} catch (e) {
// Double check if we are redirected to home
const url = page.url();
if (url === 'https://www.reddit.com/' || url.match(/reddit\.com\/?$/)) {
// Verify one more time
const isValid = await this.isSessionValid();
if (isValid) {
this.logger.info('Login successful (verified by session check)');
return true;
}
}
// Check for error messages
const errorContent = await page.evaluate(() => {
const err = document.querySelector('[role="alert"], .AnimatedForm__errorMessage');
return err ? err.textContent : null;
});
if (errorContent) {
this.logger.error(`Login failed with error: ${errorContent}`);
} else {
this.logger.error('Login flow finished but session is invalid.');
}
return false;
}
// Check for error messages
const errorContent = await page.evaluate(() => {
const err = document.querySelector('[role="alert"], .AnimatedForm__errorMessage');
return err ? err.textContent : null;
});
if (errorContent) {
this.logger.error(`Login failed with error: ${errorContent}`);
} else {
this.logger.error('Login flow finished but session is invalid.');
}
return false;
} catch (error) {
this.logger.error('Error during automated login:', error);
return false;