mirror of
https://github.com/LightZirconite/Microsoft-Rewards-Bot.git
synced 2026-01-11 09:46:16 +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
|
## 🆘 Getting Help
|
||||||
|
|
||||||
- 💬 **[Join our Discord](https://discord.gg/h6Z69ZPPCz)** — Community support and updates
|
- 💬 **[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",
|
"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",
|
"typecheck": "tsc --noEmit",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
|
"test": "node --test --loader ts-node/esm tests",
|
||||||
"start": "node --enable-source-maps ./dist/index.js",
|
"start": "node --enable-source-maps ./dist/index.js",
|
||||||
"ts-start": "node --loader ts-node/esm ./src/index.ts",
|
"ts-start": "node --loader ts-node/esm ./src/index.ts",
|
||||||
"dev": "ts-node ./src/index.ts -dev",
|
"dev": "ts-node ./src/index.ts -dev",
|
||||||
|
|||||||
33
src/index.ts
33
src/index.ts
@@ -29,6 +29,7 @@ import { Analytics } from './util/Analytics'
|
|||||||
import { QueryDiversityEngine } from './util/QueryDiversityEngine'
|
import { QueryDiversityEngine } from './util/QueryDiversityEngine'
|
||||||
import JobState from './util/JobState'
|
import JobState from './util/JobState'
|
||||||
import { StartupValidator } from './util/StartupValidator'
|
import { StartupValidator } from './util/StartupValidator'
|
||||||
|
import { MobileRetryTracker } from './util/MobileRetryTracker'
|
||||||
|
|
||||||
|
|
||||||
// Main bot class
|
// Main bot class
|
||||||
@@ -58,7 +59,6 @@ export class MicrosoftRewardsBot {
|
|||||||
private pointsInitial: number = 0
|
private pointsInitial: number = 0
|
||||||
|
|
||||||
private activeWorkers: number
|
private activeWorkers: number
|
||||||
private mobileRetryAttempts: number
|
|
||||||
private browserFactory: Browser = new Browser(this)
|
private browserFactory: Browser = new Browser(this)
|
||||||
private accounts: Account[]
|
private accounts: Account[]
|
||||||
private workers: Workers
|
private workers: Workers
|
||||||
@@ -103,7 +103,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
|
||||||
this.mobileRetryAttempts = 0
|
|
||||||
|
|
||||||
if (this.config.queryDiversity?.enabled) {
|
if (this.config.queryDiversity?.enabled) {
|
||||||
this.queryEngine = new QueryDiversityEngine({
|
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')
|
log(true,'FLOW','Mobile() invoked')
|
||||||
const browser = await this.browserFactory.createBrowser(account.proxy, account.email)
|
const browser = await this.browserFactory.createBrowser(account.proxy, account.email)
|
||||||
this.homePage = await browser.newPage()
|
this.homePage = await browser.newPage()
|
||||||
@@ -1139,6 +1141,9 @@ export class MicrosoftRewardsBot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Do mobile searches
|
// Do mobile searches
|
||||||
|
const configuredRetries = Number(this.config.searchSettings.retryMobileSearchAmount ?? 0)
|
||||||
|
const maxMobileRetries = Number.isFinite(configuredRetries) ? configuredRetries : 0
|
||||||
|
|
||||||
if (this.config.workers.doMobileSearch) {
|
if (this.config.workers.doMobileSearch) {
|
||||||
// If no mobile searches data found, stop (Does not always exist on new accounts)
|
// If no mobile searches data found, stop (Does not always exist on new accounts)
|
||||||
if (data.userStatus.counters.mobileSearch) {
|
if (data.userStatus.counters.mobileSearch) {
|
||||||
@@ -1154,21 +1159,21 @@ export class MicrosoftRewardsBot {
|
|||||||
const mobileSearchPoints = (await this.browser.func.getSearchPoints()).mobileSearch?.[0]
|
const mobileSearchPoints = (await this.browser.func.getSearchPoints()).mobileSearch?.[0]
|
||||||
|
|
||||||
if (mobileSearchPoints && (mobileSearchPoints.pointProgressMax - mobileSearchPoints.pointProgress) > 0) {
|
if (mobileSearchPoints && (mobileSearchPoints.pointProgressMax - mobileSearchPoints.pointProgress) > 0) {
|
||||||
// Increment retry count
|
const shouldRetry = retryTracker.registerFailure()
|
||||||
this.mobileRetryAttempts++
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit if retries are exhausted
|
if (!shouldRetry) {
|
||||||
if (this.mobileRetryAttempts > this.config.searchSettings.retryMobileSearchAmount) {
|
const exhaustedAttempts = retryTracker.getAttemptCount()
|
||||||
log(this.isMobile, 'MAIN', `Max retry limit of ${this.config.searchSettings.retryMobileSearchAmount} reached. Exiting retry loop`, 'warn')
|
log(this.isMobile, 'MAIN', `Max retry limit of ${maxMobileRetries} reached after ${exhaustedAttempts} attempt(s). Exiting retry loop`, 'warn')
|
||||||
} else if (this.mobileRetryAttempts !== 0) {
|
} else {
|
||||||
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')
|
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
|
// Close mobile browser before retrying to release resources
|
||||||
await this.browser.func.closeBrowser(browser, account.email)
|
await this.browser.func.closeBrowser(browser, account.email)
|
||||||
|
|
||||||
// Create a new browser and try
|
// Create a new browser and try again with the same tracker
|
||||||
return await this.Mobile(account)
|
return await this.Mobile(account, retryTracker)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log(this.isMobile, 'MAIN', 'Unable to fetch search points, your account is most likely too "new" for this! Try again later!', 'warn')
|
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