Added dialog handlers to automate the closing of native browser dialogs and updated the Discord payload build function to return null in case of filtered error.

This commit is contained in:
2025-12-22 22:15:11 +01:00
parent 7f761b539c
commit 94272ac7f3
3 changed files with 67 additions and 3 deletions

View File

@@ -228,6 +228,10 @@ export class Login {
await this.disableFido(page) await this.disableFido(page)
// CRITICAL: Setup dialog handlers BEFORE any login interactions
// This prevents native browser dialogs (Bluetooth, Windows Hello, Passkey) from blocking automation
this.passkeyHandler.setupDialogHandlers(page)
const [reloadResult, totpResult, portalCheck] = await Promise.allSettled([ const [reloadResult, totpResult, portalCheck] = await Promise.allSettled([
this.bot.browser.utils.reloadBadPage(page), this.bot.browser.utils.reloadBadPage(page),
this.totpHandler.tryAutoTotp(page, 'initial landing'), this.totpHandler.tryAutoTotp(page, 'initial landing'),

View File

@@ -31,6 +31,57 @@ export class PasskeyHandler {
}).catch(logError('LOGIN-FIDO', 'Route interception setup failed', this.bot.isMobile)) }).catch(logError('LOGIN-FIDO', 'Route interception setup failed', this.bot.isMobile))
} }
/**
* Setup dialog handlers to automatically dismiss native browser dialogs
* CRITICAL: This handles Bluetooth/Windows Hello/Passkey prompts that appear as native browser dialogs
* These are NOT DOM elements and cannot be clicked - they must be dismissed via page.on('dialog')
*/
public setupDialogHandlers(page: Page) {
// Remove any existing listeners to prevent duplicates
page.removeAllListeners('dialog')
page.on('dialog', async (dialog) => {
const message = dialog.message()
const type = dialog.type()
this.bot.log(
this.bot.isMobile,
'LOGIN-DIALOG',
`Native browser dialog detected: [${type}] "${message.substring(0, 100)}"`,
'warn'
)
// Auto-dismiss all dialogs (alert, confirm, prompt, beforeunload)
// For passkey/Bluetooth prompts, we want to DISMISS (equivalent to Cancel)
try {
if (type === 'beforeunload') {
// Accept beforeunload to allow navigation
await dialog.accept()
this.bot.log(this.bot.isMobile, 'LOGIN-DIALOG', 'Accepted beforeunload dialog', 'log', 'green')
} else {
// Dismiss all other dialogs (passkey, Bluetooth, alerts)
await dialog.dismiss()
this.bot.log(
this.bot.isMobile,
'LOGIN-DIALOG',
`Dismissed ${type} dialog: "${message.substring(0, 50)}"`,
'log',
'green'
)
}
} catch (error) {
this.bot.log(
this.bot.isMobile,
'LOGIN-DIALOG',
`Failed to handle dialog: ${error instanceof Error ? error.message : String(error)}`,
'error'
)
}
})
this.bot.log(this.bot.isMobile, 'LOGIN-DIALOG', 'Dialog handlers installed (auto-dismiss enabled)', 'log', 'cyan')
}
public async handlePasskeyPrompts(page: Page, context: 'main' | 'oauth') { public async handlePasskeyPrompts(page: Page, context: 'main' | 'oauth') {
let did = false let did = false

View File

@@ -41,14 +41,15 @@ function sanitizeSensitiveText(text: string): string {
/** /**
* Build the Discord payload from error and context (sanitizes content) * Build the Discord payload from error and context (sanitizes content)
* Returns null if error should be filtered (prevents sending)
*/ */
function buildDiscordPayload(config: Config, error: Error | string, additionalContext?: Record<string, unknown>) { function buildDiscordPayload(config: Config, error: Error | string, additionalContext?: Record<string, unknown>): { username: string; avatar_url?: string; embeds: DiscordEmbed[] } | null {
const errorMessage = error instanceof Error ? error.message : String(error) const errorMessage = error instanceof Error ? error.message : String(error)
const sanitizedForLogging = sanitizeSensitiveText(errorMessage) const sanitizedForLogging = sanitizeSensitiveText(errorMessage)
if (!shouldReportError(errorMessage)) { if (!shouldReportError(errorMessage)) {
process.stderr.write(`[ErrorReporting] Filtered error (expected/benign): ${sanitizedForLogging.substring(0, 100)}\n`) process.stderr.write(`[ErrorReporting] Filtered error (expected/benign): ${sanitizedForLogging.substring(0, 100)}\n`)
return { username: 'Microsoft-Rewards-Bot Error Reporter', content: 'Filtered error (not reported)' } return null // FIXED: Return null instead of sending dummy message
} }
const errorStack = error instanceof Error ? error.stack : undefined const errorStack = error instanceof Error ? error.stack : undefined
@@ -434,7 +435,15 @@ export async function sendErrorReport(
process.stderr.write(`[ErrorReporting] Trying webhook: ${webhookUrl}\n`) process.stderr.write(`[ErrorReporting] Trying webhook: ${webhookUrl}\n`)
try { try {
const response = await axios.post(webhookUrl, buildDiscordPayload(config, error, additionalContext), { // FIXED: Check if payload is null (filtered error)
const payload = buildDiscordPayload(config, error, additionalContext)
if (!payload) {
process.stderr.write('[ErrorReporting] Skipping webhook send (error was filtered)\n')
sent = true // Mark as "sent" to prevent fallback error message
break
}
const response = await axios.post(webhookUrl, payload, {
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
timeout: 10000 timeout: 10000
}) })