mirror of
https://github.com/LightZirconite/Microsoft-Rewards-Bot.git
synced 2026-01-09 17:06:15 +00:00
Added a system for tracking mobile search attempts and updated associated tests
This commit is contained in:
@@ -174,6 +174,12 @@ All while maintaining **natural behavior patterns** to minimize detection risk.
|
||||
|
||||
---
|
||||
|
||||
## ✅ Tests
|
||||
|
||||
- `npm run test`: runs the node:test suite with ts-node to validate critical utilities.
|
||||
|
||||
---
|
||||
|
||||
## 🆘 Getting Help
|
||||
|
||||
- 💬 **[Join our Discord](https://discord.gg/h6Z69ZPPCz)** — Community support and updates
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"pre-build": "npm i && npm run clean && node -e \"process.exit(process.env.SKIP_PLAYWRIGHT_INSTALL?0:1)\" || npx playwright install chromium",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"build": "tsc",
|
||||
"test": "node --test --loader ts-node/esm tests",
|
||||
"start": "node --enable-source-maps ./dist/index.js",
|
||||
"ts-start": "node --loader ts-node/esm ./src/index.ts",
|
||||
"dev": "ts-node ./src/index.ts -dev",
|
||||
@@ -27,7 +28,7 @@
|
||||
"start:schedule": "node --enable-source-maps ./dist/scheduler.js",
|
||||
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
||||
"prepare": "npm run build",
|
||||
"setup": "node ./setup/update/setup.mjs",
|
||||
"setup": "node ./setup/update/setup.mjs",
|
||||
"kill-chrome-win": "powershell -Command \"Get-Process | Where-Object { $_.MainModule.FileVersionInfo.FileDescription -eq 'Google Chrome for Testing' } | ForEach-Object { Stop-Process -Id $_.Id -Force }\"",
|
||||
"create-docker": "docker build -t microsoft-rewards-bot ."
|
||||
},
|
||||
|
||||
35
src/index.ts
35
src/index.ts
@@ -29,6 +29,7 @@ import { Analytics } from './util/Analytics'
|
||||
import { QueryDiversityEngine } from './util/QueryDiversityEngine'
|
||||
import JobState from './util/JobState'
|
||||
import { StartupValidator } from './util/StartupValidator'
|
||||
import { MobileRetryTracker } from './util/MobileRetryTracker'
|
||||
|
||||
|
||||
// Main bot class
|
||||
@@ -58,7 +59,6 @@ export class MicrosoftRewardsBot {
|
||||
private pointsInitial: number = 0
|
||||
|
||||
private activeWorkers: number
|
||||
private mobileRetryAttempts: number
|
||||
private browserFactory: Browser = new Browser(this)
|
||||
private accounts: Account[]
|
||||
private workers: Workers
|
||||
@@ -103,7 +103,6 @@ export class MicrosoftRewardsBot {
|
||||
this.workers = new Workers(this)
|
||||
this.humanizer = new Humanizer(this.utils, this.config.humanization)
|
||||
this.activeWorkers = this.config.clusters
|
||||
this.mobileRetryAttempts = 0
|
||||
|
||||
if (this.config.queryDiversity?.enabled) {
|
||||
this.queryEngine = new QueryDiversityEngine({
|
||||
@@ -1070,7 +1069,10 @@ export class MicrosoftRewardsBot {
|
||||
}
|
||||
}
|
||||
|
||||
async Mobile(account: Account): Promise<{ initialPoints: number; collectedPoints: number }> {
|
||||
async Mobile(
|
||||
account: Account,
|
||||
retryTracker = new MobileRetryTracker(this.config.searchSettings.retryMobileSearchAmount)
|
||||
): Promise<{ initialPoints: number; collectedPoints: number }> {
|
||||
log(true,'FLOW','Mobile() invoked')
|
||||
const browser = await this.browserFactory.createBrowser(account.proxy, account.email)
|
||||
this.homePage = await browser.newPage()
|
||||
@@ -1139,6 +1141,9 @@ export class MicrosoftRewardsBot {
|
||||
}
|
||||
|
||||
// Do mobile searches
|
||||
const configuredRetries = Number(this.config.searchSettings.retryMobileSearchAmount ?? 0)
|
||||
const maxMobileRetries = Number.isFinite(configuredRetries) ? configuredRetries : 0
|
||||
|
||||
if (this.config.workers.doMobileSearch) {
|
||||
// If no mobile searches data found, stop (Does not always exist on new accounts)
|
||||
if (data.userStatus.counters.mobileSearch) {
|
||||
@@ -1154,21 +1159,21 @@ export class MicrosoftRewardsBot {
|
||||
const mobileSearchPoints = (await this.browser.func.getSearchPoints()).mobileSearch?.[0]
|
||||
|
||||
if (mobileSearchPoints && (mobileSearchPoints.pointProgressMax - mobileSearchPoints.pointProgress) > 0) {
|
||||
// Increment retry count
|
||||
this.mobileRetryAttempts++
|
||||
}
|
||||
const shouldRetry = retryTracker.registerFailure()
|
||||
|
||||
// Exit if retries are exhausted
|
||||
if (this.mobileRetryAttempts > this.config.searchSettings.retryMobileSearchAmount) {
|
||||
log(this.isMobile, 'MAIN', `Max retry limit of ${this.config.searchSettings.retryMobileSearchAmount} reached. Exiting retry loop`, 'warn')
|
||||
} else if (this.mobileRetryAttempts !== 0) {
|
||||
log(this.isMobile, 'MAIN', `Attempt ${this.mobileRetryAttempts}/${this.config.searchSettings.retryMobileSearchAmount}: Unable to complete mobile searches, bad User-Agent? Increase search delay? Retrying...`, 'log', 'yellow')
|
||||
if (!shouldRetry) {
|
||||
const exhaustedAttempts = retryTracker.getAttemptCount()
|
||||
log(this.isMobile, 'MAIN', `Max retry limit of ${maxMobileRetries} reached after ${exhaustedAttempts} attempt(s). Exiting retry loop`, 'warn')
|
||||
} else {
|
||||
const attempt = retryTracker.getAttemptCount()
|
||||
log(this.isMobile, 'MAIN', `Attempt ${attempt}/${maxMobileRetries}: Unable to complete mobile searches, bad User-Agent? Increase search delay? Retrying...`, 'log', 'yellow')
|
||||
|
||||
// Close mobile browser
|
||||
await this.browser.func.closeBrowser(browser, account.email)
|
||||
// Close mobile browser before retrying to release resources
|
||||
await this.browser.func.closeBrowser(browser, account.email)
|
||||
|
||||
// Create a new browser and try
|
||||
return await this.Mobile(account)
|
||||
// Create a new browser and try again with the same tracker
|
||||
return await this.Mobile(account, retryTracker)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log(this.isMobile, 'MAIN', 'Unable to fetch search points, your account is most likely too "new" for this! Try again later!', 'warn')
|
||||
|
||||
26
src/util/MobileRetryTracker.ts
Normal file
26
src/util/MobileRetryTracker.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export class MobileRetryTracker {
|
||||
private attempts = 0
|
||||
private readonly maxRetries: number
|
||||
|
||||
constructor(maxRetries: number) {
|
||||
const normalized = Number.isFinite(maxRetries) ? Math.floor(maxRetries) : 0
|
||||
this.maxRetries = Math.max(0, normalized)
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an incomplete mobile search attempt.
|
||||
* @returns true when another retry should be attempted, false when the retry budget is exhausted.
|
||||
*/
|
||||
registerFailure(): boolean {
|
||||
this.attempts += 1
|
||||
return this.attempts <= this.maxRetries
|
||||
}
|
||||
|
||||
hasExceeded(): boolean {
|
||||
return this.attempts > this.maxRetries
|
||||
}
|
||||
|
||||
getAttemptCount(): number {
|
||||
return this.attempts
|
||||
}
|
||||
}
|
||||
28
tests/mobileRetryTracker.test.js
Normal file
28
tests/mobileRetryTracker.test.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const test = require('node:test')
|
||||
const assert = require('node:assert/strict')
|
||||
|
||||
const { MobileRetryTracker } = require('../dist/util/MobileRetryTracker.js')
|
||||
|
||||
test('MobileRetryTracker stops retries after configured limit', () => {
|
||||
const tracker = new MobileRetryTracker(2)
|
||||
|
||||
assert.equal(tracker.registerFailure(), true)
|
||||
assert.equal(tracker.hasExceeded(), false)
|
||||
assert.equal(tracker.getAttemptCount(), 1)
|
||||
|
||||
assert.equal(tracker.registerFailure(), true)
|
||||
assert.equal(tracker.hasExceeded(), false)
|
||||
assert.equal(tracker.getAttemptCount(), 2)
|
||||
|
||||
assert.equal(tracker.registerFailure(), false)
|
||||
assert.equal(tracker.hasExceeded(), true)
|
||||
assert.equal(tracker.getAttemptCount(), 3)
|
||||
})
|
||||
|
||||
test('MobileRetryTracker normalizes invalid configuration', () => {
|
||||
const tracker = new MobileRetryTracker(-3)
|
||||
|
||||
assert.equal(tracker.registerFailure(), false)
|
||||
assert.equal(tracker.hasExceeded(), true)
|
||||
assert.equal(tracker.getAttemptCount(), 1)
|
||||
})
|
||||
28
tests/mobileRetryTracker.test.ts
Normal file
28
tests/mobileRetryTracker.test.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
import { MobileRetryTracker } from '../src/util/MobileRetryTracker'
|
||||
|
||||
test('MobileRetryTracker stops retries after configured limit', () => {
|
||||
const tracker = new MobileRetryTracker(2)
|
||||
|
||||
assert.equal(tracker.registerFailure(), true)
|
||||
assert.equal(tracker.hasExceeded(), false)
|
||||
assert.equal(tracker.getAttemptCount(), 1)
|
||||
|
||||
assert.equal(tracker.registerFailure(), true)
|
||||
assert.equal(tracker.hasExceeded(), false)
|
||||
assert.equal(tracker.getAttemptCount(), 2)
|
||||
|
||||
assert.equal(tracker.registerFailure(), false)
|
||||
assert.equal(tracker.hasExceeded(), true)
|
||||
assert.equal(tracker.getAttemptCount(), 3)
|
||||
})
|
||||
|
||||
test('MobileRetryTracker normalizes invalid configuration', () => {
|
||||
const tracker = new MobileRetryTracker(-3)
|
||||
|
||||
assert.equal(tracker.registerFailure(), false)
|
||||
assert.equal(tracker.hasExceeded(), true)
|
||||
assert.equal(tracker.getAttemptCount(), 1)
|
||||
})
|
||||
Reference in New Issue
Block a user