mirror of
https://github.com/LightZirconite/Microsoft-Rewards-Bot.git
synced 2026-01-10 17:26:17 +00:00
feat: implement humanization enforcement and improve Axios request error handling
This commit is contained in:
@@ -228,6 +228,7 @@ export class Search extends Workers {
|
|||||||
// Try dismissing overlays before interacting
|
// Try dismissing overlays before interacting
|
||||||
await this.bot.browser.utils.tryDismissAllMessages(searchPage)
|
await this.bot.browser.utils.tryDismissAllMessages(searchPage)
|
||||||
await this.bot.utils.wait(200)
|
await this.bot.utils.wait(200)
|
||||||
|
await this.bot.browser.utils.humanizePage(searchPage)
|
||||||
|
|
||||||
let navigatedDirectly = false
|
let navigatedDirectly = false
|
||||||
try {
|
try {
|
||||||
@@ -311,7 +312,7 @@ export class Search extends Workers {
|
|||||||
data: `f.req=[[[i0OFE,"[null, null, \\"${geoLocale.toUpperCase()}\\", 0, null, 48]"]]]`
|
data: `f.req=[[[i0OFE,"[null, null, \\"${geoLocale.toUpperCase()}\\", 0, null, 48]"]]]`
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await this.bot.axios.request(request, this.bot.config.proxy.proxyGoogleTrends)
|
const response = await this.bot.axios.request(request, !this.bot.config.proxy.proxyGoogleTrends)
|
||||||
const rawText = response.data
|
const rawText = response.data
|
||||||
|
|
||||||
const trendsData = this.extractJsonFromResponse(rawText)
|
const trendsData = this.extractJsonFromResponse(rawText)
|
||||||
@@ -368,7 +369,7 @@ export class Search extends Workers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await this.bot.axios.request(request, this.bot.config.proxy.proxyBingTerms)
|
const response = await this.bot.axios.request(request, !this.bot.config.proxy.proxyBingTerms)
|
||||||
|
|
||||||
return response.data[1] as string[]
|
return response.data[1] as string[]
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
43
src/index.ts
43
src/index.ts
@@ -1,4 +1,5 @@
|
|||||||
import chalk from 'chalk'
|
import type { AxiosRequestConfig } from 'axios'
|
||||||
|
import chalk from 'chalk'
|
||||||
import { spawn } from 'child_process'
|
import { spawn } from 'child_process'
|
||||||
import type { Worker } from 'cluster'
|
import type { Worker } from 'cluster'
|
||||||
import cluster from 'cluster'
|
import cluster from 'cluster'
|
||||||
@@ -75,6 +76,7 @@ export class MicrosoftRewardsBot {
|
|||||||
this.accounts = []
|
this.accounts = []
|
||||||
this.utils = new Util()
|
this.utils = new Util()
|
||||||
this.config = loadConfig()
|
this.config = loadConfig()
|
||||||
|
this.enforceHumanization()
|
||||||
// JobState will be initialized in initialize() method after validation
|
// JobState will be initialized in initialize() method after validation
|
||||||
this.browser = {
|
this.browser = {
|
||||||
func: new BrowserFunc(this),
|
func: new BrowserFunc(this),
|
||||||
@@ -84,14 +86,6 @@ export class MicrosoftRewardsBot {
|
|||||||
this.workers = new Workers(this)
|
this.workers = new Workers(this)
|
||||||
this.humanizer = new Humanizer(this.utils, this.config.humanization)
|
this.humanizer = new Humanizer(this.utils, this.config.humanization)
|
||||||
this.activeWorkers = this.config.clusters
|
this.activeWorkers = this.config.clusters
|
||||||
|
|
||||||
if (this.config.queryDiversity?.enabled) {
|
|
||||||
this.queryEngine = new QueryDiversityEngine({
|
|
||||||
sources: this.config.queryDiversity.sources,
|
|
||||||
maxQueriesPerSource: this.config.queryDiversity.maxQueriesPerSource,
|
|
||||||
cacheMinutes: this.config.queryDiversity.cacheMinutes
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
@@ -199,6 +193,35 @@ export class MicrosoftRewardsBot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enforceHumanization(): void {
|
||||||
|
const allowDisable = process.env.ALLOW_HUMANIZATION_OFF === '1'
|
||||||
|
if (this.config?.humanization?.enabled === false && !allowDisable) {
|
||||||
|
log('main', 'HUMANIZATION', 'Humanization disabled in config; forcing it on for anti-detection safety (set ALLOW_HUMANIZATION_OFF=1 to override).', 'warn')
|
||||||
|
this.config.humanization = { ...this.config.humanization, enabled: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildQueryEngine(): QueryDiversityEngine | undefined {
|
||||||
|
if (!this.config.queryDiversity?.enabled) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const proxyHttpClient = {
|
||||||
|
request: (config: AxiosRequestConfig) => this.axios.request(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
const logger = (source: string, message: string, level: 'info' | 'warn' | 'error' = 'info') => {
|
||||||
|
const mapped = level === 'error' ? 'error' : level === 'warn' ? 'warn' : 'log'
|
||||||
|
this.log(this.isMobile, source, message, mapped)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new QueryDiversityEngine({
|
||||||
|
sources: this.config.queryDiversity.sources,
|
||||||
|
maxQueriesPerSource: this.config.queryDiversity.maxQueriesPerSource,
|
||||||
|
cacheMinutes: this.config.queryDiversity.cacheMinutes
|
||||||
|
}, logger, proxyHttpClient)
|
||||||
|
}
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
this.printBanner()
|
this.printBanner()
|
||||||
log('main', 'MAIN', `Bot started with ${this.config.clusters} clusters`)
|
log('main', 'MAIN', `Bot started with ${this.config.clusters} clusters`)
|
||||||
@@ -502,6 +525,7 @@ export class MicrosoftRewardsBot {
|
|||||||
const banned = { status: false, reason: '' }
|
const banned = { status: false, reason: '' }
|
||||||
|
|
||||||
this.axios = new Axios(account.proxy)
|
this.axios = new Axios(account.proxy)
|
||||||
|
this.queryEngine = this.buildQueryEngine()
|
||||||
const verbose = process.env.DEBUG_REWARDS_VERBOSE === '1'
|
const verbose = process.env.DEBUG_REWARDS_VERBOSE === '1'
|
||||||
|
|
||||||
if (this.config.dryRun) {
|
if (this.config.dryRun) {
|
||||||
@@ -525,6 +549,7 @@ export class MicrosoftRewardsBot {
|
|||||||
if (this.config.parallel) {
|
if (this.config.parallel) {
|
||||||
const mobileInstance = new MicrosoftRewardsBot(true)
|
const mobileInstance = new MicrosoftRewardsBot(true)
|
||||||
mobileInstance.axios = this.axios
|
mobileInstance.axios = this.axios
|
||||||
|
mobileInstance.queryEngine = this.queryEngine
|
||||||
|
|
||||||
// IMPROVED: Shared state to track desktop issues for early mobile abort consideration
|
// IMPROVED: Shared state to track desktop issues for early mobile abort consideration
|
||||||
let desktopDetectedIssue = false
|
let desktopDetectedIssue = false
|
||||||
|
|||||||
@@ -99,9 +99,8 @@ class AxiosClient {
|
|||||||
|
|
||||||
// Handle HTTP 407 Proxy Authentication Required
|
// Handle HTTP 407 Proxy Authentication Required
|
||||||
if (this.isProxyAuthError(err)) {
|
if (this.isProxyAuthError(err)) {
|
||||||
// Retry without proxy on auth failure
|
const msg = err instanceof Error ? err.message : String(err)
|
||||||
const bypassInstance = axios.create()
|
throw new Error(`Proxy authentication failed. Direct fallback disabled to avoid IP leakage. Details: ${msg}`)
|
||||||
return bypassInstance.request(config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle retryable network errors
|
// Handle retryable network errors
|
||||||
@@ -112,9 +111,6 @@ class AxiosClient {
|
|||||||
await this.sleep(delayMs)
|
await this.sleep(delayMs)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Last attempt: try without proxy
|
|
||||||
const bypassInstance = axios.create()
|
|
||||||
return bypassInstance.request(config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-retryable error
|
// Non-retryable error
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import axios from 'axios'
|
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||||
import { Util } from '../core/Utils'
|
import { Util } from '../core/Utils'
|
||||||
|
|
||||||
|
type HttpClient = Pick<typeof axios, 'request'> | { request: (config: AxiosRequestConfig) => Promise<AxiosResponse> }
|
||||||
|
|
||||||
export interface QueryDiversityConfig {
|
export interface QueryDiversityConfig {
|
||||||
sources: Array<'google-trends' | 'reddit' | 'news' | 'wikipedia' | 'local-fallback'>
|
sources: Array<'google-trends' | 'reddit' | 'news' | 'wikipedia' | 'local-fallback'>
|
||||||
deduplicate: boolean
|
deduplicate: boolean
|
||||||
@@ -18,8 +20,9 @@ export class QueryDiversityEngine {
|
|||||||
private cache: Map<string, { queries: string[]; expires: number }> = new Map()
|
private cache: Map<string, { queries: string[]; expires: number }> = new Map()
|
||||||
private util: Util = new Util()
|
private util: Util = new Util()
|
||||||
private logger?: (source: string, message: string, level?: 'info' | 'warn' | 'error') => void
|
private logger?: (source: string, message: string, level?: 'info' | 'warn' | 'error') => void
|
||||||
|
private httpClient: HttpClient
|
||||||
|
|
||||||
constructor(config?: Partial<QueryDiversityConfig>, logger?: (source: string, message: string, level?: 'info' | 'warn' | 'error') => void) {
|
constructor(config?: Partial<QueryDiversityConfig>, logger?: (source: string, message: string, level?: 'info' | 'warn' | 'error'), httpClient?: HttpClient) {
|
||||||
const maxQueriesPerSource = Math.max(1, Math.min(config?.maxQueriesPerSource || 10, 50))
|
const maxQueriesPerSource = Math.max(1, Math.min(config?.maxQueriesPerSource || 10, 50))
|
||||||
const cacheMinutes = Math.max(1, Math.min(config?.cacheMinutes || 30, 1440))
|
const cacheMinutes = Math.max(1, Math.min(config?.cacheMinutes || 30, 1440))
|
||||||
|
|
||||||
@@ -33,6 +36,7 @@ export class QueryDiversityEngine {
|
|||||||
cacheMinutes
|
cacheMinutes
|
||||||
}
|
}
|
||||||
this.logger = logger
|
this.logger = logger
|
||||||
|
this.httpClient = httpClient || axios
|
||||||
}
|
}
|
||||||
|
|
||||||
private log(source: string, message: string, level: 'info' | 'warn' | 'error' = 'info'): void {
|
private log(source: string, message: string, level: 'info' | 'warn' | 'error' = 'info'): void {
|
||||||
@@ -51,7 +55,7 @@ export class QueryDiversityEngine {
|
|||||||
timeout?: number
|
timeout?: number
|
||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const response = await axios({
|
const response = await this.httpClient.request({
|
||||||
url,
|
url,
|
||||||
method: config?.method || 'GET',
|
method: config?.method || 'GET',
|
||||||
headers: config?.headers || { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' },
|
headers: config?.headers || { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' },
|
||||||
|
|||||||
@@ -226,10 +226,19 @@ export function log(isMobile: boolean | 'main', title: string, message: string,
|
|||||||
type LoggingCfg = { excludeFunc?: string[]; webhookExcludeFunc?: string[]; redactEmails?: boolean }
|
type LoggingCfg = { excludeFunc?: string[]; webhookExcludeFunc?: string[]; redactEmails?: boolean }
|
||||||
const loggingCfg: LoggingCfg = logging || {}
|
const loggingCfg: LoggingCfg = logging || {}
|
||||||
const shouldRedact = !!loggingCfg.redactEmails
|
const shouldRedact = !!loggingCfg.redactEmails
|
||||||
const redact = (s: string) => shouldRedact ? s.replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/ig, (m) => {
|
const redactSensitive = (s: string) => {
|
||||||
const [u, d] = m.split('@'); return `${(u || '').slice(0, 2)}***@${d || ''}`
|
const scrubbed = s
|
||||||
}) : s
|
.replace(/:\/\/[A-Z0-9._%+-]+:[^@\s]+@/ig, '://***:***@')
|
||||||
const cleanStr = redact(`[${currentTime}] [PID: ${process.pid}] [${type.toUpperCase()}] ${platformText} [${title}] ${message}`)
|
.replace(/(token=|apikey=|auth=)[^\s&]+/ig, '$1***')
|
||||||
|
|
||||||
|
if (!shouldRedact) return scrubbed
|
||||||
|
|
||||||
|
return scrubbed.replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/ig, (m) => {
|
||||||
|
const [u, d] = m.split('@'); return `${(u || '').slice(0, 2)}***@${d || ''}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const cleanStr = redactSensitive(`[${currentTime}] [PID: ${process.pid}] [${type.toUpperCase()}] ${platformText} [${title}] ${message}`)
|
||||||
|
|
||||||
// Define conditions for sending to NTFY
|
// Define conditions for sending to NTFY
|
||||||
const ntfyConditions = {
|
const ntfyConditions = {
|
||||||
@@ -293,7 +302,7 @@ export function log(isMobile: boolean | 'main', title: string, message: string,
|
|||||||
typeColor(`${typeIndicator}`),
|
typeColor(`${typeIndicator}`),
|
||||||
platformColor(`[${platformText}]`),
|
platformColor(`[${platformText}]`),
|
||||||
chalk.bold(`[${title}]`),
|
chalk.bold(`[${title}]`),
|
||||||
iconPart + redact(message)
|
iconPart + redactSensitive(message)
|
||||||
].join(' ')
|
].join(' ')
|
||||||
|
|
||||||
const applyChalk = color && typeof chalk[color] === 'function' ? chalk[color] as (msg: string) => string : null
|
const applyChalk = color && typeof chalk[color] === 'function' ? chalk[color] as (msg: string) => string : null
|
||||||
|
|||||||
Reference in New Issue
Block a user