mirror of
https://github.com/TheNetsky/Microsoft-Rewards-Script.git
synced 2026-01-11 19:06:18 +00:00
1.1.0
This commit is contained in:
10
README.md
10
README.md
@@ -9,18 +9,24 @@ Under development, however mainly for personal use!
|
|||||||
3. Change `accounts.example.json` to `accounts.json` and add your account details
|
3. Change `accounts.example.json` to `accounts.json` and add your account details
|
||||||
4. Change `config.json` to your liking
|
4. Change `config.json` to your liking
|
||||||
5. Run `npm run build` to build the script
|
5. Run `npm run build` to build the script
|
||||||
6. Run `nom run start` to start the built script
|
6. Run `npm run start` to start the built script
|
||||||
|
|
||||||
## Features ##
|
## Features ##
|
||||||
|
- [x] Multi-Account Support
|
||||||
|
- [x] Session Storing
|
||||||
|
- [x] 2FA Support
|
||||||
|
- [x] Discord Webhook Support
|
||||||
- [x] Desktop Searches
|
- [x] Desktop Searches
|
||||||
|
- [x] Microsoft Edge Searches
|
||||||
- [x] Mobile Searches
|
- [x] Mobile Searches
|
||||||
|
- [x] Emulate scrolling and link clicking (Optional)
|
||||||
- [x] Completing Daily Set
|
- [x] Completing Daily Set
|
||||||
- [x] Completing More Promotions
|
- [x] Completing More Promotions
|
||||||
- [x] Solving Quiz (10 point variant)
|
- [x] Solving Quiz (10 point variant)
|
||||||
- [x] Solving Quiz (30-40 point variant)
|
- [x] Solving Quiz (30-40 point variant)
|
||||||
- [x] Completing Click Rewards
|
- [x] Completing Click Rewards
|
||||||
- [x] Completing Polls
|
- [x] Completing Polls
|
||||||
- [ ] Completing Punchcards
|
- [x] Completing Punchcards
|
||||||
- [ ] Solving This Or That Quiz
|
- [ ] Solving This Or That Quiz
|
||||||
- [x] Clicking Promotional Items
|
- [x] Clicking Promotional Items
|
||||||
- [x] Solving ABC Quiz
|
- [x] Solving ABC Quiz
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -1,12 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "microsoft-rewards-script",
|
"name": "microsoft-rewards-script",
|
||||||
"version": "1.0.7",
|
"version": "1.1.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": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"start": "node ./dist/index.js",
|
"start": "node ./dist/index.js",
|
||||||
"dev": "ts-node ./src/index.ts"
|
"dev": "ts-node ./src/index.ts -dev"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Bing Rewards",
|
"Bing Rewards",
|
||||||
@@ -31,4 +34,4 @@
|
|||||||
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
||||||
"ts-node": "^10.9.1"
|
"ts-node": "^10.9.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ import { tryDismissAllMessages, tryDismissCookieBanner } from './BrowserUtil'
|
|||||||
import { getFormattedDate, wait } from './../util/Utils'
|
import { getFormattedDate, wait } from './../util/Utils'
|
||||||
import { log } from './../util/Logger'
|
import { log } from './../util/Logger'
|
||||||
|
|
||||||
import { Counters, DashboardData } from './../interface/DashboardData'
|
import { Counters, DashboardData, MorePromotion, PromotionalItem } from './../interface/DashboardData'
|
||||||
import { QuizData } from './../interface/QuizData'
|
import { QuizData } from './../interface/QuizData'
|
||||||
|
|
||||||
import { baseURL, sessionPath } from './../config.json'
|
import { baseURL, sessionPath } from './../config.json'
|
||||||
@@ -195,7 +195,6 @@ export async function loadSesion(email: string): Promise<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return sessionDir
|
return sessionDir
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error as string)
|
throw new Error(error as string)
|
||||||
}
|
}
|
||||||
@@ -203,7 +202,9 @@ export async function loadSesion(email: string): Promise<string> {
|
|||||||
|
|
||||||
export async function waitForQuizRefresh(page: Page) {
|
export async function waitForQuizRefresh(page: Page) {
|
||||||
try {
|
try {
|
||||||
await page.waitForSelector('#rqHeaderCredits', { timeout: 5000 })
|
await page.waitForSelector('#rqHeaderCredits', { visible: true, timeout: 5000 })
|
||||||
|
await wait(2000)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log('QUIZ-REFRESH', 'An error occurred:' + error, 'error')
|
log('QUIZ-REFRESH', 'An error occurred:' + error, 'error')
|
||||||
@@ -213,7 +214,9 @@ export async function waitForQuizRefresh(page: Page) {
|
|||||||
|
|
||||||
export async function checkQuizCompleted(page: Page) {
|
export async function checkQuizCompleted(page: Page) {
|
||||||
try {
|
try {
|
||||||
await page.waitForSelector('#quizCompleteContainer', { timeout: 1000 })
|
await page.waitForSelector('#quizCompleteContainer', { visible: true, timeout: 1000 })
|
||||||
|
await wait(2000)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false
|
return false
|
||||||
@@ -225,4 +228,21 @@ export async function refreshCheerio(page: Page) {
|
|||||||
const $ = load(html)
|
const $ = load(html)
|
||||||
|
|
||||||
return $
|
return $
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPunchCardActivity(page: Page, activity: PromotionalItem | MorePromotion) {
|
||||||
|
let selector = ''
|
||||||
|
try {
|
||||||
|
const html = await page.content()
|
||||||
|
const $ = load(html)
|
||||||
|
|
||||||
|
const element = $('.offer-cta').toArray().find(x => x.attribs.href?.includes(activity.offerId))
|
||||||
|
if (element) {
|
||||||
|
selector = `a[href*="${element.attribs.href}"]`
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
log('GET-PUNCHCARD-ACTIVITY', 'An error occurred:' + error, 'error')
|
||||||
|
}
|
||||||
|
|
||||||
|
return selector
|
||||||
}
|
}
|
||||||
@@ -3,9 +3,14 @@
|
|||||||
"sessionPath": "sessions",
|
"sessionPath": "sessions",
|
||||||
"headless": false,
|
"headless": false,
|
||||||
"runOnZeroPoints": false,
|
"runOnZeroPoints": false,
|
||||||
"searches": {
|
"workers": {
|
||||||
"doMobile": true,
|
"doDailySet": true,
|
||||||
"doDesktop": true,
|
"doMorePromotions": true,
|
||||||
|
"doPunchCards": true,
|
||||||
|
"doDesktopSearch": true,
|
||||||
|
"doMobileSearch": true
|
||||||
|
},
|
||||||
|
"searchSettings": {
|
||||||
"scrollRandomResults": true,
|
"scrollRandomResults": true,
|
||||||
"clickRandomResults": true
|
"clickRandomResults": true
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,74 +6,33 @@ import { doUrlReward } from './activities/UrlReward'
|
|||||||
import { doThisOrThat } from './activities/ThisOrThat'
|
import { doThisOrThat } from './activities/ThisOrThat'
|
||||||
import { doABC } from './activities/ABC'
|
import { doABC } from './activities/ABC'
|
||||||
|
|
||||||
|
import { getPunchCardActivity } from '../browser/BrowserFunc'
|
||||||
|
import { getLatestTab } from '../browser/BrowserUtil'
|
||||||
|
|
||||||
import { getFormattedDate, wait } from '../util/Utils'
|
import { getFormattedDate, wait } from '../util/Utils'
|
||||||
import { log } from '../util/Logger'
|
import { log } from '../util/Logger'
|
||||||
|
|
||||||
import { DashboardData, MorePromotion } from '../interface/DashboardData'
|
import { DashboardData, MorePromotion, PromotionalItem, PunchCard } from '../interface/DashboardData'
|
||||||
|
|
||||||
|
|
||||||
// Daily Set
|
// Daily Set
|
||||||
export async function doDailySet(page: Page, data: DashboardData) {
|
export async function doDailySet(page: Page, data: DashboardData) {
|
||||||
|
|
||||||
const todayData = data.dailySetPromotions[getFormattedDate()]
|
const todayData = data.dailySetPromotions[getFormattedDate()]
|
||||||
|
|
||||||
const activitiesUncompleted = todayData?.filter(x => !x.complete) ?? []
|
const activitiesUncompleted = todayData?.filter(x => !x.complete && x.pointProgressMax > 0) ?? []
|
||||||
|
|
||||||
if (!activitiesUncompleted.length) {
|
if (!activitiesUncompleted.length) {
|
||||||
log('DAILY-SET', 'All daily set items have already been completed')
|
log('DAILY-SET', 'All Daily Set" items have already been completed')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const activity of activitiesUncompleted) {
|
// Solve Activities
|
||||||
log('DAILY-SET', 'Started doing daily set items')
|
log('DAILY-SET', 'Started solving "Daily Set" items')
|
||||||
|
|
||||||
// If activity does not give points, skip
|
await solveActivities(page, activitiesUncompleted)
|
||||||
if (activity.pointProgressMax <= 0) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (activity.promotionType) {
|
log('DAILY-SET', 'All "Daily Set" items have been completed')
|
||||||
// Quiz (Poll, Quiz or ABC)
|
|
||||||
case 'quiz':
|
|
||||||
|
|
||||||
switch (activity.pointProgressMax) {
|
|
||||||
// Poll or ABC (Usually 10 points)
|
|
||||||
case 10:
|
|
||||||
// Normal poll
|
|
||||||
if (activity.destinationUrl.toLowerCase().includes('pollscenarioid')) {
|
|
||||||
log('ACTIVITY', 'Found daily activity type: Poll')
|
|
||||||
await doPoll(page, activity)
|
|
||||||
} else { // ABC
|
|
||||||
log('ACTIVITY', 'Found daily activity type: ABC')
|
|
||||||
await doABC(page, activity)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
// This Or That Quiz (Usually 50 points)
|
|
||||||
case 50:
|
|
||||||
log('ACTIVITY', 'Found daily activity type: ThisOrThat')
|
|
||||||
await doThisOrThat(page, activity)
|
|
||||||
break
|
|
||||||
|
|
||||||
// Quizzes are usually 30-40 points
|
|
||||||
default:
|
|
||||||
log('ACTIVITY', 'Found daily activity type: Quiz')
|
|
||||||
await doQuiz(page, activity)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
// UrlReward (Visit)
|
|
||||||
case 'urlreward':
|
|
||||||
log('ACTIVITY', 'Found daily activity type: UrlReward')
|
|
||||||
await doUrlReward(page, activity)
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
await wait(1500)
|
|
||||||
}
|
|
||||||
|
|
||||||
log('DAILY-SET', 'Daily set items have been completed')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Punch Card
|
// Punch Card
|
||||||
@@ -82,66 +41,31 @@ export async function doPunchCard(page: Page, data: DashboardData) {
|
|||||||
const punchCardsUncompleted = data.punchCards?.filter(x => !x.parentPromotion.complete) ?? [] // Only return uncompleted punch cards
|
const punchCardsUncompleted = data.punchCards?.filter(x => !x.parentPromotion.complete) ?? [] // Only return uncompleted punch cards
|
||||||
|
|
||||||
if (!punchCardsUncompleted.length) {
|
if (!punchCardsUncompleted.length) {
|
||||||
log('PUNCH-CARD', 'All punch cards have already been completed')
|
log('PUNCH-CARD', 'All "Punch Cards" have already been completed')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const promotion of punchCardsUncompleted) {
|
for (const punchCard of punchCardsUncompleted) {
|
||||||
const activities = promotion.childPromotions.filter(x => !x.complete) // Only return uncompleted activities
|
const activitiesUncompleted = punchCard.childPromotions.filter(x => !x.complete) // Only return uncompleted activities
|
||||||
|
|
||||||
for (const activity of activities) {
|
// Solve Activities
|
||||||
log('PUNCH-CARD', 'Started doing daily set items')
|
log('PUNCH-CARD', `Started solving "Punch Card" items for punchcard: "${punchCard.parentPromotion.title}"`)
|
||||||
|
|
||||||
// If activity does not give points, skip
|
const browser = page.browser()
|
||||||
if (activity.pointProgressMax <= 0) {
|
page = await browser.newPage()
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (activity.promotionType) {
|
// Got to punch card index page in a new tab
|
||||||
// Quiz (Poll, Quiz or ABC)
|
await page.goto(punchCard.parentPromotion.destinationUrl, { referer: 'https://rewards.bing.com/' })
|
||||||
case 'quiz':
|
|
||||||
|
|
||||||
switch (activity.pointProgressMax) {
|
await solveActivities(page, activitiesUncompleted, punchCard)
|
||||||
// Poll or ABC (Usually 10 points)
|
|
||||||
case 10:
|
|
||||||
// Normal poll
|
|
||||||
if (activity.destinationUrl.toLowerCase().includes('pollscenarioid')) {
|
|
||||||
log('ACTIVITY', 'Found daily activity type: Poll')
|
|
||||||
await doPoll(page, activity)
|
|
||||||
} else { // ABC
|
|
||||||
log('ACTIVITY', 'Found daily activity type: ABC')
|
|
||||||
await doABC(page, activity)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
// This Or That Quiz (Usually 50 points)
|
// Close the punch card index page
|
||||||
case 50:
|
await page.close()
|
||||||
log('ACTIVITY', 'Found daily activity type: ThisOrThat')
|
|
||||||
await doThisOrThat(page, activity)
|
|
||||||
break
|
|
||||||
|
|
||||||
// Quizzes are usually 30-40 points
|
log('PUNCH-CARD', `All items for punchcard: "${punchCard.parentPromotion.title}" have been completed`)
|
||||||
default:
|
|
||||||
log('ACTIVITY', 'Found daily activity type: Quiz')
|
|
||||||
await doQuiz(page, activity)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
// UrlReward (Visit)
|
|
||||||
case 'urlreward':
|
|
||||||
log('ACTIVITY', 'Found daily activity type: UrlReward')
|
|
||||||
await doUrlReward(page, activity)
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
await wait(1500)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log('PUNCH-CARD', 'Punch card items have been completed')
|
log('PUNCH-CARD', 'All "Punch Card" items have been completed')
|
||||||
}
|
}
|
||||||
|
|
||||||
// More Promotions
|
// More Promotions
|
||||||
@@ -153,59 +77,87 @@ export async function doMorePromotions(page: Page, data: DashboardData) {
|
|||||||
morePromotions.push(data.promotionalItem as unknown as MorePromotion)
|
morePromotions.push(data.promotionalItem as unknown as MorePromotion)
|
||||||
}
|
}
|
||||||
|
|
||||||
const activitiesUncompleted = morePromotions?.filter(x => !x.complete) ?? []
|
const activitiesUncompleted = morePromotions?.filter(x => !x.complete && x.pointProgressMax > 0) ?? []
|
||||||
|
|
||||||
if (!activitiesUncompleted.length) {
|
if (!activitiesUncompleted.length) {
|
||||||
log('MORE-PROMOTIONS', 'All more promotion items have already been completed')
|
log('MORE-PROMOTIONS', 'All "More Promotion" items have already been completed')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const activity of activitiesUncompleted) {
|
// Solve Activities
|
||||||
// If activity does not give points, skip
|
log('MORE-PROMOTIONS', 'Started solving "More Promotions" item')
|
||||||
if (activity.pointProgressMax <= 0) {
|
|
||||||
continue
|
await solveActivities(page, activitiesUncompleted)
|
||||||
|
|
||||||
|
log('MORE-PROMOTIONS', 'All "More Promotion" items have been completed')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Solve all the different types of activities
|
||||||
|
async function solveActivities(page: Page, activities: PromotionalItem[] | MorePromotion[], punchCard?: PunchCard) {
|
||||||
|
try {
|
||||||
|
for (const activity of activities) {
|
||||||
|
|
||||||
|
if (punchCard) {
|
||||||
|
const selector = await getPunchCardActivity(page, activity)
|
||||||
|
|
||||||
|
// Wait for page to load and click to load the activity in a new tab
|
||||||
|
await page.waitForSelector(selector, { timeout: 5000 })
|
||||||
|
await page.click(selector)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const selector = `[data-bi-id="${activity.offerId}"]`
|
||||||
|
|
||||||
|
// Wait for page to load and click to load the activity in a new tab
|
||||||
|
await page.waitForSelector(selector, { timeout: 5000 })
|
||||||
|
await page.click(selector)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select the new activity page
|
||||||
|
const activityPage = await getLatestTab(page)
|
||||||
|
|
||||||
|
switch (activity.promotionType) {
|
||||||
|
// Quiz (Poll, Quiz or ABC)
|
||||||
|
case 'quiz':
|
||||||
|
switch (activity.pointProgressMax) {
|
||||||
|
// Poll or ABC (Usually 10 points)
|
||||||
|
case 10:
|
||||||
|
// Normal poll
|
||||||
|
if (activity.destinationUrl.toLowerCase().includes('pollscenarioid')) {
|
||||||
|
log('ACTIVITY', `Found activity type: "Poll" title: "${activity.title}"`)
|
||||||
|
await doPoll(activityPage)
|
||||||
|
} else { // ABC
|
||||||
|
log('ACTIVITY', `Found activity type: "ABC" title: "${activity.title}"`)
|
||||||
|
await doABC(activityPage)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
// This Or That Quiz (Usually 50 points)
|
||||||
|
case 50:
|
||||||
|
log('ACTIVITY', `Found activity type: "ThisOrThat" title: "${activity.title}"`)
|
||||||
|
await doThisOrThat(activityPage, activity)
|
||||||
|
break
|
||||||
|
|
||||||
|
// Quizzes are usually 30-40 points
|
||||||
|
default:
|
||||||
|
log('ACTIVITY', `Found activity type: "Quiz" title: "${activity.title}"`)
|
||||||
|
await doQuiz(activityPage)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
// UrlReward (Visit)
|
||||||
|
case 'urlreward':
|
||||||
|
log('ACTIVITY', `Found activity type: "UrlReward" title: "${activity.title}"`)
|
||||||
|
await doUrlReward(activityPage)
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
await wait(1500)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (activity.promotionType) {
|
} catch (error) {
|
||||||
// Quiz (Poll, Quiz or ABC)
|
log('ACTIVITY', 'An error occurred:' + error, 'error')
|
||||||
case 'quiz':
|
|
||||||
|
|
||||||
switch (activity.pointProgressMax) {
|
|
||||||
// Poll or ABC (Usually 10 points)
|
|
||||||
case 10:
|
|
||||||
// Normal poll
|
|
||||||
if (activity.destinationUrl.toLowerCase().includes('pollscenarioid')) {
|
|
||||||
log('ACTIVITY', 'Found daily activity type: Poll')
|
|
||||||
await doPoll(page, activity)
|
|
||||||
} else { // ABC
|
|
||||||
log('ACTIVITY', 'Found daily activity type: ABC')
|
|
||||||
await doABC(page, activity)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
// This Or That Quiz (Usually 50 points)
|
|
||||||
case 50:
|
|
||||||
log('ACTIVITY', 'Found daily activity type: ThisOrThat')
|
|
||||||
await doThisOrThat(page, activity)
|
|
||||||
break
|
|
||||||
|
|
||||||
// Quizzes are usually 30-40 points
|
|
||||||
default:
|
|
||||||
log('ACTIVITY', 'Found promotion activity type: Quiz')
|
|
||||||
await doQuiz(page, activity)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
// UrlReward (Visit)
|
|
||||||
case 'urlreward':
|
|
||||||
log('ACTIVITY', 'Found promotion activity type: UrlReward')
|
|
||||||
await doUrlReward(page, activity)
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
await wait(1500)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,47 +5,38 @@ import { getLatestTab } from '../../browser/BrowserUtil'
|
|||||||
import { log } from '../../util/Logger'
|
import { log } from '../../util/Logger'
|
||||||
import { randomNumber, wait } from '../../util/Utils'
|
import { randomNumber, wait } from '../../util/Utils'
|
||||||
|
|
||||||
import { MorePromotion, PromotionalItem } from '../../interface/DashboardData'
|
export async function doABC(page: Page) {
|
||||||
|
|
||||||
export async function doABC(page: Page, data: PromotionalItem | MorePromotion) {
|
|
||||||
log('ABC', 'Trying to complete poll')
|
log('ABC', 'Trying to complete poll')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const selector = `[data-bi-id="${data.offerId}"]`
|
|
||||||
|
|
||||||
// Wait for page to load and click to load the quiz in a new tab
|
|
||||||
await page.waitForSelector(selector, { timeout: 5000 })
|
|
||||||
await page.click(selector)
|
|
||||||
|
|
||||||
let abcPage = await getLatestTab(page)
|
|
||||||
await wait(2000)
|
await wait(2000)
|
||||||
let $ = await refreshCheerio(abcPage)
|
let $ = await refreshCheerio(page)
|
||||||
|
|
||||||
// Don't loop more than 15 in case unable to solve, would lock otherwise
|
// Don't loop more than 15 in case unable to solve, would lock otherwise
|
||||||
const maxIterations = 15
|
const maxIterations = 15
|
||||||
let i
|
let i
|
||||||
for (i = 0; i < maxIterations && !$('span.rw_icon').length; i++) {
|
for (i = 0; i < maxIterations && !$('span.rw_icon').length; i++) {
|
||||||
await abcPage.waitForSelector('.wk_OptionClickClass', { visible: true, timeout: 5000 })
|
await page.waitForSelector('.wk_OptionClickClass', { visible: true, timeout: 5000 })
|
||||||
|
|
||||||
const answers = $('.wk_OptionClickClass')
|
const answers = $('.wk_OptionClickClass')
|
||||||
const answer = answers[randomNumber(0, 2)]?.attribs['id']
|
const answer = answers[randomNumber(0, 2)]?.attribs['id']
|
||||||
|
|
||||||
await abcPage.waitForSelector(`#${answer}`, { visible: true, timeout: 5000 })
|
await page.waitForSelector(`#${answer}`, { visible: true, timeout: 5000 })
|
||||||
|
|
||||||
await wait(2000)
|
await wait(2000)
|
||||||
await abcPage.click(`#${answer}`) // Click answer
|
await page.click(`#${answer}`) // Click answer
|
||||||
|
|
||||||
await wait(4000)
|
await wait(4000)
|
||||||
await abcPage.waitForSelector('div.wk_button', { visible: true, timeout: 5000 })
|
await page.waitForSelector('div.wk_button', { visible: true, timeout: 5000 })
|
||||||
await abcPage.click('div.wk_button') // Click next question button
|
await page.click('div.wk_button') // Click next question button
|
||||||
|
|
||||||
abcPage = await getLatestTab(abcPage)
|
page = await getLatestTab(page)
|
||||||
$ = await refreshCheerio(abcPage)
|
$ = await refreshCheerio(page)
|
||||||
await wait(1000)
|
await wait(1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
await wait(4000)
|
await wait(4000)
|
||||||
await abcPage.close()
|
await page.close()
|
||||||
|
|
||||||
if (i === maxIterations) {
|
if (i === maxIterations) {
|
||||||
log('ABC', 'Failed to solve quiz, exceeded max iterations of 15', 'warn')
|
log('ABC', 'Failed to solve quiz, exceeded max iterations of 15', 'warn')
|
||||||
@@ -54,8 +45,7 @@ export async function doABC(page: Page, data: PromotionalItem | MorePromotion) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const abcPage = await getLatestTab(page)
|
await page.close()
|
||||||
await abcPage.close()
|
|
||||||
log('ABC', 'An error occurred:' + error, 'error')
|
log('ABC', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,26 @@
|
|||||||
import { Page } from 'puppeteer'
|
import { Page } from 'puppeteer'
|
||||||
|
|
||||||
import { getLatestTab } from '../../browser/BrowserUtil'
|
|
||||||
import { log } from '../../util/Logger'
|
import { log } from '../../util/Logger'
|
||||||
import { randomNumber, wait } from '../../util/Utils'
|
import { randomNumber, wait } from '../../util/Utils'
|
||||||
|
|
||||||
import { MorePromotion, PromotionalItem } from '../../interface/DashboardData'
|
export async function doPoll(page: Page) {
|
||||||
|
|
||||||
export async function doPoll(page: Page, data: PromotionalItem | MorePromotion) {
|
|
||||||
log('POLL', 'Trying to complete poll')
|
log('POLL', 'Trying to complete poll')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const selector = `[data-bi-id="${data.offerId}"]`
|
|
||||||
|
|
||||||
// Wait for page to load and click to load the quiz in a new tab
|
|
||||||
await page.waitForSelector(selector, { timeout: 5000 })
|
|
||||||
await page.click(selector)
|
|
||||||
|
|
||||||
const pollPage = await getLatestTab(page)
|
|
||||||
|
|
||||||
const buttonId = `#btoption${Math.floor(randomNumber(0, 1))}`
|
const buttonId = `#btoption${Math.floor(randomNumber(0, 1))}`
|
||||||
|
|
||||||
await pollPage.waitForNetworkIdle({ timeout: 5000 })
|
await page.waitForNetworkIdle({ timeout: 5000 })
|
||||||
await pollPage.waitForSelector(buttonId, { visible: true, timeout: 5000 })
|
await page.waitForSelector(buttonId, { visible: true, timeout: 5000 })
|
||||||
await wait(2000)
|
await wait(2000)
|
||||||
|
|
||||||
await pollPage.click(buttonId)
|
await page.click(buttonId)
|
||||||
|
|
||||||
await wait(4000)
|
await wait(4000)
|
||||||
await pollPage.close()
|
await page.close()
|
||||||
|
|
||||||
log('POLL', 'Completed the poll successfully')
|
log('POLL', 'Completed the poll successfully')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const pollPage = await getLatestTab(page)
|
await page.close()
|
||||||
await pollPage.close()
|
|
||||||
log('POLL', 'An error occurred:' + error, 'error')
|
log('POLL', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,37 +1,27 @@
|
|||||||
import { Page } from 'puppeteer'
|
import { Page } from 'puppeteer'
|
||||||
|
|
||||||
import { getLatestTab } from '../../browser/BrowserUtil'
|
|
||||||
import { getQuizData, waitForQuizRefresh } from '../../browser/BrowserFunc'
|
import { getQuizData, waitForQuizRefresh } from '../../browser/BrowserFunc'
|
||||||
import { wait } from '../../util/Utils'
|
import { wait } from '../../util/Utils'
|
||||||
import { log } from '../../util/Logger'
|
import { log } from '../../util/Logger'
|
||||||
|
|
||||||
import { MorePromotion, PromotionalItem } from '../../interface/DashboardData'
|
export async function doQuiz(page: Page) {
|
||||||
|
|
||||||
export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion) {
|
|
||||||
log('QUIZ', 'Trying to complete quiz')
|
log('QUIZ', 'Trying to complete quiz')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const selector = `[data-bi-id="${data.offerId}"]`
|
await page.waitForNetworkIdle({ timeout: 5000 })
|
||||||
|
|
||||||
// Wait for page to load and click to load the quiz in a new tab
|
|
||||||
await page.waitForSelector(selector, { timeout: 5000 })
|
|
||||||
await page.click(selector)
|
|
||||||
|
|
||||||
const quizPage = await getLatestTab(page)
|
|
||||||
await quizPage.waitForNetworkIdle({ timeout: 5000 })
|
|
||||||
await wait(2000)
|
await wait(2000)
|
||||||
|
|
||||||
// Check if the quiz has been started or not
|
// Check if the quiz has been started or not
|
||||||
const quizNotStarted = await quizPage.waitForSelector('#rqStartQuiz', { visible: true, timeout: 3000 }).then(() => true).catch(() => false)
|
const quizNotStarted = await page.waitForSelector('#rqStartQuiz', { visible: true, timeout: 3000 }).then(() => true).catch(() => false)
|
||||||
if (quizNotStarted) {
|
if (quizNotStarted) {
|
||||||
await quizPage.click('#rqStartQuiz')
|
await page.click('#rqStartQuiz')
|
||||||
} else {
|
} else {
|
||||||
log('QUIZ', 'Quiz has already been started, trying to finish it')
|
log('QUIZ', 'Quiz has already been started, trying to finish it')
|
||||||
}
|
}
|
||||||
|
|
||||||
await wait(2000)
|
await wait(2000)
|
||||||
|
|
||||||
let quizData = await getQuizData(quizPage)
|
let quizData = await getQuizData(page)
|
||||||
const questionsRemaining = quizData.maxQuestions - quizData.CorrectlyAnsweredQuestionCount // Amount of questions remaining
|
const questionsRemaining = quizData.maxQuestions - quizData.CorrectlyAnsweredQuestionCount // Amount of questions remaining
|
||||||
|
|
||||||
// All questions
|
// All questions
|
||||||
@@ -41,7 +31,7 @@ export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion)
|
|||||||
const answers: string[] = []
|
const answers: string[] = []
|
||||||
|
|
||||||
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
||||||
const answerSelector = await quizPage.waitForSelector(`#rqAnswerOption${i}`, { visible: true, timeout: 5000 })
|
const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { visible: true, timeout: 5000 })
|
||||||
const answerAttribute = await answerSelector?.evaluate(el => el.getAttribute('iscorrectoption'))
|
const answerAttribute = await answerSelector?.evaluate(el => el.getAttribute('iscorrectoption'))
|
||||||
|
|
||||||
if (answerAttribute && answerAttribute.toLowerCase() === 'true') {
|
if (answerAttribute && answerAttribute.toLowerCase() === 'true') {
|
||||||
@@ -51,14 +41,14 @@ export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion)
|
|||||||
|
|
||||||
// Click the answers
|
// Click the answers
|
||||||
for (const answer of answers) {
|
for (const answer of answers) {
|
||||||
await quizPage.waitForSelector(answer, { visible: true, timeout: 2000 })
|
await page.waitForSelector(answer, { visible: true, timeout: 2000 })
|
||||||
|
|
||||||
// Click the answer on page
|
// Click the answer on page
|
||||||
await quizPage.click(answer)
|
await page.click(answer)
|
||||||
|
|
||||||
const refreshSuccess = await waitForQuizRefresh(quizPage)
|
const refreshSuccess = await waitForQuizRefresh(page)
|
||||||
if (!refreshSuccess) {
|
if (!refreshSuccess) {
|
||||||
await quizPage.close()
|
await page.close()
|
||||||
log('QUIZ', 'An error occurred, refresh was unsuccessful', 'error')
|
log('QUIZ', 'An error occurred, refresh was unsuccessful', 'error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -66,21 +56,21 @@ export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion)
|
|||||||
|
|
||||||
// Other type quiz
|
// Other type quiz
|
||||||
} else if ([2, 3, 4].includes(quizData.numberOfOptions)) {
|
} else if ([2, 3, 4].includes(quizData.numberOfOptions)) {
|
||||||
quizData = await getQuizData(quizPage) // Refresh Quiz Data
|
quizData = await getQuizData(page) // Refresh Quiz Data
|
||||||
const correctOption = quizData.correctAnswer
|
const correctOption = quizData.correctAnswer
|
||||||
|
|
||||||
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
||||||
|
|
||||||
const answerSelector = await quizPage.waitForSelector(`#rqAnswerOption${i}`, { visible: true, timeout: 5000 })
|
const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { visible: true, timeout: 5000 })
|
||||||
const dataOption = await answerSelector?.evaluate(el => el.getAttribute('data-option'))
|
const dataOption = await answerSelector?.evaluate(el => el.getAttribute('data-option'))
|
||||||
|
|
||||||
if (dataOption === correctOption) {
|
if (dataOption === correctOption) {
|
||||||
// Click the answer on page
|
// Click the answer on page
|
||||||
await quizPage.click(`#rqAnswerOption${i}`)
|
await page.click(`#rqAnswerOption${i}`)
|
||||||
|
|
||||||
const refreshSuccess = await waitForQuizRefresh(quizPage)
|
const refreshSuccess = await waitForQuizRefresh(page)
|
||||||
if (!refreshSuccess) {
|
if (!refreshSuccess) {
|
||||||
await quizPage.close()
|
await page.close()
|
||||||
log('QUIZ', 'An error occurred, refresh was unsuccessful', 'error')
|
log('QUIZ', 'An error occurred, refresh was unsuccessful', 'error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -94,11 +84,10 @@ export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion)
|
|||||||
|
|
||||||
// Done with
|
// Done with
|
||||||
await wait(2000)
|
await wait(2000)
|
||||||
await quizPage.close()
|
await page.close()
|
||||||
log('QUIZ', 'Completed the quiz successfully')
|
log('QUIZ', 'Completed the quiz successfully')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const quizPage = await getLatestTab(page)
|
await page.close()
|
||||||
await quizPage.close()
|
|
||||||
log('QUIZ', 'An error occurred:' + error, 'error')
|
log('QUIZ', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { getSearchPoints } from '../../browser/BrowserFunc'
|
|||||||
import { log } from '../../util/Logger'
|
import { log } from '../../util/Logger'
|
||||||
import { randomNumber, shuffleArray, wait } from '../../util/Utils'
|
import { randomNumber, shuffleArray, wait } from '../../util/Utils'
|
||||||
|
|
||||||
import { searches } from '../../config.json'
|
import { searchSettings } from '../../config.json'
|
||||||
|
|
||||||
import { DashboardData, DashboardImpression } from '../../interface/DashboardData'
|
import { DashboardData, DashboardImpression } from '../../interface/DashboardData'
|
||||||
import { GoogleTrends } from '../../interface/GoogleDailyTrends'
|
import { GoogleTrends } from '../../interface/GoogleDailyTrends'
|
||||||
@@ -153,12 +153,12 @@ async function bingSearch(page: Page, searchPage: Page, query: string) {
|
|||||||
await searchPage.keyboard.type(query)
|
await searchPage.keyboard.type(query)
|
||||||
await searchPage.keyboard.press('Enter')
|
await searchPage.keyboard.press('Enter')
|
||||||
|
|
||||||
if (searches.scrollRandomResults) {
|
if (searchSettings.scrollRandomResults) {
|
||||||
await wait(2000)
|
await wait(2000)
|
||||||
await randomScroll(searchPage)
|
await randomScroll(searchPage)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searches.clickRandomResults) {
|
if (searchSettings.clickRandomResults) {
|
||||||
await wait(2000)
|
await wait(2000)
|
||||||
await clickRandomLink(searchPage)
|
await clickRandomLink(searchPage)
|
||||||
}
|
}
|
||||||
@@ -266,12 +266,15 @@ async function clickRandomLink(page: Page) {
|
|||||||
|
|
||||||
await page.click('#b_results .b_algo h2').catch(() => { }) // Since we don't really care if it did it or not
|
await page.click('#b_results .b_algo h2').catch(() => { }) // Since we don't really care if it did it or not
|
||||||
|
|
||||||
|
// Wait for website to load
|
||||||
await wait(3000)
|
await wait(3000)
|
||||||
|
|
||||||
let lastTab = await getLatestTab(page) // Will get current tab if no new one is created
|
// Will get current tab if no new one is created
|
||||||
await lastTab.waitForNetworkIdle() // Wait for page to load
|
let lastTab = await getLatestTab(page)
|
||||||
|
|
||||||
|
// Wait for website to finish loading, don't break loop however
|
||||||
|
await lastTab.waitForNetworkIdle({ idleTime: 1000, timeout: 5000 }).catch(() => { })
|
||||||
|
|
||||||
// Check if the tab is closed or not
|
// Check if the tab is closed or not
|
||||||
if (!lastTab.isClosed()) {
|
if (!lastTab.isClosed()) {
|
||||||
let lastTabURL = new URL(lastTab.url()) // Get new tab info
|
let lastTabURL = new URL(lastTab.url()) // Get new tab info
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ export async function doThisOrThat(page: Page, data: PromotionalItem | MorePromo
|
|||||||
|
|
||||||
log('THIS-OR-THAT', 'Completed the ThisOrthat successfully')
|
log('THIS-OR-THAT', 'Completed the ThisOrthat successfully')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const thisorthatPage = await getLatestTab(page)
|
await page.close()
|
||||||
await thisorthatPage.close()
|
|
||||||
log('THIS-OR-THAT', 'An error occurred:' + error, 'error')
|
log('THIS-OR-THAT', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,20 @@
|
|||||||
import { Page } from 'puppeteer'
|
import { Page } from 'puppeteer'
|
||||||
|
|
||||||
import { getLatestTab } from '../../browser/BrowserUtil'
|
import { wait } from '../../util/Utils'
|
||||||
import { log } from '../../util/Logger'
|
import { log } from '../../util/Logger'
|
||||||
|
|
||||||
import { PromotionalItem, MorePromotion } from '../../interface/DashboardData'
|
export async function doUrlReward(page: Page) {
|
||||||
|
|
||||||
export async function doUrlReward(page: Page, data: PromotionalItem | MorePromotion) {
|
|
||||||
log('URL-REWARD', 'Trying to complete UrlReward')
|
log('URL-REWARD', 'Trying to complete UrlReward')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const selector = `[data-bi-id="${data.offerId}"]`
|
|
||||||
|
|
||||||
// Wait for page to load and click to load the url reward in a new tab
|
|
||||||
await page.waitForSelector(selector, { timeout: 5000 })
|
|
||||||
await page.click(selector)
|
|
||||||
|
|
||||||
// After waiting, close the page
|
// After waiting, close the page
|
||||||
const visitPage = await getLatestTab(page)
|
await page.waitForNetworkIdle({ timeout: 10_000 })
|
||||||
await visitPage.waitForNetworkIdle({ timeout: 10_000 })
|
await wait(2000)
|
||||||
await visitPage.close()
|
await page.close()
|
||||||
|
|
||||||
log('URL-REWARD', 'Completed the UrlReward successfully')
|
log('URL-REWARD', 'Completed the UrlReward successfully')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const visitPage = await getLatestTab(page)
|
await page.close()
|
||||||
await visitPage.close()
|
|
||||||
log('URL-REWARD', 'An error occurred:' + error, 'error')
|
log('URL-REWARD', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
25
src/index.ts
25
src/index.ts
@@ -1,15 +1,15 @@
|
|||||||
import Browser from './browser/Browser'
|
import Browser from './browser/Browser'
|
||||||
import { getDashboardData, getEarnablePoints, goHome } from './browser/BrowserFunc'
|
import { getDashboardData, getEarnablePoints, goHome } from './browser/BrowserFunc'
|
||||||
import { log } from './util/Logger'
|
import { log } from './util/Logger'
|
||||||
|
import { loadAccounts } from './util/Account'
|
||||||
|
|
||||||
import { login } from './functions/Login'
|
import { login } from './functions/Login'
|
||||||
import { doDailySet, doMorePromotions } from './functions/Workers'
|
import { doDailySet, doMorePromotions, doPunchCard } from './functions/Workers'
|
||||||
import { doSearch } from './functions/activities/Search'
|
import { doSearch } from './functions/activities/Search'
|
||||||
|
|
||||||
import { Account } from './interface/Account'
|
import { Account } from './interface/Account'
|
||||||
|
|
||||||
import accounts from './accounts.json'
|
import { runOnZeroPoints, workers } from './config.json'
|
||||||
import { runOnZeroPoints, searches } from './config.json'
|
|
||||||
|
|
||||||
// Main bot class
|
// Main bot class
|
||||||
class MicrosoftRewardsBot {
|
class MicrosoftRewardsBot {
|
||||||
@@ -19,6 +19,8 @@ class MicrosoftRewardsBot {
|
|||||||
async run() {
|
async run() {
|
||||||
log('MAIN', 'Bot started')
|
log('MAIN', 'Bot started')
|
||||||
|
|
||||||
|
const accounts = await loadAccounts()
|
||||||
|
|
||||||
for (const account of accounts) {
|
for (const account of accounts) {
|
||||||
log('MAIN', `Started tasks for account ${account.email}`)
|
log('MAIN', `Started tasks for account ${account.email}`)
|
||||||
|
|
||||||
@@ -74,13 +76,22 @@ class MicrosoftRewardsBot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Complete daily set
|
// Complete daily set
|
||||||
await doDailySet(page, data)
|
if (workers.doDailySet) {
|
||||||
|
await doDailySet(page, data)
|
||||||
|
}
|
||||||
|
|
||||||
// Complete more promotions
|
// Complete more promotions
|
||||||
await doMorePromotions(page, data)
|
if (workers.doMorePromotions) {
|
||||||
|
await doMorePromotions(page, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complete punch cards
|
||||||
|
if (workers.doPunchCards) {
|
||||||
|
await doPunchCard(page, data)
|
||||||
|
}
|
||||||
|
|
||||||
// Do desktop searches
|
// Do desktop searches
|
||||||
if (searches.doDesktop) {
|
if (workers.doDesktopSearch) {
|
||||||
await doSearch(page, data, false)
|
await doSearch(page, data, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +121,7 @@ class MicrosoftRewardsBot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Do mobile searches
|
// Do mobile searches
|
||||||
if (searches.doMobile) {
|
if (workers.doMobileSearch) {
|
||||||
await doSearch(page, data, true)
|
await doSearch(page, data, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
20
src/util/Account.ts
Normal file
20
src/util/Account.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import * as fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
export async function loadAccounts() {
|
||||||
|
try {
|
||||||
|
let file = 'accounts.json'
|
||||||
|
|
||||||
|
// If dev mode, use dev account(s)
|
||||||
|
if (process.argv.includes('-dev')) {
|
||||||
|
file = 'accounts.dev.json'
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountDir = path.join(__dirname, '../', file)
|
||||||
|
const accounts = fs.readFileSync(accountDir, 'utf-8')
|
||||||
|
|
||||||
|
return JSON.parse(accounts)
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error as string)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user