This commit is contained in:
TheNetsky
2023-12-01 11:49:14 +01:00
parent 28286ff9fe
commit a8cb5482d4
7 changed files with 115 additions and 113 deletions

View File

@@ -11,6 +11,11 @@ Under development, however mainly for personal use!
5. Run `npm run build` to build the script
6. Run `npm run start` to start the built script
## Notes ##
- If you end the script without closing the browser window first (only with headless as false), you'll be left with hanging chrome instances using resources. Use taskmanager to kill these or use the included `npm run chrome-kill` script. WINDOWS ONLY
- If you automate this script, set it to run at least 2 times a day to make sure it picked up all tasks, set `"runOnZeroPoints": false` so it doesn't run when no points are found.
## Features ##
- [x] Multi-Account Support
- [x] Session Storing

View File

@@ -1,6 +1,6 @@
{
"name": "microsoft-rewards-script",
"version": "1.2.5",
"version": "1.3.0",
"description": "Automatically do tasks for Microsoft Rewards but in TS!",
"main": "index.js",
"engines": {
@@ -10,7 +10,8 @@
"build": "tsc",
"start": "node ./dist/index.js",
"ts-start": "ts-node ./src/index.ts",
"dev": "ts-node ./src/index.ts -dev"
"dev": "ts-node ./src/index.ts -dev",
"kill-chrome-win": "powershell -Command \"Get-Process | Where-Object { $_.MainModule.FileVersionInfo.FileDescription -eq 'Google Chrome for Testing' } | ForEach-Object { Stop-Process -Id $_.Id -Force }\""
},
"keywords": [
"Bing Rewards",
@@ -30,11 +31,11 @@
"typescript": "^5.2.2"
},
"dependencies": {
"axios": "^1.6.1",
"axios": "^1.6.2",
"cheerio": "^1.0.0-rc.12",
"fingerprint-generator": "^2.1.43",
"fingerprint-injector": "^2.1.43",
"puppeteer": "^21.2.1",
"fingerprint-generator": "^2.1.45",
"fingerprint-injector": "^2.1.45",
"puppeteer": "^21.5.2",
"ts-node": "^10.9.1"
}
}

View File

