v3.1.0 initial

This commit is contained in:
TheNetsky
2026-01-05 16:26:47 +01:00
parent a8ddb65b21
commit 576899f39d
37 changed files with 3391 additions and 865 deletions

View File

@@ -0,0 +1,124 @@
import type { AxiosRequestConfig } from 'axios'
import { Workers } from '../../Workers'
import { PromotionalItem } from '../../../interface/DashboardData'
export class DoubleSearchPoints extends Workers {
private cookieHeader: string = ''
private fingerprintHeader: { [x: string]: string } = {}
public async doDoubleSearchPoints(promotion: PromotionalItem) {
const offerId = promotion.offerId
const activityType = promotion.activityType
try {
if (!this.bot.requestToken) {
this.bot.logger.warn(
this.bot.isMobile,
'DOUBLE-SEARCH-POINTS',
'Skipping: Request token not available, this activity requires it!'
)
return
}
this.cookieHeader = (this.bot.isMobile ? this.bot.cookies.mobile : this.bot.cookies.desktop)
.map((c: { name: string; value: string }) => `${c.name}=${c.value}`)
.join('; ')
const fingerprintHeaders = { ...this.bot.fingerprint.headers }
delete fingerprintHeaders['Cookie']
delete fingerprintHeaders['cookie']
this.fingerprintHeader = fingerprintHeaders
this.bot.logger.info(
this.bot.isMobile,
'DOUBLE-SEARCH-POINTS',
`Starting Double Search Points | offerId=${offerId}`
)
this.bot.logger.debug(
this.bot.isMobile,
'DOUBLE-SEARCH-POINTS',
`Prepared headers | cookieLength=${this.cookieHeader.length} | fingerprintHeaderKeys=${Object.keys(this.fingerprintHeader).length}`
)
const formData = new URLSearchParams({
id: offerId,
hash: promotion.hash,
timeZone: '60',
activityAmount: '1',
dbs: '0',
form: '',
type: activityType,
__RequestVerificationToken: this.bot.requestToken
})
this.bot.logger.debug(
this.bot.isMobile,
'DOUBLE-SEARCH-POINTS',
`Prepared Double Search Points form data | offerId=${offerId} | hash=${promotion.hash} | timeZone=60 | activityAmount=1 | type=${activityType}`
)
const request: AxiosRequestConfig = {
url: 'https://rewards.bing.com/api/reportactivity?X-Requested-With=XMLHttpRequest',
method: 'POST',
headers: {
...(this.bot.fingerprint?.headers ?? {}),
Cookie: this.cookieHeader,
Referer: 'https://rewards.bing.com/',
Origin: 'https://rewards.bing.com'
},
data: formData
}
this.bot.logger.debug(
this.bot.isMobile,
'DOUBLE-SEARCH-POINTS',
`Sending Double Search Points request | offerId=${offerId} | url=${request.url}`
)
const response = await this.bot.axios.request(request)
this.bot.logger.debug(
this.bot.isMobile,
'DOUBLE-SEARCH-POINTS',
`Received Double Search Points response | offerId=${offerId} | status=${response.status}`
)
const data = await this.bot.browser.func.getDashboardData()
const promotionalItem = data.promotionalItems.find(item =>
item.name.toLowerCase().includes('ww_banner_optin_2x')
)
// If OK, should no longer be presernt in promotionalItems
if (promotionalItem) {
this.bot.logger.warn(
this.bot.isMobile,
'DOUBLE-SEARCH-POINTS',
`Unable to find or activate Double Search Points | offerId=${offerId} | status=${response.status}`
)
} else {
this.bot.logger.info(
this.bot.isMobile,
'DOUBLE-SEARCH-POINTS',
`Activated Double Search Points | offerId=${offerId} | status=${response.status}`,
'green'
)
}
this.bot.logger.debug(
this.bot.isMobile,
'DOUBLE-SEARCH-POINTS',
`Waiting after Double Search Points | offerId=${offerId}`
)
await this.bot.utils.wait(this.bot.utils.randomDelay(5000, 10000))
} catch (error) {
this.bot.logger.error(
this.bot.isMobile,
'DOUBLE-SEARCH-POINTS',
`Error in doDoubleSearchPoints | offerId=${offerId} | message=${error instanceof Error ? error.message : String(error)}`
)
}
}
}

