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'
|
const envForceHeadless = process.env.FORCE_HEADLESS === '1'
|
||||||
let headless = envForceHeadless ? true : (this.bot.config.browser?.headless ?? false)
|
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) {
|
if (headless !== false) {
|
||||||
const target = this.bot.getBuyModeTarget()
|
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
|
headless = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const engineName = 'chromium'
|
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 proxyConfig = this.buildPlaywrightProxy(proxy)
|
||||||
|
|
||||||
const isLinux = process.platform === 'linux'
|
const isLinux = process.platform === 'linux'
|
||||||
|
|
||||||
|
// Base arguments for stability
|
||||||
const baseArgs = [
|
const baseArgs = [
|
||||||
'--no-sandbox',
|
'--no-sandbox',
|
||||||
'--mute-audio',
|
'--mute-audio',
|
||||||
@@ -52,7 +55,7 @@ class Browser {
|
|||||||
'--ignore-ssl-errors'
|
'--ignore-ssl-errors'
|
||||||
]
|
]
|
||||||
|
|
||||||
// Linux stability fixes without detection risk
|
// Linux stability fixes
|
||||||
const linuxStabilityArgs = isLinux ? [
|
const linuxStabilityArgs = isLinux ? [
|
||||||
'--disable-dev-shm-usage',
|
'--disable-dev-shm-usage',
|
||||||
'--disable-software-rasterizer',
|
'--disable-software-rasterizer',
|
||||||
@@ -60,10 +63,26 @@ class Browser {
|
|||||||
'--disk-cache-size=1'
|
'--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({
|
browser = await playwright.chromium.launch({
|
||||||
headless,
|
headless,
|
||||||
...(proxyConfig && { proxy: proxyConfig }),
|
...(proxyConfig && { proxy: proxyConfig }),
|
||||||
args: [...baseArgs, ...linuxStabilityArgs],
|
args: [...baseArgs, ...linuxStabilityArgs, ...stealthArgs],
|
||||||
timeout: isLinux ? 90000 : 60000
|
timeout: isLinux ? 90000 : 60000
|
||||||
})
|
})
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
@@ -87,6 +106,8 @@ class Browser {
|
|||||||
const globalTimeout = this.bot.config.browser?.globalTimeout ?? 30000
|
const globalTimeout = this.bot.config.browser?.globalTimeout ?? 30000
|
||||||
context.setDefaultTimeout(typeof globalTimeout === 'number' ? globalTimeout : this.bot.utils.stringToMs(globalTimeout))
|
context.setDefaultTimeout(typeof globalTimeout === 'number' ? globalTimeout : this.bot.utils.stringToMs(globalTimeout))
|
||||||
|
|
||||||
|
const isBuyMode = this.bot.isBuyModeEnabled()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
context.on('page', async (page) => {
|
context.on('page', async (page) => {
|
||||||
try {
|
try {
|
||||||
@@ -96,6 +117,7 @@ class Browser {
|
|||||||
|
|
||||||
await page.setViewportSize(viewport)
|
await page.setViewportSize(viewport)
|
||||||
|
|
||||||
|
// Standard styling
|
||||||
await page.addInitScript(() => {
|
await page.addInitScript(() => {
|
||||||
try {
|
try {
|
||||||
const style = document.createElement('style')
|
const style = document.createElement('style')
|
||||||
@@ -109,6 +131,63 @@ class Browser {
|
|||||||
document.documentElement.appendChild(style)
|
document.documentElement.appendChild(style)
|
||||||
} catch {/* ignore */}
|
} 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) {
|
} catch (e) {
|
||||||
this.bot.log(this.bot.isMobile, 'BROWSER', `Page setup warning: ${e instanceof Error ? e.message : String(e)}`, 'warn')
|
this.bot.log(this.bot.isMobile, 'BROWSER', `Page setup warning: ${e instanceof Error ? e.message : String(e)}`, 'warn')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
},
|
},
|
||||||
"jobState": {
|
"jobState": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"dir": ""
|
"dir": "",
|
||||||
|
"autoResetOnComplete": true // Set to true to automatically rerun all accounts without prompting (for scheduled tasks)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Tasks
|
// Tasks
|
||||||
|
|||||||
24
src/index.ts
24
src/index.ts
@@ -171,9 +171,27 @@ export class MicrosoftRewardsBot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async promptResetJobState(): Promise<boolean> {
|
private async promptResetJobState(): Promise<boolean> {
|
||||||
// Skip prompt in non-interactive environments (Docker, CI, scheduled tasks)
|
// Check if auto-reset is enabled in config (for scheduled tasks)
|
||||||
if (!process.stdin.isTTY) {
|
if (this.config.jobState?.autoResetOnComplete === true) {
|
||||||
log('main','TASK','Non-interactive environment detected - keeping job state', 'warn')
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ export interface ConfigJobState {
|
|||||||
enabled?: boolean; // default true
|
enabled?: boolean; // default true
|
||||||
dir?: string; // base directory; defaults to <sessionPath>/job-state
|
dir?: string; // base directory; defaults to <sessionPath>/job-state
|
||||||
skipCompletedAccounts?: boolean; // if true (default), skip accounts already completed for the day
|
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
|
// Live logging configuration
|
||||||
|
|||||||
Reference in New Issue
Block a user