mirror of
https://github.com/LightZirconite/Microsoft-Rewards-Bot.git
synced 2026-01-11 09:46:16 +00:00
Fix: Enhance error handling and timeout management across various modules; improve validation and documentation
This commit is contained in:
@@ -2,18 +2,18 @@ import { Page } from 'rebrowser-playwright'
|
||||
|
||||
import { MicrosoftRewardsBot } from '../index'
|
||||
|
||||
import { Search } from './activities/Search'
|
||||
import { ABC } from './activities/ABC'
|
||||
import { DailyCheckIn } from './activities/DailyCheckIn'
|
||||
import { Poll } from './activities/Poll'
|
||||
import { Quiz } from './activities/Quiz'
|
||||
import { ReadToEarn } from './activities/ReadToEarn'
|
||||
import { Search } from './activities/Search'
|
||||
import { SearchOnBing } from './activities/SearchOnBing'
|
||||
import { ThisOrThat } from './activities/ThisOrThat'
|
||||
import { UrlReward } from './activities/UrlReward'
|
||||
import { SearchOnBing } from './activities/SearchOnBing'
|
||||
import { ReadToEarn } from './activities/ReadToEarn'
|
||||
import { DailyCheckIn } from './activities/DailyCheckIn'
|
||||
|
||||
import { DashboardData, MorePromotion, PromotionalItem } from '../interface/DashboardData'
|
||||
import type { ActivityHandler } from '../interface/ActivityHandler'
|
||||
import { DashboardData, MorePromotion, PromotionalItem } from '../interface/DashboardData'
|
||||
|
||||
type ActivityKind =
|
||||
| { type: 'poll' }
|
||||
@@ -73,9 +73,14 @@ export class Activities {
|
||||
case 'urlReward':
|
||||
await this.doUrlReward(page)
|
||||
break
|
||||
default:
|
||||
case 'unsupported':
|
||||
// FIXED: Added explicit default case
|
||||
this.bot.log(this.bot.isMobile, 'ACTIVITY', `Skipped activity "${activity.title}" | Reason: Unsupported type: "${String((activity as { promotionType?: string }).promotionType)}"!`, 'warn')
|
||||
break
|
||||
default:
|
||||
// Exhaustiveness check - should never reach here due to ActivityKind type
|
||||
this.bot.log(this.bot.isMobile, 'ACTIVITY', `Unexpected activity kind for "${activity.title}"`, 'error')
|
||||
break
|
||||
}
|
||||
} catch (e) {
|
||||
this.bot.log(this.bot.isMobile, 'ACTIVITY', `Dispatcher error for "${activity.title}": ${e instanceof Error ? e.message : e}`, 'error')
|
||||
|
||||
@@ -42,7 +42,8 @@ const LOGIN_TARGET = { host: 'rewards.bing.com', path: '/' }
|
||||
const DEFAULT_TIMEOUTS = {
|
||||
loginMaxMs: (() => {
|
||||
const val = Number(process.env.LOGIN_MAX_WAIT_MS || 180000)
|
||||
return (isNaN(val) || val < 10000 || val > 600000) ? 180000 : val
|
||||
// IMPROVED: Use isFinite instead of isNaN for consistency
|
||||
return (!Number.isFinite(val) || val < 10000 || val > 600000) ? 180000 : val
|
||||
})(),
|
||||
short: 200,
|
||||
medium: 800,
|
||||
@@ -51,7 +52,7 @@ const DEFAULT_TIMEOUTS = {
|
||||
portalWaitMs: 15000,
|
||||
elementCheck: 100,
|
||||
fastPoll: 500
|
||||
}
|
||||
} as const
|
||||
|
||||
// Security pattern bundle
|
||||
const SIGN_IN_BLOCK_PATTERNS: { re: RegExp; label: string }[] = [
|
||||
@@ -739,16 +740,18 @@ export class Login {
|
||||
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
|
||||
|
||||
try {
|
||||
// IMPROVED: Add 120s timeout to prevent infinite blocking
|
||||
// FIXED: Add 120s timeout with proper cleanup to prevent memory leak
|
||||
let timeoutHandle: NodeJS.Timeout | undefined
|
||||
const code = await Promise.race([
|
||||
new Promise<string>(res => {
|
||||
rl.question('Enter 2FA code:\n', ans => {
|
||||
if (timeoutHandle) clearTimeout(timeoutHandle)
|
||||
rl.close()
|
||||
res(ans.trim())
|
||||
})
|
||||
}),
|
||||
new Promise<string>((_, reject) => {
|
||||
setTimeout(() => {
|
||||
timeoutHandle = setTimeout(() => {
|
||||
rl.close()
|
||||
reject(new Error('2FA code input timeout after 120s'))
|
||||
}, 120000)
|
||||
@@ -1677,7 +1680,11 @@ export class Login {
|
||||
}
|
||||
|
||||
private startCompromisedInterval() {
|
||||
if (this.compromisedInterval) clearInterval(this.compromisedInterval)
|
||||
// FIXED: Always cleanup existing interval before creating new one
|
||||
if (this.compromisedInterval) {
|
||||
clearInterval(this.compromisedInterval)
|
||||
this.compromisedInterval = undefined
|
||||
}
|
||||
this.compromisedInterval = setInterval(()=>{
|
||||
try {
|
||||
this.bot.log(this.bot.isMobile,'SECURITY','Security standby active. Manual review required before proceeding.','warn')
|
||||
|
||||
@@ -237,11 +237,22 @@ export class Workers {
|
||||
await page.click(selector, { timeout: 10000 })
|
||||
page = await this.bot.browser.utils.getLatestTab(page)
|
||||
|
||||
// FIXED: Use AbortController for proper cancellation instead of race condition
|
||||
const timeoutMs = this.bot.utils.stringToMs(this.bot.config?.globalTimeout ?? '30s') * 2
|
||||
const runWithTimeout = (p: Promise<void>) => Promise.race([
|
||||
p,
|
||||
new Promise<void>((_, rej) => setTimeout(() => rej(new Error('activity-timeout')), timeoutMs))
|
||||
])
|
||||
const controller = new AbortController()
|
||||
const timeoutHandle = setTimeout(() => {
|
||||
controller.abort()
|
||||
}, timeoutMs)
|
||||
|
||||
const runWithTimeout = async (p: Promise<void>) => {
|
||||
try {
|
||||
await p
|
||||
clearTimeout(timeoutHandle)
|
||||
} catch (error) {
|
||||
clearTimeout(timeoutHandle)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
await retry.run(async () => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user