View File

@@ -33,38 +33,34 @@ export class Search extends Workers {
`Search points remaining | Edge=${missingPoints.edgePoints} | Desktop=${missingPoints.desktopPoints} | Mobile=${missingPoints.mobilePoints}`
)
let queries: string[] = []
const queryCore = new QueryCore(this.bot)
const locale = (this.bot.userData.geoLocale ?? 'US').toUpperCase()
const langCode = (this.bot.userData.langCode ?? 'en').toLowerCase()
const locale = this.bot.userData.geoLocale.toUpperCase()
this.bot.logger.debug(
isMobile,
'SEARCH-BING',
`Resolving search queries via QueryCore | locale=${locale} | lang=${langCode} | related=true`
)
this.bot.logger.debug(isMobile, 'SEARCH-BING', `Resolving search queries | locale=${locale}`)
let queries = await queryCore.queryManager({
shuffle: true,
related: true,
langCode,
geoLocale: locale,
sourceOrder: ['google', 'wikipedia', 'reddit', 'local']
})
// Set Google search queries
queries = await queryCore.getGoogleTrends(locale)
queries = [...new Set(queries.map(q => q.trim()).filter(Boolean))]
this.bot.logger.debug(isMobile, 'SEARCH-BING', `Fetched base queries | count=${queries.length}`)
// Deduplicate queries
queries = [...new Set(queries)]
this.bot.logger.debug(isMobile, 'SEARCH-BING', `Deduplicated queries | count=${queries.length}`)
// Shuffle
queries = this.bot.utils.shuffleArray(queries)
this.bot.logger.debug(isMobile, 'SEARCH-BING', `Shuffled queries | count=${queries.length}`)
this.bot.logger.debug(isMobile, 'SEARCH-BING', `Query pool ready | count=${queries.length}`)
// Go to bing
const targetUrl = this.searchPageURL ? this.searchPageURL : this.bingHome
this.bot.logger.debug(isMobile, 'SEARCH-BING', `Navigating to search page | url=${targetUrl}`)
await page.goto(targetUrl)
// Wait until page loaded
await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {})
await this.bot.browser.utils.tryDismissAllMessages(page)
let stagnantLoop = 0
@@ -77,7 +73,6 @@ export class Search extends Workers {
const newMissingPoints = this.bot.browser.func.missingSearchPoints(searchCounters, isMobile)
const newMissingPointsTotal = newMissingPoints.totalPoints
// Points gained for THIS query only
const rawGained = missingPointsTotal - newMissingPointsTotal
const gainedPoints = Math.max(0, rawGained)
@@ -91,12 +86,10 @@ export class Search extends Workers {
} else {
stagnantLoop = 0
// Update global user data
const newBalance = Number(this.bot.userData.currentPoints ?? 0) + gainedPoints
this.bot.userData.currentPoints = newBalance
this.bot.userData.gainedPoints = (this.bot.userData.gainedPoints ?? 0) + gainedPoints
// Track for return value
totalGainedPoints += gainedPoints
this.bot.logger.info(
@@ -107,10 +100,8 @@ export class Search extends Workers {
)
}
// Update loop state
missingPointsTotal = newMissingPointsTotal
// Completed
if (missingPointsTotal === 0) {
this.bot.logger.info(
isMobile,
@@ -120,7 +111,6 @@ export class Search extends Workers {
break
}
// Stuck
if (stagnantLoop > stagnantLoopMax) {
this.bot.logger.warn(
isMobile,
@@ -130,105 +120,123 @@ export class Search extends Workers {
stagnantLoop = 0
break
}
const remainingQueries = queries.length - (i + 1)
const minBuffer = 20
if (missingPointsTotal > 0 && remainingQueries < minBuffer) {
this.bot.logger.warn(
isMobile,
'SEARCH-BING',
`Low query buffer while still missing points, regenerating | remainingQueries=${remainingQueries} | missing=${missingPointsTotal}`
)
const extra = await queryCore.queryManager({
shuffle: true,
related: true,
langCode,
geoLocale: locale,
sourceOrder: this.bot.config.searchSettings.queryEngines
})
const merged = [...queries, ...extra].map(q => q.trim()).filter(Boolean)
queries = [...new Set(merged)]
queries = this.bot.utils.shuffleArray(queries)
this.bot.logger.debug(isMobile, 'SEARCH-BING', `Query pool regenerated | count=${queries.length}`)
}
}
if (missingPointsTotal > 0) {
this.bot.logger.info(
isMobile,
'SEARCH-BING',
`Search completed but still missing points, generating extra searches | remaining=${missingPointsTotal}`
`Search completed but still missing points, continuing with regenerated queries | remaining=${missingPointsTotal}`
)
let i = 0
let stagnantLoop = 0
const stagnantLoopMax = 5
while (missingPointsTotal > 0) {
const query = queries[i++] as string
const extra = await queryCore.queryManager({
shuffle: true,
related: true,
langCode,
geoLocale: locale,
sourceOrder: this.bot.config.searchSettings.queryEngines
})
const merged = [...queries, ...extra].map(q => q.trim()).filter(Boolean)
const newPool = [...new Set(merged)]
queries = this.bot.utils.shuffleArray(newPool)
this.bot.logger.debug(
isMobile,
'SEARCH-BING-EXTRA',
`Fetching related terms for extra searches | baseQuery="${query}"`
`New query pool generated | count=${queries.length}`
)
const relatedTerms = await queryCore.getBingRelatedTerms(query)
this.bot.logger.debug(
isMobile,
'SEARCH-BING-EXTRA',
`Related terms resolved | baseQuery="${query}" | count=${relatedTerms.length}`
)
for (const query of queries) {
this.bot.logger.info(
isMobile,
'SEARCH-BING-EXTRA',
`Extra search | remaining=${missingPointsTotal} | query="${query}"`
)
if (relatedTerms.length > 3) {
for (const term of relatedTerms.slice(1, 3)) {
searchCounters = await this.bingSearch(page, query, isMobile)
const newMissingPoints = this.bot.browser.func.missingSearchPoints(searchCounters, isMobile)
const newMissingPointsTotal = newMissingPoints.totalPoints
const rawGained = missingPointsTotal - newMissingPointsTotal
const gainedPoints = Math.max(0, rawGained)
if (gainedPoints === 0) {
stagnantLoop++
this.bot.logger.info(
isMobile,
'SEARCH-BING-EXTRA',
`Extra search | remaining=${missingPointsTotal} | query="${term}"`
`No points gained ${stagnantLoop}/${stagnantLoopMax} | query="${query}" | remaining=${newMissingPointsTotal}`
)
} else {
stagnantLoop = 0
searchCounters = await this.bingSearch(page, term, isMobile)
const newMissingPoints = this.bot.browser.func.missingSearchPoints(searchCounters, isMobile)
const newMissingPointsTotal = newMissingPoints.totalPoints
const newBalance = Number(this.bot.userData.currentPoints ?? 0) + gainedPoints
this.bot.userData.currentPoints = newBalance
this.bot.userData.gainedPoints = (this.bot.userData.gainedPoints ?? 0) + gainedPoints
// Points gained for THIS extra query only
const rawGained = missingPointsTotal - newMissingPointsTotal
const gainedPoints = Math.max(0, rawGained)
totalGainedPoints += gainedPoints
if (gainedPoints === 0) {
stagnantLoop++
this.bot.logger.info(
isMobile,
'SEARCH-BING-EXTRA',
`No points gained for extra query ${stagnantLoop}/${stagnantLoopMax} | query="${term}" | remaining=${newMissingPointsTotal}`
)
} else {
stagnantLoop = 0
this.bot.logger.info(
isMobile,
'SEARCH-BING-EXTRA',
`gainedPoints=${gainedPoints} points | query="${query}" | remaining=${newMissingPointsTotal}`,
'green'
)
}
// Update global user data
const newBalance = Number(this.bot.userData.currentPoints ?? 0) + gainedPoints
this.bot.userData.currentPoints = newBalance
this.bot.userData.gainedPoints = (this.bot.userData.gainedPoints ?? 0) + gainedPoints
missingPointsTotal = newMissingPointsTotal
// Track for return value
totalGainedPoints += gainedPoints
if (missingPointsTotal === 0) {
this.bot.logger.info(
isMobile,
'SEARCH-BING-EXTRA',
'All required search points earned during extra searches'
)
break
}
this.bot.logger.info(
isMobile,
'SEARCH-BING-EXTRA',
`gainedPoints=${gainedPoints} points | query="${term}" | remaining=${newMissingPointsTotal}`,
'green'
)
}
// Update loop state
missingPointsTotal = newMissingPointsTotal
// Completed
if (missingPointsTotal === 0) {
this.bot.logger.info(
isMobile,
'SEARCH-BING-EXTRA',
'All required search points earned during extra searches'
)
break
}
// Stuck again
if (stagnantLoop > stagnantLoopMax) {
this.bot.logger.warn(
isMobile,
'SEARCH-BING-EXTRA',
`Search did not gain points for ${stagnantLoopMax} extra iterations, aborting extra searches`
)
const finalBalance = Number(this.bot.userData.currentPoints ?? startBalance)
this.bot.logger.info(
isMobile,
'SEARCH-BING',
`Aborted extra searches | startBalance=${startBalance} | finalBalance=${finalBalance}`
)
return totalGainedPoints
}
if (stagnantLoop > stagnantLoopMax) {
this.bot.logger.warn(
isMobile,
'SEARCH-BING-EXTRA',
`Search did not gain points for ${stagnantLoopMax} iterations, aborting extra searches`
)
const finalBalance = Number(this.bot.userData.currentPoints ?? startBalance)
this.bot.logger.info(
isMobile,
'SEARCH-BING',
`Aborted extra searches | startBalance=${startBalance} | finalBalance=${finalBalance}`
)
return totalGainedPoints
}
}
}
@@ -259,7 +267,6 @@ export class Search extends Workers {
this.searchCount++
// Page fill seems to get more sluggish over time
if (this.searchCount % refreshThreshold === 0) {
this.bot.logger.info(
isMobile,
@@ -271,7 +278,7 @@ export class Search extends Workers {
await searchPage.goto(this.bingHome)
await searchPage.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {})
await this.bot.browser.utils.tryDismissAllMessages(searchPage) // Not always the case but possible for new cookie headers
await this.bot.browser.utils.tryDismissAllMessages(searchPage)
}
this.bot.logger.debug(
@@ -402,11 +409,9 @@ export class Search extends Workers {
await this.bot.utils.wait(this.bot.config.searchSettings.searchResultVisitTime)
if (isMobile) {
// Mobile
await page.goto(searchPageUrl)
this.bot.logger.debug(isMobile, 'SEARCH-RANDOM-CLICK', 'Navigated back to search page')
} else {
// Desktop
const newTab = await this.bot.browser.utils.getLatestTab(page)
const newTabUrl = newTab.url()

View File

@@ -232,7 +232,7 @@ export class SearchOnBing extends Workers {
if (this.bot.config.searchOnBingLocalQueries) {
this.bot.logger.debug(this.bot.isMobile, 'SEARCH-ON-BING-QUERY', 'Using local queries config file')
const data = fs.readFileSync(path.join(__dirname, '../queries.json'), 'utf8')
const data = fs.readFileSync(path.join(__dirname, '../bing-search-activity-queries.json'), 'utf8')
queries = JSON.parse(data)
this.bot.logger.debug(
@@ -250,7 +250,7 @@ export class SearchOnBing extends Workers {
// Fetch from the repo directly so the user doesn't need to redownload the script for the new activities
const response = await this.bot.axios.request({
method: 'GET',
url: 'https://raw.githubusercontent.com/TheNetsky/Microsoft-Rewards-Script/refs/heads/v3/src/functions/queries.json'
url: 'https://raw.githubusercontent.com/TheNetsky/Microsoft-Rewards-Script/refs/heads/v3/src/functions/bing-search-activity-queries.json'
})
queries = response.data