mirror of
https://github.com/LightZirconite/Microsoft-Rewards-Bot.git
synced 2026-01-10 01:06:17 +00:00
Improved interactive mode: added an automatic job reset option for scheduled tasks and improved stealth mode to bypass CAPTCHAs.
This commit is contained in:
@@ -29,20 +29,23 @@ class Browser {
|
||||
const envForceHeadless = process.env.FORCE_HEADLESS === '1'
|
||||
let headless = envForceHeadless ? true : (this.bot.config.browser?.headless ?? false)
|
||||
|
||||
if (this.bot.isBuyModeEnabled() && !envForceHeadless) {
|
||||
// Buy/Interactive mode: always visible and with enhanced stealth
|
||||
const isBuyMode = this.bot.isBuyModeEnabled()
|
||||
if (isBuyMode && !envForceHeadless) {
|
||||
if (headless !== false) {
|
||||
const target = this.bot.getBuyModeTarget()
|
||||
this.bot.log(this.bot.isMobile, 'BROWSER', `Buy mode: forcing headless=false${target ? ` for ${target}` : ''}`, 'warn')
|
||||
this.bot.log(this.bot.isMobile, 'BROWSER', `Interactive mode: forcing headless=false${target ? ` for ${target}` : ''}`, 'warn')
|
||||
}
|
||||
headless = false
|
||||
}
|
||||
|
||||
const engineName = 'chromium'
|
||||
this.bot.log(this.bot.isMobile, 'BROWSER', `Launching ${engineName} (headless=${headless})`)
|
||||
this.bot.log(this.bot.isMobile, 'BROWSER', `Launching ${engineName} (headless=${headless}${isBuyMode ? ', stealth-mode=ENHANCED' : ''})`)
|
||||
const proxyConfig = this.buildPlaywrightProxy(proxy)
|
||||
|
||||
const isLinux = process.platform === 'linux'
|
||||
|
||||
// Base arguments for stability
|
||||
const baseArgs = [
|
||||
'--no-sandbox',
|
||||
'--mute-audio',
|
||||
@@ -52,7 +55,7 @@ class Browser {
|
||||
'--ignore-ssl-errors'
|
||||
]
|
||||
|
||||
// Linux stability fixes without detection risk
|
||||
// Linux stability fixes
|
||||
const linuxStabilityArgs = isLinux ? [
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-software-rasterizer',
|
||||
@@ -60,10 +63,26 @@ class Browser {
|
||||
'--disk-cache-size=1'
|
||||
] : []
|
||||
|
||||
// ENHANCED STEALTH MODE for Buy/Interactive Mode
|
||||
// These arguments help bypass CAPTCHA and automation detection
|
||||
const stealthArgs = isBuyMode ? [
|
||||
'--disable-blink-features=AutomationControlled', // Critical: Hide automation
|
||||
'--disable-features=IsolateOrigins,site-per-process', // Reduce detection surface
|
||||
'--disable-site-isolation-trials',
|
||||
'--disable-web-security', // Allow cross-origin (may help with CAPTCHA)
|
||||
'--disable-features=VizDisplayCompositor', // Reduce GPU fingerprinting
|
||||
'--no-first-run',
|
||||
'--no-default-browser-check',
|
||||
'--disable-infobars',
|
||||
'--window-position=0,0',
|
||||
'--window-size=1920,1080', // Consistent window size
|
||||
'--start-maximized'
|
||||
] : []
|
||||
|
||||
browser = await playwright.chromium.launch({
|
||||
headless,
|
||||
...(proxyConfig && { proxy: proxyConfig }),
|
||||
args: [...baseArgs, ...linuxStabilityArgs],
|
||||
args: [...baseArgs, ...linuxStabilityArgs, ...stealthArgs],
|
||||
timeout: isLinux ? 90000 : 60000
|
||||
})
|
||||
} catch (e: unknown) {
|
||||
@@ -87,6 +106,8 @@ class Browser {
|
||||
const globalTimeout = this.bot.config.browser?.globalTimeout ?? 30000
|
||||
context.setDefaultTimeout(typeof globalTimeout === 'number' ? globalTimeout : this.bot.utils.stringToMs(globalTimeout))
|
||||
|
||||
const isBuyMode = this.bot.isBuyModeEnabled()
|
||||
|
||||
try {
|
||||
context.on('page', async (page) => {
|
||||
try {
|
||||
@@ -96,6 +117,7 @@ class Browser {
|
||||
|
||||
await page.setViewportSize(viewport)
|
||||
|
||||
// Standard styling
|
||||
await page.addInitScript(() => {
|
||||
try {
|
||||
const style = document.createElement('style')
|
||||
@@ -109,6 +131,63 @@ class Browser {
|
||||
document.documentElement.appendChild(style)
|
||||
} catch {/* ignore */}
|
||||
})
|
||||
|
||||
// ENHANCED ANTI-DETECTION for Buy/Interactive Mode
|
||||
if (isBuyMode) {
|
||||
await page.addInitScript(`
|
||||
// Override navigator.webdriver (critical for CAPTCHA bypass)
|
||||
Object.defineProperty(Object.getPrototypeOf(navigator), 'webdriver', {
|
||||
get: () => false
|
||||
});
|
||||
|
||||
// Add chrome runtime (looks more human)
|
||||
Object.defineProperty(window, 'chrome', {
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
value: { runtime: {} }
|
||||
});
|
||||
|
||||
// Add plugins (looks more human)
|
||||
Object.defineProperty(navigator, 'plugins', {
|
||||
get: () => [
|
||||
{ name: 'Chrome PDF Plugin' },
|
||||
{ name: 'Chrome PDF Viewer' },
|
||||
{ name: 'Native Client' }
|
||||
]
|
||||
});
|
||||
|
||||
// Languages
|
||||
Object.defineProperty(navigator, 'languages', {
|
||||
get: () => ['en-US', 'en', 'fr']
|
||||
});
|
||||
|
||||
// Hide automation markers
|
||||
['__nightmare', '__playwright', '__pw_manual', '__webdriver_script_fn', 'webdriver'].forEach(prop => {
|
||||
try {
|
||||
if (prop in window) delete window[prop];
|
||||
} catch {}
|
||||
});
|
||||
|
||||
// Override permissions to avoid detection
|
||||
const originalPermissionsQuery = window.navigator.permissions.query;
|
||||
window.navigator.permissions.query = function(params) {
|
||||
if (params.name === 'notifications') {
|
||||
return Promise.resolve({
|
||||
state: Notification.permission,
|
||||
name: 'notifications',
|
||||
onchange: null,
|
||||
addEventListener: () => {},
|
||||
removeEventListener: () => {},
|
||||
dispatchEvent: () => true
|
||||
});
|
||||
}
|
||||
return originalPermissionsQuery.call(this, params);
|
||||
};
|
||||
`)
|
||||
|
||||
this.bot.log(this.bot.isMobile, 'BROWSER', '🛡️ Enhanced stealth mode activated (anti-CAPTCHA)', 'log', 'green')
|
||||
}
|
||||
} catch (e) {
|
||||
this.bot.log(this.bot.isMobile, 'BROWSER', `Page setup warning: ${e instanceof Error ? e.message : String(e)}`, 'warn')
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
},
|
||||
"jobState": {
|
||||
"enabled": true,
|
||||
"dir": ""
|
||||
"dir": "",
|
||||
"autoResetOnComplete": true // Set to true to automatically rerun all accounts without prompting (for scheduled tasks)
|
||||
},
|
||||
|
||||
// Tasks
|
||||
|
||||
24
src/index.ts
24
src/index.ts
@@ -171,9 +171,27 @@ export class MicrosoftRewardsBot {
|
||||
}
|
||||
|
||||
private async promptResetJobState(): Promise<boolean> {
|
||||
// Skip prompt in non-interactive environments (Docker, CI, scheduled tasks)
|
||||
if (!process.stdin.isTTY) {
|
||||
log('main','TASK','Non-interactive environment detected - keeping job state', 'warn')
|
||||
// Check if auto-reset is enabled in config (for scheduled tasks)
|
||||
if (this.config.jobState?.autoResetOnComplete === true) {
|
||||
log('main','TASK','Auto-reset enabled (jobState.autoResetOnComplete=true) - resetting and rerunning all accounts', 'log', 'green')
|
||||
return true
|
||||
}
|
||||
|
||||
// Check environment variable override
|
||||
const envAutoReset = process.env.REWARDS_AUTO_RESET_JOBSTATE
|
||||
if (envAutoReset === '1' || envAutoReset?.toLowerCase() === 'true') {
|
||||
log('main','TASK','Auto-reset enabled (REWARDS_AUTO_RESET_JOBSTATE) - resetting and rerunning all accounts', 'log', 'green')
|
||||
return true
|
||||
}
|
||||
|
||||
// Detect non-interactive environments more reliably
|
||||
const isNonInteractive = !process.stdin.isTTY ||
|
||||
process.env.CI === 'true' ||
|
||||
process.env.DOCKER === 'true' ||
|
||||
process.env.SCHEDULED_TASK === 'true'
|
||||
|
||||
if (isNonInteractive) {
|
||||
log('main','TASK','Non-interactive environment detected - keeping job state (set jobState.autoResetOnComplete=true to auto-rerun)', 'warn')
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,7 @@ export interface ConfigJobState {
|
||||
enabled?: boolean; // default true
|
||||
dir?: string; // base directory; defaults to <sessionPath>/job-state
|
||||
skipCompletedAccounts?: boolean; // if true (default), skip accounts already completed for the day
|
||||
autoResetOnComplete?: boolean; // if true, automatically reset and rerun without prompting (useful for scheduled tasks)
|
||||
}
|
||||
|
||||
// Live logging configuration
|
||||
|
||||
Reference in New Issue
Block a user