@@ -24,6 +24,10 @@ export default class BrowserFunc {
try {
const dashboardURL = new URL(this.bot.config.baseURL)
if (page.url() === dashboardURL.href) {
return
}
await page.goto(this.bot.config.baseURL)
const maxIterations = 5 // Maximum iterations set to 5

View File

@@ -27,6 +27,11 @@ export class Workers {
await this.solveActivities(page, activitiesUncompleted)
page = await this.bot.browser.utils.getLatestTab(page)
// Always return to the homepage if not already
await this.bot.browser.func.goHome(page)
this.bot.log('DAILY-SET', 'All "Daily Set" items have been completed')
}
@@ -41,24 +46,31 @@ export class Workers {
}
for (const punchCard of punchCardsUncompleted) {
// Get latest page for each card
page = await this.bot.browser.utils.getLatestTab(page)
const activitiesUncompleted = punchCard.childPromotions.filter(x => !x.complete) // Only return uncompleted activities
// Solve Activities
this.bot.log('PUNCH-CARD', `Started solving "Punch Card" items for punchcard: "${punchCard.parentPromotion.title}"`)
const browser = page.browser()
page = await browser.newPage()
// Got to punch card index page in a new tab
await page.goto(punchCard.parentPromotion.destinationUrl, { referer: this.bot.config.baseURL })
// Wait for new page to load, max 10 seconds, however try regardless in case of error
await page.waitForNetworkIdle({ timeout: 10_000 }).catch(() => { })
await page.waitForNetworkIdle({ timeout: 5_000 }).catch(() => { })
await this.solveActivities(page, activitiesUncompleted, punchCard)
// Close the punch card index page
page = await this.bot.browser.utils.getLatestTab(page)
const pages = await (page.browser()).pages()
if (pages.length > 3) {
await page.close()
} else {
await this.bot.browser.func.goHome(page)
}
this.bot.log('PUNCH-CARD', `All items for punchcard: "${punchCard.parentPromotion.title}" have been completed`)
}
@@ -85,39 +97,55 @@ export class Workers {
// Solve Activities
this.bot.log('MORE-PROMOTIONS', 'Started solving "More Promotions" item')
page = await this.bot.browser.utils.getLatestTab(page)
await this.solveActivities(page, activitiesUncompleted)
page = await this.bot.browser.utils.getLatestTab(page)
// Always return to the homepage if not already
await this.bot.browser.func.goHome(page)
this.bot.log('MORE-PROMOTIONS', 'All "More Promotion" items have been completed')
}
// Solve all the different types of activities
private async solveActivities(page: Page, activities: PromotionalItem[] | MorePromotion[], punchCard?: PunchCard) {
let activityPage = page
private async solveActivities(activityPage: Page, activities: PromotionalItem[] | MorePromotion[], punchCard?: PunchCard) {
const activityInitial = activityPage.url() // Homepage for Daily/More and Index for promotions
for (const activity of activities) {
try {
// Reselect the worker page
activityPage = await this.bot.browser.utils.getLatestTab(activityPage)
const pages = await activityPage.browser().pages()
if (pages.length > 3) {
await activityPage.close()
activityPage = await this.bot.browser.utils.getLatestTab(activityPage)
}
await this.bot.utils.wait(1000)
if (activityPage.url() !== activityInitial) {
await activityPage.goto(activityInitial)
}
let selector = `[data-bi-id="${activity.offerId}"]`
if (punchCard) {
selector = await this.bot.browser.func.getPunchCardActivity(page, activity)
selector = await this.bot.browser.func.getPunchCardActivity(activityPage, activity)
} else if (activity.name.toLowerCase().includes('membercenter')) {
// Promotion
if (activity.priority === 1) {
selector = await page.waitForSelector('#promo-item', { visible: true, timeout: 2000 }).then(() => true).catch(() => false) ?
'#promo-item' : activity.name
} else {
selector = `[data-bi-id="${activity.name}"]`
}
}
// Click element, it will be opened in a new tab
await page.click(selector)
await activityPage.click(selector)
// Select the new activity page
activityPage = await this.bot.browser.utils.getLatestTab(page)
activityPage = await this.bot.browser.utils.getLatestTab(activityPage)
// Wait for the new tab to fully load, ignore error.
/*
@@ -175,12 +203,8 @@ export class Workers {
} catch (error) {
this.bot.log('ACTIVITY', 'An error occurred:' + error, 'error')
const tabs = await (page.browser()).pages()
}
if (tabs.length > 2) {
await activityPage.close() // Already assigned to be the "latest tab"
}
}
}
}

View File

@@ -3,7 +3,7 @@ import axios from 'axios'
import { Workers } from '../Workers'
import { DashboardData, DashboardImpression } from '../../interface/DashboardData'
import { Counters, DashboardData } from '../../interface/DashboardData'
import { GoogleTrends } from '../../interface/GoogleDailyTrends'
import { GoogleSearch } from '../../interface/Search'
@@ -15,21 +15,12 @@ export class Search extends Workers {
public async doSearch(page: Page, data: DashboardData) {
this.bot.log('SEARCH-BING', 'Starting bing searches')
let retries = 0
while ((!data.userStatus.counters?.pcSearch || data.userStatus.counters.pcSearch.length < 2) && retries < 3) {
data = await this.bot.browser.func.getDashboardData()
retries++
}
page = await this.bot.browser.utils.getLatestTab(page)
const mobileData = data.userStatus.counters?.mobileSearch ? data.userStatus.counters.mobileSearch[0] : null // Mobile searches
const edgeData = data.userStatus.counters.pcSearch[1] as DashboardImpression // Edge searches
const genericData = data.userStatus.counters.pcSearch[0] as DashboardImpression // Normal searches
let searchCounters: Counters = await this.bot.browser.func.getSearchPoints()
let missingPoints = this.calculatePoints(searchCounters)
let missingPoints = (this.bot.isMobile && mobileData) ?
(mobileData.pointProgressMax - mobileData.pointProgress) :
(edgeData.pointProgressMax - edgeData.pointProgress) + (genericData.pointProgressMax - genericData.pointProgress)
if (missingPoints == 0) {
if (missingPoints === 0) {
this.bot.log('SEARCH-BING', `Bing searches for ${this.bot.isMobile ? 'MOBILE' : 'DESKTOP'} have already been completed`)
return
}
@@ -41,7 +32,6 @@ export class Search extends Workers {
// Deduplicate the search terms
googleSearchQueries = [...new Set(googleSearchQueries)]
// Go to bing
await page.goto(this.searchPageURL)
@@ -57,25 +47,8 @@ export class Search extends Workers {
this.bot.log('SEARCH-BING', `${missingPoints} Points Remaining | Query: ${query} | Mobile: ${this.bot.isMobile}`)
let newData = await this.bingSearch(page, page, query)
let retries = 0
while ((!newData?.pcSearch || newData.pcSearch.length < 2) && retries < 3) {
newData = await this.bot.browser.func.getSearchPoints()
retries++
}
if (!newData?.pcSearch || newData.pcSearch.length < 2) {
newData = await this.bot.browser.func.getSearchPoints()
}
const newMobileData = newData.mobileSearch ? newData.mobileSearch[0] : null // Mobile searches
const newEdgeData = newData.pcSearch[1] as DashboardImpression // Edge searches
const newGenericData = newData.pcSearch[0] as DashboardImpression // Normal searches
const newMissingPoints = (this.bot.isMobile && newMobileData) ?
(newMobileData.pointProgressMax - newMobileData.pointProgress) :
(newEdgeData.pointProgressMax - newEdgeData.pointProgress) + (newGenericData.pointProgressMax - newGenericData.pointProgress)
searchCounters = await this.bingSearch(page, query)
const newMissingPoints = this.calculatePoints(searchCounters)
// If the new point amount is the same as before
if (newMissingPoints == missingPoints) {
@@ -86,7 +59,7 @@ export class Search extends Workers {
missingPoints = newMissingPoints
if (missingPoints == 0) {
if (missingPoints === 0) {
break
}
@@ -113,21 +86,8 @@ export class Search extends Workers {
for (const term of relatedTerms.slice(1, 3)) {
this.bot.log('SEARCH-BING-EXTRA', `${missingPoints} Points Remaining | Query: ${term} | Mobile: ${this.bot.isMobile}`)
let newData = await this.bingSearch(page, page, query.topic)
let retries = 0
while ((!newData?.pcSearch || newData.pcSearch.length < 2) && retries < 3) {
newData = await this.bot.browser.func.getSearchPoints()
retries++
}
const newMobileData = newData.mobileSearch ? newData.mobileSearch[0] : null // Mobile searches
const newEdgeData = newData.pcSearch[1] as DashboardImpression // Edge searches
const newGenericData = newData.pcSearch[0] as DashboardImpression // Normal searches
const newMissingPoints = (this.bot.isMobile && newMobileData) ?
(newMobileData.pointProgressMax - newMobileData.pointProgress) :
(newEdgeData.pointProgressMax - newEdgeData.pointProgress) + (newGenericData.pointProgressMax - newGenericData.pointProgress)
searchCounters = await this.bingSearch(page, query.topic)
const newMissingPoints = this.calculatePoints(searchCounters)
// If the new point amount is the same as before
if (newMissingPoints == missingPoints) {
@@ -139,7 +99,7 @@ export class Search extends Workers {
missingPoints = newMissingPoints
// If we satisfied the searches
if (missingPoints == 0) {
if (missingPoints === 0) {
break
}
@@ -156,7 +116,7 @@ export class Search extends Workers {
this.bot.log('SEARCH-BING', 'Completed searches')
}
private async bingSearch(page: Page, searchPage: Page, query: string) {
private async bingSearch(searchPage: Page, query: string) {
// Try a max of 5 times
for (let i = 0; i < 5; i++) {
try {
@@ -300,37 +260,18 @@ export class Search extends Workers {
// Check if the URL is different from the original one, don't loop more than 5 times.
let i = 0
while (lastTabURL.href !== searchListingURL.href && i < 5) {
// If hostname is still bing, (Bing images/news etc)
if (lastTabURL.hostname === searchListingURL.hostname) {
await lastTab.goBack()
const browser = page.browser()
const tabs = await browser.pages()
// Refresh
lastTab = await this.bot.browser.utils.getLatestTab(page)
lastTabURL = new URL(lastTab.url())
// If "goBack" didn't return to search listing (due to redirects)
if (lastTabURL.hostname !== searchListingURL.hostname) {
await lastTab.goto(this.searchPageURL)
}
} else { // No longer on bing, likely opened a new tab, close this tab
const tabs = await (page.browser()).pages()
// If the browser has more than 3 tabs open, it has opened a new one, we need to close this one.
if (tabs.length > 3) {
if (tabs.length === 4) {
await lastTab.close()
} else if (lastTabURL.href !== searchListingURL.href) {
await lastTab.goBack()
// Refresh
lastTab = await this.bot.browser.utils.getLatestTab(page)
lastTabURL = new URL(lastTab.url())
} else if (tabs.length === 2) {
const newPage = await browser.newPage()
await newPage.goto(searchListingURL.href)
// If "goBack" didn't return to search listing (due to redirects)
if (lastTabURL.hostname !== searchListingURL.hostname) {
await lastTab.goto(this.searchPageURL)
}
}
} else {
await lastTab.goBack()
}
// End of loop, refresh lastPage
@@ -344,4 +285,16 @@ export class Search extends Workers {
}
}
private calculatePoints(counters: Counters) {
const mobileData = counters.mobileSearch?.[0] // Mobile searches
const genericData = counters.pcSearch?.[0] // Normal searches
const edgeData = counters.pcSearch?.[1] // Edge searches
const missingPoints = (this.bot.isMobile && mobileData)
? mobileData.pointProgressMax - mobileData.pointProgress
: (edgeData ? edgeData.pointProgressMax - edgeData.pointProgress : 0)
+ (genericData ? genericData.pointProgressMax - genericData.pointProgress : 0)
return missingPoints
}
}

View File

@@ -9,7 +9,8 @@ export class UrlReward extends Workers {
this.bot.log('URL-REWARD', 'Trying to complete UrlReward')
try {
// After waiting, close the page
this.bot.utils.wait(2000)
await page.close()
this.bot.log('URL-REWARD', 'Completed the UrlReward successfully')

View File

@@ -230,6 +230,20 @@ export class MicrosoftRewardsBot {
// Do mobile searches
if (this.config.workers.doMobileSearch) {
await this.activities.doSearch(workerPage, data)
// Fetch current search points
const mobileSearchPoints = (await this.browser.func.getSearchPoints()).mobileSearch?.[0]
// If the remaining mobile points does not equal 0, restart and assume the generated UA is invalid
if (mobileSearchPoints && ((mobileSearchPoints.pointProgressMax - mobileSearchPoints.pointProgress) > 0)) {
log('MAIN', 'Unable to complete mobile searches, bad User-Agent?, retrying...')
// Close mobile browser
await browser.close()
// Retry
await this.Mobile(account)
}
}
// Fetch new points