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 5. Run `npm run build` to build the script
6. Run `npm run start` to start the built 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 ## ## Features ##
- [x] Multi-Account Support - [x] Multi-Account Support
- [x] Session Storing - [x] Session Storing

View File

@@ -1,6 +1,6 @@
{ {
"name": "microsoft-rewards-script", "name": "microsoft-rewards-script",
"version": "1.2.5", "version": "1.3.0",
"description": "Automatically do tasks for Microsoft Rewards but in TS!", "description": "Automatically do tasks for Microsoft Rewards but in TS!",
"main": "index.js", "main": "index.js",
"engines": { "engines": {
@@ -10,7 +10,8 @@
"build": "tsc", "build": "tsc",
"start": "node ./dist/index.js", "start": "node ./dist/index.js",
"ts-start": "ts-node ./src/index.ts", "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": [ "keywords": [
"Bing Rewards", "Bing Rewards",
@@ -30,11 +31,11 @@
"typescript": "^5.2.2" "typescript": "^5.2.2"
}, },
"dependencies": { "dependencies": {
"axios": "^1.6.1", "axios": "^1.6.2",
"cheerio": "^1.0.0-rc.12", "cheerio": "^1.0.0-rc.12",
"fingerprint-generator": "^2.1.43", "fingerprint-generator": "^2.1.45",
"fingerprint-injector": "^2.1.43", "fingerprint-injector": "^2.1.45",
"puppeteer": "^21.2.1", "puppeteer": "^21.5.2",
"ts-node": "^10.9.1" "ts-node": "^10.9.1"
} }
} }

View File

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

View File

@@ -27,6 +27,11 @@ export class Workers {
await this.solveActivities(page, activitiesUncompleted) 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') this.bot.log('DAILY-SET', 'All "Daily Set" items have been completed')
} }
@@ -41,24 +46,31 @@ export class Workers {
} }
for (const punchCard of punchCardsUncompleted) { 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 const activitiesUncompleted = punchCard.childPromotions.filter(x => !x.complete) // Only return uncompleted activities
// Solve Activities // Solve Activities
this.bot.log('PUNCH-CARD', `Started solving "Punch Card" items for punchcard: "${punchCard.parentPromotion.title}"`) 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 // Got to punch card index page in a new tab
await page.goto(punchCard.parentPromotion.destinationUrl, { referer: this.bot.config.baseURL }) 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 // 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) 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() 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`) this.bot.log('PUNCH-CARD', `All items for punchcard: "${punchCard.parentPromotion.title}" have been completed`)
} }
@@ -85,39 +97,55 @@ export class Workers {
// Solve Activities // Solve Activities
this.bot.log('MORE-PROMOTIONS', 'Started solving "More Promotions" item') this.bot.log('MORE-PROMOTIONS', 'Started solving "More Promotions" item')
page = await this.bot.browser.utils.getLatestTab(page)
await this.solveActivities(page, activitiesUncompleted) 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') this.bot.log('MORE-PROMOTIONS', 'All "More Promotion" items have been completed')
} }
// Solve all the different types of activities // Solve all the different types of activities
private async solveActivities(page: Page, activities: PromotionalItem[] | MorePromotion[], punchCard?: PunchCard) { private async solveActivities(activityPage: Page, activities: PromotionalItem[] | MorePromotion[], punchCard?: PunchCard) {
let activityPage = page const activityInitial = activityPage.url() // Homepage for Daily/More and Index for promotions
for (const activity of activities) { for (const activity of activities) {
try { 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}"]` let selector = `[data-bi-id="${activity.offerId}"]`
if (punchCard) { 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')) { } 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}"]` selector = `[data-bi-id="${activity.name}"]`
} }
}
// Click element, it will be opened in a new tab // Click element, it will be opened in a new tab
await page.click(selector) await activityPage.click(selector)
// Select the new activity page // 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. // Wait for the new tab to fully load, ignore error.
/* /*
@@ -175,12 +203,8 @@ export class Workers {
} catch (error) { } catch (error) {
this.bot.log('ACTIVITY', 'An error occurred:' + error, '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 { Workers } from '../Workers'
import { DashboardData, DashboardImpression } from '../../interface/DashboardData' import { Counters, DashboardData } from '../../interface/DashboardData'
import { GoogleTrends } from '../../interface/GoogleDailyTrends' import { GoogleTrends } from '../../interface/GoogleDailyTrends'
import { GoogleSearch } from '../../interface/Search' import { GoogleSearch } from '../../interface/Search'
@@ -15,21 +15,12 @@ export class Search extends Workers {
public async doSearch(page: Page, data: DashboardData) { public async doSearch(page: Page, data: DashboardData) {
this.bot.log('SEARCH-BING', 'Starting bing searches') this.bot.log('SEARCH-BING', 'Starting bing searches')
let retries = 0 page = await this.bot.browser.utils.getLatestTab(page)
while ((!data.userStatus.counters?.pcSearch || data.userStatus.counters.pcSearch.length < 2) && retries < 3) {
data = await this.bot.browser.func.getDashboardData()
retries++
}
const mobileData = data.userStatus.counters?.mobileSearch ? data.userStatus.counters.mobileSearch[0] : null // Mobile searches let searchCounters: Counters = await this.bot.browser.func.getSearchPoints()
const edgeData = data.userStatus.counters.pcSearch[1] as DashboardImpression // Edge searches let missingPoints = this.calculatePoints(searchCounters)
const genericData = data.userStatus.counters.pcSearch[0] as DashboardImpression // Normal searches
let missingPoints = (this.bot.isMobile && mobileData) ? if (missingPoints === 0) {
(mobileData.pointProgressMax - mobileData.pointProgress) :
(edgeData.pointProgressMax - edgeData.pointProgress) + (genericData.pointProgressMax - genericData.pointProgress)
if (missingPoints == 0) {
this.bot.log('SEARCH-BING', `Bing searches for ${this.bot.isMobile ? 'MOBILE' : 'DESKTOP'} have already been completed`) this.bot.log('SEARCH-BING', `Bing searches for ${this.bot.isMobile ? 'MOBILE' : 'DESKTOP'} have already been completed`)
return return
} }
@@ -41,7 +32,6 @@ export class Search extends Workers {
// Deduplicate the search terms // Deduplicate the search terms
googleSearchQueries = [...new Set(googleSearchQueries)] googleSearchQueries = [...new Set(googleSearchQueries)]
// Go to bing // Go to bing
await page.goto(this.searchPageURL) 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}`) this.bot.log('SEARCH-BING', `${missingPoints} Points Remaining | Query: ${query} | Mobile: ${this.bot.isMobile}`)
let newData = await this.bingSearch(page, page, query) searchCounters = await this.bingSearch(page, query)
const newMissingPoints = this.calculatePoints(searchCounters)
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)
// If the new point amount is the same as before // If the new point amount is the same as before
if (newMissingPoints == missingPoints) { if (newMissingPoints == missingPoints) {
@@ -86,7 +59,7 @@ export class Search extends Workers {
missingPoints = newMissingPoints missingPoints = newMissingPoints
if (missingPoints == 0) { if (missingPoints === 0) {
break break
} }
@@ -113,21 +86,8 @@ export class Search extends Workers {
for (const term of relatedTerms.slice(1, 3)) { for (const term of relatedTerms.slice(1, 3)) {
this.bot.log('SEARCH-BING-EXTRA', `${missingPoints} Points Remaining | Query: ${term} | Mobile: ${this.bot.isMobile}`) this.bot.log('SEARCH-BING-EXTRA', `${missingPoints} Points Remaining | Query: ${term} | Mobile: ${this.bot.isMobile}`)
let newData = await this.bingSearch(page, page, query.topic) searchCounters = await this.bingSearch(page, query.topic)
const newMissingPoints = this.calculatePoints(searchCounters)
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)
// If the new point amount is the same as before // If the new point amount is the same as before
if (newMissingPoints == missingPoints) { if (newMissingPoints == missingPoints) {
@@ -139,7 +99,7 @@ export class Search extends Workers {
missingPoints = newMissingPoints missingPoints = newMissingPoints
// If we satisfied the searches // If we satisfied the searches
if (missingPoints == 0) { if (missingPoints === 0) {
break break
} }
@@ -156,7 +116,7 @@ export class Search extends Workers {
this.bot.log('SEARCH-BING', 'Completed searches') 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 // Try a max of 5 times
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
try { 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. // Check if the URL is different from the original one, don't loop more than 5 times.
let i = 0 let i = 0
while (lastTabURL.href !== searchListingURL.href && i < 5) { while (lastTabURL.href !== searchListingURL.href && i < 5) {
// If hostname is still bing, (Bing images/news etc) const browser = page.browser()
if (lastTabURL.hostname === searchListingURL.hostname) { const tabs = await browser.pages()
await lastTab.goBack()
// Refresh if (tabs.length === 4) {
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) {
await lastTab.close() await lastTab.close()
} else if (lastTabURL.href !== searchListingURL.href) { } else if (tabs.length === 2) {
await lastTab.goBack() const newPage = await browser.newPage()
// Refresh await newPage.goto(searchListingURL.href)
lastTab = await this.bot.browser.utils.getLatestTab(page)
lastTabURL = new URL(lastTab.url())
// If "goBack" didn't return to search listing (due to redirects) } else {
if (lastTabURL.hostname !== searchListingURL.hostname) { await lastTab.goBack()
await lastTab.goto(this.searchPageURL)
}
}
} }
// End of loop, refresh lastPage // 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') this.bot.log('URL-REWARD', 'Trying to complete UrlReward')
try { try {
// After waiting, close the page this.bot.utils.wait(2000)
await page.close() await page.close()
this.bot.log('URL-REWARD', 'Completed the UrlReward successfully') this.bot.log('URL-REWARD', 'Completed the UrlReward successfully')

View File

@@ -230,6 +230,20 @@ export class MicrosoftRewardsBot {
// Do mobile searches // Do mobile searches
if (this.config.workers.doMobileSearch) { if (this.config.workers.doMobileSearch) {
await this.activities.doSearch(workerPage, data) 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 // Fetch new points