diff --git a/src/browser/Browser.ts b/src/browser/Browser.ts index 9144bdd..3797805 100644 --- a/src/browser/Browser.ts +++ b/src/browser/Browser.ts @@ -21,7 +21,9 @@ class Browser { // Dynamically import child_process to avoid overhead otherwise const { execSync } = await import('child_process') execSync('npx playwright install chromium', { stdio: 'ignore' }) - } catch { /* silent */ } + } catch (e) { + this.bot.log(this.bot.isMobile, 'BROWSER', `Auto-install failed: ${e instanceof Error ? e.message : String(e)}`, 'warn') + } } let browser: import('rebrowser-playwright').Browser @@ -114,11 +116,18 @@ class Browser { } ` document.documentElement.appendChild(style) - } catch { /* ignore */ } + } catch (e) { + // Style injection failed - not critical, page will still function + } }) - } catch { /* ignore */ } + } catch (e) { + // Viewport/script setup failed - log for debugging but continue + this.bot.log(this.bot.isMobile, 'BROWSER', `Page setup warning: ${e instanceof Error ? e.message : String(e)}`, 'warn') + } }) - } catch { /* ignore */ } + } catch (e) { + this.bot.log(this.bot.isMobile, 'BROWSER', `Context event handler setup warning: ${e instanceof Error ? e.message : String(e)}`, 'warn') + } await context.addCookies(sessionData.cookies) diff --git a/src/index.ts b/src/index.ts index 4167f89..57f14df 100644 --- a/src/index.ts +++ b/src/index.ts @@ -300,7 +300,9 @@ export class MicrosoftRewardsBot { this.log(false, 'BUY-MODE', 'Monitor tab was closed; reopening in background...', 'warn') await recreateMonitor() } - } catch { /* ignore */ } + } catch (e) { + this.log(false, 'BUY-MODE', `Failed to check/recreate monitor tab: ${e instanceof Error ? e.message : String(e)}`, 'warn') + } try { const data = await this.browser.func.getDashboardData(monitor) @@ -321,9 +323,14 @@ export class MicrosoftRewardsBot { const msg = err instanceof Error ? err.message : String(err) if (/Target closed|page has been closed|browser has been closed/i.test(msg)) { this.log(false, 'BUY-MODE', 'Monitor page closed or lost; recreating...', 'warn') - try { await recreateMonitor() } catch { /* ignore */ } + try { + await recreateMonitor() + } catch (e) { + this.log(false, 'BUY-MODE', `Failed to recreate monitor: ${e instanceof Error ? e.message : String(e)}`, 'warn') + } + } else { + this.log(false, 'BUY-MODE', `Dashboard check error: ${msg}`, 'warn') } - // Swallow other errors to avoid disrupting the user } } @@ -333,7 +340,11 @@ export class MicrosoftRewardsBot { } catch (e) { log(false, 'BUY-MODE', `Failed to save session: ${e instanceof Error ? e.message : String(e)}`, 'warn') } - try { if (!monitor.isClosed()) await monitor.close() } catch {/* ignore */} + try { + if (!monitor.isClosed()) await monitor.close() + } catch (e) { + log(false, 'BUY-MODE', `Failed to close monitor tab: ${e instanceof Error ? e.message : String(e)}`, 'warn') + } // Send a final minimal conclusion webhook for this manual session const summary: AccountSummary = { @@ -351,7 +362,7 @@ export class MicrosoftRewardsBot { this.log(false, 'BUY-MODE', 'Buy mode session finished (monitoring period ended). You can close the browser when done.') } catch (e) { - this.log(false, 'BUY-MODE', `Error in buy mode: ${e instanceof Error ? e.message : e}`, 'error') + this.log(false, 'BUY-MODE', `Error in buy mode: ${e instanceof Error ? e.message : String(e)}`, 'error') } } @@ -506,10 +517,14 @@ export class MicrosoftRewardsBot { (async () => { try { await this.sendConclusion(this.accountSummaries) - } catch {/* ignore */} + } catch (e) { + log('main', 'CONCLUSION', `Failed to send conclusion: ${e instanceof Error ? e.message : String(e)}`, 'warn') + } try { await this.runAutoUpdate() - } catch {/* ignore */} + } catch (e) { + log('main', 'UPDATE', `Auto-update failed: ${e instanceof Error ? e.message : String(e)}`, 'warn') + } log('main', 'MAIN-WORKER', 'All workers destroyed. Exiting main process!', 'warn') process.exit(0) })() @@ -806,7 +821,9 @@ export class MicrosoftRewardsBot { // Single process mode -> build and send conclusion directly await this.sendConclusion(this.accountSummaries) // After conclusion, run optional auto-update - await this.runAutoUpdate().catch(() => {/* ignore update errors */}) + await this.runAutoUpdate().catch((e) => { + log('main', 'UPDATE', `Auto-update failed: ${e instanceof Error ? e.message : String(e)}`, 'warn') + }) } process.exit() } diff --git a/src/luxon.d.ts b/src/luxon.d.ts deleted file mode 100644 index eb6a2d6..0000000 --- a/src/luxon.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* Minimal ambient declarations to unblock TypeScript when @types/luxon is absent. */ -declare module 'luxon' { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - export const DateTime: any - // eslint-disable-next-line @typescript-eslint/no-explicit-any - export const IANAZone: any -} diff --git a/src/util/Humanizer.ts b/src/util/Humanizer.ts index 17f93c9..98e85c0 100644 --- a/src/util/Humanizer.ts +++ b/src/util/Humanizer.ts @@ -19,13 +19,19 @@ export class Humanizer { if (Math.random() < moveProb) { const x = Math.floor(Math.random() * 40) + 5 const y = Math.floor(Math.random() * 30) + 5 - await page.mouse.move(x, y, { steps: 2 }).catch(() => {}) + await page.mouse.move(x, y, { steps: 2 }).catch(() => { + // Mouse move failed - page may be closed or unavailable + }) } if (Math.random() < scrollProb) { const dy = (Math.random() < 0.5 ? 1 : -1) * (Math.floor(Math.random() * 150) + 50) - await page.mouse.wheel(0, dy).catch(() => {}) + await page.mouse.wheel(0, dy).catch(() => { + // Mouse wheel failed - page may be closed or unavailable + }) } - } catch {/* noop */} + } catch { + // Gesture execution failed - not critical for operation + } } async actionPause(): Promise { @@ -40,7 +46,10 @@ export class Humanizer { try { const n = this.util.stringToMs(String(v)) return Math.max(0, Math.min(n, 10_000)) - } catch { return defMin } + } catch (e) { + // Parse failed - use default minimum + return defMin + } } min = parse(this.cfg.actionDelay.min) max = parse(this.cfg.actionDelay.max) diff --git a/src/util/Load.ts b/src/util/Load.ts index 98f5567..816aa7a 100644 --- a/src/util/Load.ts +++ b/src/util/Load.ts @@ -260,7 +260,15 @@ export function loadAccounts(): Account[] { ] let chosen: string | null = null for (const p of candidates) { - try { if (fs.existsSync(p)) { chosen = p; break } } catch { /* ignore */ } + try { + if (fs.existsSync(p)) { + chosen = p + break + } + } catch (e) { + // Filesystem check failed for this path, try next + continue + } } if (!chosen) throw new Error(`accounts file not found in: ${candidates.join(' | ')}`) raw = fs.readFileSync(chosen, 'utf-8') @@ -349,7 +357,15 @@ export function loadConfig(): Config { let cfgPath: string | null = null for (const p of candidates) { - try { if (fs.existsSync(p)) { cfgPath = p; break } } catch { /* ignore */ } + try { + if (fs.existsSync(p)) { + cfgPath = p + break + } + } catch (e) { + // Filesystem check failed for this path, try next + continue + } } if (!cfgPath) throw new Error(`config.json not found in: ${candidates.join(' | ')}`) const config = fs.readFileSync(cfgPath, 'utf-8') @@ -377,6 +393,8 @@ export async function loadSessionData(sessionPath: string, email: string, isMobi } // Fetch fingerprint file (support both legacy typo "fingerpint" and corrected "fingerprint") + // NOTE: "fingerpint" is a historical typo that must be maintained for backwards compatibility + // with existing session files. We check for the corrected name first, then fall back to the typo. const baseDir = path.join(__dirname, '../browser/', sessionPath, email) const legacyFile = path.join(baseDir, `${isMobile ? 'mobile_fingerpint' : 'desktop_fingerpint'}.json`) const correctFile = path.join(baseDir, `${isMobile ? 'mobile_fingerprint' : 'desktop_fingerprint'}.json`) @@ -436,11 +454,18 @@ export async function saveFingerprintData(sessionPath: string, email: string, is } // Save fingerprint to files (write both legacy and corrected names for compatibility) + // NOTE: Writing to both "fingerpint" (typo) and "fingerprint" (correct) ensures backwards + // compatibility with older bot versions that expect the typo filename. const legacy = path.join(sessionDir, `${isMobile ? 'mobile_fingerpint' : 'desktop_fingerpint'}.json`) const correct = path.join(sessionDir, `${isMobile ? 'mobile_fingerprint' : 'desktop_fingerprint'}.json`) const payload = JSON.stringify(fingerprint) await fs.promises.writeFile(correct, payload) - try { await fs.promises.writeFile(legacy, payload) } catch { /* ignore */ } + try { + await fs.promises.writeFile(legacy, payload) + } catch (e) { + // Legacy file write failed - not critical since correct file was written + // Silently continue to maintain compatibility + } return sessionDir } catch (error) { diff --git a/tsconfig.json b/tsconfig.json index 69ab7f5..3463be8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,72 +1,44 @@ { "compilerOptions": { /* Visit https://aka.ms/tsconfig.json to read more about this file */ + /* Basic Options */ - "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ - // "lib": [], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ - "declaration": true, /* Generates corresponding '.d.ts' file. */ - "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./dist", /* Redirect output structure to the directory. */ - "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + "target": "ES2020", + "module": "commonjs", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist", + "rootDir": "./src", + /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - "strictNullChecks": true, /* Enable strict null checks. */ - "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "noImplicitThis": true, + "alwaysStrict": true, + /* Additional Checks */ - "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + "noUnusedLocals": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + /* Module Resolution Options */ - "moduleResolution":"node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - "types": ["node"], - // Keep explicit typeRoots to ensure resolution in environments that don't auto-detect before full install. - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + "moduleResolution": "node", + "types": ["node"], + "esModuleInterop": true, + /* Advanced Options */ - "skipLibCheck": true, /* Skip type checking of declaration files. */ - "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, "resolveJsonModule": true }, "include": [ - "src/**/*.ts", - "src/**/*.d.ts", + "src/**/*.ts", + "src/**/*.d.ts", "src/functions/queries.json" ], "exclude": [