From 5651f62088c0dc9776fc0ff72e35542612a2ba80 Mon Sep 17 00:00:00 2001 From: LightZirconite Date: Thu, 13 Nov 2025 20:23:57 +0100 Subject: [PATCH] Fix error --- src/browser/BrowserUtil.ts | 43 +++++++++++++-- src/flows/DesktopFlow.ts | 9 ++- src/flows/MobileFlow.ts | 9 ++- src/functions/activities/Search.ts | 9 ++- .../notifications/ErrorReportingWebhook.ts | 55 +++++++++++++++---- 5 files changed, 105 insertions(+), 20 deletions(-) diff --git a/src/browser/BrowserUtil.ts b/src/browser/BrowserUtil.ts index b33633b..0719055 100644 --- a/src/browser/BrowserUtil.ts +++ b/src/browser/BrowserUtil.ts @@ -179,18 +179,51 @@ export default class BrowserUtil { const browser = page.context() const pages = browser.pages() + + // IMPROVED: If no pages exist, create a new one instead of throwing error + if (pages.length === 0) { + this.bot.log(this.bot.isMobile, 'GET-NEW-TAB', 'No pages found in context, creating new page', 'warn') + const newPage = await browser.newPage() + await this.bot.utils.wait(500) + return newPage + } + const newTab = pages[pages.length - 1] - if (newTab) { + // IMPROVED: Verify the page is not closed before returning + if (newTab && !newTab.isClosed()) { return newTab } - this.bot.log(this.bot.isMobile, 'GET-NEW-TAB', 'Unable to get latest tab', 'error') - throw new Error('Unable to get latest tab - no pages found in browser context') + // IMPROVED: If latest tab is closed, find first non-closed tab or create new one + const openPage = pages.find(p => !p.isClosed()) + if (openPage) { + this.bot.log(this.bot.isMobile, 'GET-NEW-TAB', 'Latest tab was closed, using first available open tab') + return openPage + } + + // IMPROVED: Last resort - create new page + this.bot.log(this.bot.isMobile, 'GET-NEW-TAB', 'All tabs were closed, creating new page', 'warn') + const newPage = await browser.newPage() + await this.bot.utils.wait(500) + return newPage + } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error) - this.bot.log(this.bot.isMobile, 'GET-NEW-TAB', 'An error occurred: ' + errorMessage, 'error') - throw new Error('Get new tab failed: ' + errorMessage) + this.bot.log(this.bot.isMobile, 'GET-NEW-TAB', 'Critical error in getLatestTab: ' + errorMessage, 'error') + + // IMPROVED: Try one more time to create a new page as absolute last resort + try { + const browser = page.context() + this.bot.log(this.bot.isMobile, 'GET-NEW-TAB', 'Attempting recovery by creating new page', 'warn') + const recoveryPage = await browser.newPage() + await this.bot.utils.wait(500) + return recoveryPage + } catch (recoveryError) { + const recoveryMsg = recoveryError instanceof Error ? recoveryError.message : String(recoveryError) + this.bot.log(this.bot.isMobile, 'GET-NEW-TAB', 'Recovery failed: ' + recoveryMsg, 'error') + throw new Error('Get new tab failed and recovery unsuccessful: ' + errorMessage) + } } } diff --git a/src/flows/DesktopFlow.ts b/src/flows/DesktopFlow.ts index d244c29..2ecb817 100644 --- a/src/flows/DesktopFlow.ts +++ b/src/flows/DesktopFlow.ts @@ -124,7 +124,14 @@ export class DesktopFlow { // Do desktop searches if (this.bot.config.workers.doDesktopSearch) { - await this.bot.activities.doSearch(workerPage, data) + try { + await this.bot.activities.doSearch(workerPage, data) + } catch (searchError) { + const errorMsg = searchError instanceof Error ? searchError.message : String(searchError) + this.bot.log(false, 'DESKTOP-FLOW', `Desktop search failed: ${errorMsg}`, 'error') + // IMPROVED: Don't throw - continue with other tasks, just log the error + // User will see reduced points but flow completes + } } // Fetch points BEFORE closing (avoid page closed reload error) diff --git a/src/flows/MobileFlow.ts b/src/flows/MobileFlow.ts index 39b6746..d935aa9 100644 --- a/src/flows/MobileFlow.ts +++ b/src/flows/MobileFlow.ts @@ -136,7 +136,14 @@ export class MobileFlow { // Go to homepage on worker page await this.bot.browser.func.goHome(workerPage) - await this.bot.activities.doSearch(workerPage, data) + // IMPROVED: Add error handling for mobile search to prevent flow termination + try { + await this.bot.activities.doSearch(workerPage, data) + } catch (searchError) { + const errorMsg = searchError instanceof Error ? searchError.message : String(searchError) + this.bot.log(true, 'MOBILE-FLOW', `Mobile search failed: ${errorMsg}`, 'error') + // Continue execution - let retry logic handle it below + } // Fetch current search points const mobileSearchPoints = (await this.bot.browser.func.getSearchPoints()).mobileSearch?.[0] diff --git a/src/functions/activities/Search.ts b/src/functions/activities/Search.ts index aa1c45f..74335ae 100644 --- a/src/functions/activities/Search.ts +++ b/src/functions/activities/Search.ts @@ -28,7 +28,14 @@ export class Search extends Workers { public async doSearch(page: Page, data: DashboardData) { this.bot.log(this.bot.isMobile, 'SEARCH-BING', 'Starting Bing searches') - page = await this.bot.browser.utils.getLatestTab(page) + // IMPROVED: Add error handling for getLatestTab to prevent early flow failure + try { + page = await this.bot.browser.utils.getLatestTab(page) + } catch (error) { + const errorMsg = error instanceof Error ? error.message : String(error) + this.bot.log(this.bot.isMobile, 'SEARCH-BING', `Failed to get latest tab: ${errorMsg}`, 'error') + throw new Error(`Cannot start search - tab retrieval failed: ${errorMsg}`) + } let searchCounters: Counters = await this.bot.browser.func.getSearchPoints() let missingPoints = this.calculatePoints(searchCounters) diff --git a/src/util/notifications/ErrorReportingWebhook.ts b/src/util/notifications/ErrorReportingWebhook.ts index f1a1470..8e237ae 100644 --- a/src/util/notifications/ErrorReportingWebhook.ts +++ b/src/util/notifications/ErrorReportingWebhook.ts @@ -1,4 +1,6 @@ import axios from 'axios' +import fs from 'fs' +import path from 'path' import { DISCORD } from '../../constants' import { Config } from '../../interface/Config' @@ -188,40 +190,49 @@ export async function sendErrorReport( Object.assign(payload.context, sanitizedContext) } - // Build Discord embed + // Build Discord embed with improved formatting const embed = { title: '🐛 Automatic Error Report', - description: `\`\`\`\n${sanitizedMessage.slice(0, 500)}\n\`\`\``, + description: `\`\`\`js\n${sanitizedMessage.slice(0, 700)}\n\`\`\``, color: DISCORD.COLOR_RED, fields: [ { name: '📦 Version', - value: payload.context.version, + value: payload.context.version === 'unknown' ? '⚠️ Unknown (check package.json)' : `v${payload.context.version}`, inline: true }, { name: '💻 Platform', - value: `${payload.context.platform} (${payload.context.arch})`, + value: `${payload.context.platform} ${payload.context.arch}`, inline: true }, { name: '⚙️ Node.js', value: payload.context.nodeVersion, inline: true + }, + { + name: '🕐 Timestamp', + value: new Date(payload.context.timestamp).toLocaleString('en-US', { timeZone: 'UTC', timeZoneName: 'short' }), + inline: false } ], timestamp: payload.context.timestamp, footer: { - text: 'Automatic error reporting - Thank you for contributing!', + text: 'Automatic error reporting • Non-sensitive data only', icon_url: DISCORD.AVATAR_URL } } - // Add stack trace field if available (truncated) + // Add stack trace field if available (truncated to fit Discord limits) if (sanitizedStack) { + // Limit to 900 chars to leave room for backticks and formatting + const truncated = sanitizedStack.slice(0, 900) + const wasTruncated = sanitizedStack.length > 900 + embed.fields.push({ - name: '📋 Stack Trace (truncated)', - value: `\`\`\`\n${sanitizedStack.slice(0, 800)}\n\`\`\``, + name: '📋 Stack Trace' + (wasTruncated ? ' (truncated for display)' : ''), + value: `\`\`\`js\n${truncated}${wasTruncated ? '\n... (see full trace in logs)' : ''}\n\`\`\``, inline: false }) } @@ -262,13 +273,33 @@ export async function sendErrorReport( /** * Get project version from package.json + * FIXED: Use path.join to correctly resolve package.json location in both dev and production */ function getProjectVersion(): string { try { - // Dynamic import for package.json - // eslint-disable-next-line @typescript-eslint/no-var-requires - const packageJson = require('../../package.json') as { version?: string } - return packageJson.version || 'unknown' + // Try multiple possible paths (dev and compiled) + const possiblePaths = [ + path.join(__dirname, '../../../package.json'), // From dist/util/notifications/ + path.join(__dirname, '../../package.json'), // From src/util/notifications/ + path.join(process.cwd(), 'package.json') // From project root + ] + + for (const pkgPath of possiblePaths) { + try { + if (fs.existsSync(pkgPath)) { + const raw = fs.readFileSync(pkgPath, 'utf-8') + const pkg = JSON.parse(raw) as { version?: string } + if (pkg.version) { + return pkg.version + } + } + } catch { + // Try next path + continue + } + } + + return 'unknown' } catch { return 'unknown' }