From 03ba5129c6016692e0f90303fc05635e2c8ed8d5 Mon Sep 17 00:00:00 2001 From: TheNetsky <56271887+TheNetsky@users.noreply.github.com> Date: Sun, 8 Oct 2023 13:55:36 +0200 Subject: [PATCH] 1.0.7 --- .gitignore | 1 + README.md | 1 + package.json | 2 +- src/browser/Browser.ts | 45 ++---- src/browser/BrowserFunc.ts | 7 + src/functions/DailySet.ts | 69 -------- src/functions/MorePromotions.ts | 70 -------- src/functions/PunchCard.ts | 72 --------- src/functions/Workers.ts | 211 +++++++++++++++++++++++++ src/functions/activities/ABC.ts | 54 +++++++ src/functions/activities/Poll.ts | 5 +- src/functions/activities/Quiz.ts | 1 + src/functions/activities/Search.ts | 60 ++++--- src/functions/activities/ThisOrThat.ts | 1 + src/functions/activities/UrlReward.ts | 2 +- src/index.ts | 163 +++++++++---------- 16 files changed, 419 insertions(+), 345 deletions(-) delete mode 100644 src/functions/DailySet.ts delete mode 100644 src/functions/MorePromotions.ts delete mode 100644 src/functions/PunchCard.ts create mode 100644 src/functions/Workers.ts create mode 100644 src/functions/activities/ABC.ts diff --git a/.gitignore b/.gitignore index 0ea3667..7ff9e8e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ package-lock.json accounts.json notes accounts.dev.json +accounts.main.json \ No newline at end of file diff --git a/README.md b/README.md index 9659eb4..d78cb03 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Under development, however mainly for personal use! - [ ] Completing Punchcards - [ ] Solving This Or That Quiz - [x] Clicking Promotional Items +- [x] Solving ABC quiz - [ ] Completing Shop And Earn - [ ] Proxy Support diff --git a/package.json b/package.json index 53d3fd0..39117dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "microsoft-rewards-script", - "version": "1.0.6", + "version": "1.0.7", "description": "Automatically do tasks for Microsoft Rewards but in TS", "main": "index.js", "scripts": { diff --git a/src/browser/Browser.ts b/src/browser/Browser.ts index 653703e..3ffbbfa 100644 --- a/src/browser/Browser.ts +++ b/src/browser/Browser.ts @@ -8,35 +8,24 @@ import { headless } from '../config.json' puppeteer.use(StealthPlugin()) -export async function Browser(email: string) { - const userAgent = await getUserAgent(false) +class Browser { - const browser = await puppeteer.launch({ - headless: headless, - userDataDir: await loadSesion(email), - args: [ - '--no-sandbox', - '--disable-setuid-sandbox', - `--user-agent=${userAgent.userAgent}` - ] - }) + async createBrowser(email: string, isMobile: boolean) { + const userAgent = await getUserAgent(isMobile) - return browser + const browser = await puppeteer.launch({ + headless: headless, + userDataDir: await loadSesion(email), + args: [ + '--no-sandbox', + '--disable-setuid-sandbox', + `--user-agent=${userAgent.userAgent}`, + isMobile ? '--window-size=568,1024' : '' + ] + }) + + return browser + } } -export async function mobileBrowser(email: string) { - const userAgent = await getUserAgent(true) - - const browser = await puppeteer.launch({ - headless: headless, - userDataDir: await loadSesion(email), - args: [ - '--no-sandbox', - '--disable-setuid-sandbox', - `--user-agent=${userAgent.userAgent}`, - '--window-size=568,1024' - ] - }) - - return browser -} \ No newline at end of file +export default Browser \ No newline at end of file diff --git a/src/browser/BrowserFunc.ts b/src/browser/BrowserFunc.ts index 1428c78..967b36b 100644 --- a/src/browser/BrowserFunc.ts +++ b/src/browser/BrowserFunc.ts @@ -218,4 +218,11 @@ export async function checkQuizCompleted(page: Page) { } catch (error) { return false } +} + +export async function refreshCheerio(page: Page) { + const html = await page.content() + const $ = load(html) + + return $ } \ No newline at end of file diff --git a/src/functions/DailySet.ts b/src/functions/DailySet.ts deleted file mode 100644 index fba5364..0000000 --- a/src/functions/DailySet.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Page } from 'puppeteer' - -import { doPoll } from './activities/Poll' -import { doQuiz } from './activities/Quiz' -import { doUrlReward } from './activities/UrlReward' -import { doThisOrThat } from './activities/ThisOrThat' - -import { getFormattedDate, wait } from '../util/Utils' -import { log } from '../util/Logger' - -import { DashboardData } from '../interface/DashboardData' - -export async function doDailySet(page: Page, data: DashboardData) { - const todayData = data.dailySetPromotions[getFormattedDate()] - - const activitiesUncompleted = todayData?.filter(x => !x.complete) ?? [] - - if (!activitiesUncompleted.length) { - log('DAILY-SET', 'All daily set items have already been completed') - return - } - - for (const activity of activitiesUncompleted) { - log('DAILY-SET', 'Started doing daily set items') - - // If activity does not give points, skip - if (activity.pointProgressMax <= 0) { - continue - } - - switch (activity.promotionType) { - // Quiz (Poll/Quiz) - case 'quiz': - - switch (activity.pointProgressMax) { - // Poll (Usually 10 points) - case 10: - log('ACTIVITY', 'Found daily activity type: Poll') - await doPoll(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') -} \ No newline at end of file diff --git a/src/functions/MorePromotions.ts b/src/functions/MorePromotions.ts deleted file mode 100644 index aee925e..0000000 --- a/src/functions/MorePromotions.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Page } from 'puppeteer' - -import { doPoll } from './activities/Poll' -import { doQuiz } from './activities/Quiz' -import { doUrlReward } from './activities/UrlReward' -import { doThisOrThat } from './activities/ThisOrThat' - -import { wait } from '../util/Utils' -import { log } from '../util/Logger' - -import { DashboardData, MorePromotion } from '../interface/DashboardData' - -export async function doMorePromotions(page: Page, data: DashboardData) { - const morePromotions = data.morePromotions - - // Check if there is a promotional item - if (data.promotionalItem) { // Convert and add the promotional item to the array - morePromotions.push(data.promotionalItem as unknown as MorePromotion) - } - - const activitiesUncompleted = morePromotions?.filter(x => !x.complete) ?? [] - - if (!activitiesUncompleted.length) { - log('MORE-PROMOTIONS', 'All more promotion items have already been completed') - return - } - - for (const activity of activitiesUncompleted) { - // If activity does not give points, skip - if (activity.pointProgressMax <= 0) { - continue - } - - switch (activity.promotionType) { - // Quiz (Poll/Quiz) - case 'quiz': - - switch (activity.pointProgressMax) { - // Poll (Usually 10 points) - case 10: - log('ACTIVITY', 'Found promotion activity type: Poll') - await doPoll(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) - } -} \ No newline at end of file diff --git a/src/functions/PunchCard.ts b/src/functions/PunchCard.ts deleted file mode 100644 index 80b4c1d..0000000 --- a/src/functions/PunchCard.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Page } from 'puppeteer' - -import { doPoll } from './activities/Poll' -import { doQuiz } from './activities/Quiz' -import { doUrlReward } from './activities/UrlReward' -import { doThisOrThat } from './activities/ThisOrThat' - -import { wait } from '../util/Utils' -import { log } from '../util/Logger' - -import { DashboardData } from '../interface/DashboardData' - -export async function doPunchCard(page: Page, data: DashboardData) { - - const punchCardsUncompleted = data.punchCards?.filter(x => !x.parentPromotion.complete) ?? [] // filter out the uncompleted punch cards - - if (!punchCardsUncompleted.length) { - log('PUNCH-CARD', 'All punch cards have already been completed') - return - } - - // Todo - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const activitiesUncompleted: any = '' - - for (const activity of activitiesUncompleted) { - log('PUNCH-CARD', 'Started doing daily set items') - - // If activity does not give points, skip - if (activity.pointProgressMax <= 0) { - continue - } - - switch (activity.promotionType) { - // Quiz (Poll/Quiz) - case 'quiz': - - switch (activity.pointProgressMax) { - // Poll (Usually 10 points) - case 10: - log('ACTIVITY', 'Found daily activity type: Poll') - await doPoll(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('PUNCH-CARD', 'Punch card items have been completed') -} \ No newline at end of file diff --git a/src/functions/Workers.ts b/src/functions/Workers.ts new file mode 100644 index 0000000..002ddbb --- /dev/null +++ b/src/functions/Workers.ts @@ -0,0 +1,211 @@ +import { Page } from 'puppeteer' + +import { doPoll } from './activities/Poll' +import { doQuiz } from './activities/Quiz' +import { doUrlReward } from './activities/UrlReward' +import { doThisOrThat } from './activities/ThisOrThat' +import { doABC } from './activities/ABC' + +import { getFormattedDate, wait } from '../util/Utils' +import { log } from '../util/Logger' + +import { DashboardData, MorePromotion } from '../interface/DashboardData' + +// Daily Set +export async function doDailySet(page: Page, data: DashboardData) { + const todayData = data.dailySetPromotions[getFormattedDate()] + + const activitiesUncompleted = todayData?.filter(x => !x.complete) ?? [] + + if (!activitiesUncompleted.length) { + log('DAILY-SET', 'All daily set items have already been completed') + return + } + + for (const activity of activitiesUncompleted) { + log('DAILY-SET', 'Started doing daily set items') + + // If activity does not give points, skip + if (activity.pointProgressMax <= 0) { + continue + } + + 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 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 +export async function doPunchCard(page: Page, data: DashboardData) { + + const punchCardsUncompleted = data.punchCards?.filter(x => !x.parentPromotion.complete) ?? [] // Only return uncompleted punch cards + + if (!punchCardsUncompleted.length) { + log('PUNCH-CARD', 'All punch cards have already been completed') + return + } + + for (const promotion of punchCardsUncompleted) { + const activities = promotion.childPromotions.filter(x => !x.complete) // Only return uncompleted activities + + for (const activity of activities) { + log('PUNCH-CARD', 'Started doing daily set items') + + // If activity does not give points, skip + if (activity.pointProgressMax <= 0) { + continue + } + + 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 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('PUNCH-CARD', 'Punch card items have been completed') +} + +// More Promotions +export async function doMorePromotions(page: Page, data: DashboardData) { + const morePromotions = data.morePromotions + + // Check if there is a promotional item + if (data.promotionalItem) { // Convert and add the promotional item to the array + morePromotions.push(data.promotionalItem as unknown as MorePromotion) + } + + const activitiesUncompleted = morePromotions?.filter(x => !x.complete) ?? [] + + if (!activitiesUncompleted.length) { + log('MORE-PROMOTIONS', 'All more promotion items have already been completed') + return + } + + for (const activity of activitiesUncompleted) { + // If activity does not give points, skip + if (activity.pointProgressMax <= 0) { + continue + } + + 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 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) + } +} \ No newline at end of file diff --git a/src/functions/activities/ABC.ts b/src/functions/activities/ABC.ts new file mode 100644 index 0000000..a579cd1 --- /dev/null +++ b/src/functions/activities/ABC.ts @@ -0,0 +1,54 @@ +import { Page } from 'puppeteer' + +import { refreshCheerio } from '../../browser/BrowserFunc' +import { getLatestTab } from '../../browser/BrowserUtil' +import { log } from '../../util/Logger' +import { randomNumber, wait } from '../../util/Utils' + +import { MorePromotion, PromotionalItem } from '../../interface/DashboardData' + +export async function doABC(page: Page, data: PromotionalItem | MorePromotion) { + log('ABC', 'Trying to complete poll') + + 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) + let $ = await refreshCheerio(abcPage) + + while (!$('span.rw_icon').length) { + await abcPage.waitForSelector('.wk_OptionClickClass', { visible: true, timeout: 5000 }) + + const answers = $('.wk_OptionClickClass') + const answer = answers[randomNumber(0, 2)]?.attribs['id'] + + await abcPage.waitForSelector(`#${answer}`, { visible: true, timeout: 5000 }) + + await wait(2000) + await abcPage.click(`#${answer}`) // Click answer + + await wait(4000) + await abcPage.waitForSelector('div.wk_button', { visible: true, timeout: 5000 }) + await abcPage.click('div.wk_button') // Click next question button + + abcPage = await getLatestTab(abcPage) + $ = await refreshCheerio(abcPage) + await wait(1000) + } + + await wait(4000) + await abcPage.close() + + log('ABC', 'Completed the ABC successfully') + } catch (error) { + const abcPage = await getLatestTab(page) + await abcPage.close() + log('ABC', 'An error occurred:' + error, 'error') + } +} + diff --git a/src/functions/activities/Poll.ts b/src/functions/activities/Poll.ts index 25011c6..bb7dc58 100644 --- a/src/functions/activities/Poll.ts +++ b/src/functions/activities/Poll.ts @@ -22,10 +22,11 @@ export async function doPoll(page: Page, data: PromotionalItem | MorePromotion) await pollPage.waitForNetworkIdle({ timeout: 5000 }) await pollPage.waitForSelector(buttonId, { visible: true, timeout: 5000 }) - + await wait(2000) + await pollPage.click(buttonId) - await wait(2000) + await wait(4000) await pollPage.close() log('POLL', 'Completed the poll successfully') diff --git a/src/functions/activities/Quiz.ts b/src/functions/activities/Quiz.ts index 6d14789..c114eca 100644 --- a/src/functions/activities/Quiz.ts +++ b/src/functions/activities/Quiz.ts @@ -19,6 +19,7 @@ export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion) const quizPage = await getLatestTab(page) await quizPage.waitForNetworkIdle({ timeout: 5000 }) + await wait(2000) // Check if the quiz has been started or not const quizNotStarted = await quizPage.waitForSelector('#rqStartQuiz', { visible: true, timeout: 3000 }).then(() => true).catch(() => false) diff --git a/src/functions/activities/Search.ts b/src/functions/activities/Search.ts index d63ebd3..e7b25aa 100644 --- a/src/functions/activities/Search.ts +++ b/src/functions/activities/Search.ts @@ -263,41 +263,57 @@ async function randomScroll(page: Page) { async function clickRandomLink(page: Page, mobile: boolean) { try { const searchListingURL = new URL(page.url()) // Get page info before clicking - + mobile await page.click('#b_results .b_algo h2').catch(() => { }) // Since we don't really care if it did it or not + + await wait(3000) - const newTab = await getLatestTab(page) // Will get current tab if no new one is created - + let lastTab = await getLatestTab(page) // Will get current tab if no new one is created + await lastTab.waitForNetworkIdle() // Wait for page to load + // Check if the tab is closed or not - if (!newTab.isClosed()) { - const newTabURL = new URL(newTab.url()) // Get new tab info + if (!lastTab.isClosed()) { + let lastTabURL = new URL(lastTab.url()) // Get new tab info // Check if the URL is different from the original one - if (newTabURL.href !== searchListingURL.href) { - // Mobile is always same tab - if (mobile) { - await page.goBack() + while (lastTabURL.href !== searchListingURL.href) { + + // If hostname is still bing, (Bing images/news etc) + if (lastTabURL.hostname == searchListingURL.hostname) { + await lastTab.goBack() + + lastTab = await getLatestTab(page) // Get last opened tab + lastTabURL = new URL(lastTab.url()) - const currentURL = new URL(page.url()) // If "goBack" didn't return to search listing (due to redirects) - if (currentURL.hostname !== searchListingURL.hostname) { - await page.goto(searchListingURL.href) + if (lastTabURL.hostname !== searchListingURL.hostname) { + await lastTab.goto(searchListingURL.href) } - // Still on bing, go back (news/images pages on bing search) - } else if (newTabURL.hostname == searchListingURL.hostname) { - await page.goBack() + break + } else { // No longer on bing, likely opened a new tab, close this tab + lastTab = await getLatestTab(page) // Get last opened tab + lastTabURL = new URL(lastTab.url()) - const currentURL = new URL(page.url()) - // If "goBack" didn't return to search listing (due to redirects) - if (currentURL.hostname !== searchListingURL.hostname) { - await page.goto(searchListingURL.href) + const tabs = await (page.browser()).pages() // Get all tabs open + + // 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() + } else { + await lastTab.goBack() + + lastTab = await getLatestTab(page) // Get last opened tab + lastTabURL = new URL(lastTab.url()) + + // If "goBack" didn't return to search listing (due to redirects) + if (lastTabURL.hostname !== searchListingURL.hostname) { + await lastTab.goto(searchListingURL.href) + } } - // No longer on bing, likely opened a new tab, close this tab - } else { - await newTab.close() + break } } } diff --git a/src/functions/activities/ThisOrThat.ts b/src/functions/activities/ThisOrThat.ts index 29fdae2..77b6c29 100644 --- a/src/functions/activities/ThisOrThat.ts +++ b/src/functions/activities/ThisOrThat.ts @@ -19,6 +19,7 @@ export async function doThisOrThat(page: Page, data: PromotionalItem | MorePromo const thisorthatPage = await getLatestTab(page) await thisorthatPage.waitForNetworkIdle({ timeout: 5000 }) + await wait(2000) // Check if the quiz has been started or not const quizNotStarted = await thisorthatPage.waitForSelector('#rqStartQuiz', { visible: true, timeout: 3000 }).then(() => true).catch(() => false) diff --git a/src/functions/activities/UrlReward.ts b/src/functions/activities/UrlReward.ts index eb7b7d5..a4894ad 100644 --- a/src/functions/activities/UrlReward.ts +++ b/src/functions/activities/UrlReward.ts @@ -17,7 +17,7 @@ export async function doUrlReward(page: Page, data: PromotionalItem | MorePromot // After waiting, close the page const visitPage = await getLatestTab(page) - await visitPage.waitForNetworkIdle({ timeout: 5000 }) + await visitPage.waitForNetworkIdle({ timeout: 10_000 }) await visitPage.close() log('URL-REWARD', 'Completed the UrlReward successfully') diff --git a/src/index.ts b/src/index.ts index e25b7c0..5e5fe8d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,9 @@ -import { Browser, mobileBrowser } from './browser/Browser' +import Browser from './browser/Browser' import { getDashboardData, getEarnablePoints, goHome } from './browser/BrowserFunc' import { log } from './util/Logger' import { login } from './functions/Login' -import { doDailySet } from './functions/DailySet' -import { doMorePromotions } from './functions/MorePromotions' +import { doDailySet, doMorePromotions } from './functions/Workers' import { doSearch } from './functions/activities/Search' import { Account } from './interface/Account' @@ -12,115 +11,119 @@ import { Account } from './interface/Account' import accounts from './accounts.json' import { runOnZeroPoints, searches } from './config.json' -let collectedPoints = 0 +// Main bot class +class MicrosoftRewardsBot { + private collectedPoints: number = 0 + private browserFactory: Browser = new Browser() -async function main() { - log('MAIN', 'Bot started') + async run() { + log('MAIN', 'Bot started') - for (const account of accounts) { - log('MAIN', `Started tasks for account ${account.email}`) + for (const account of accounts) { + log('MAIN', `Started tasks for account ${account.email}`) - // Desktop Searches, DailySet and More Promotions - await Desktop(account) + // Desktop Searches, DailySet and More Promotions + await this.Desktop(account) - // If runOnZeroPoints is false and 0 points to earn, stop and try the next account - if (!runOnZeroPoints && collectedPoints === 0) { - continue + // If runOnZeroPoints is false and 0 points to earn, stop and try the next account + if (!runOnZeroPoints && this.collectedPoints === 0) { + continue + } + + // Mobile Searches + await this.Mobile(account) + + log('MAIN', `Completed tasks for account ${account.email}`) } - // Mobile Searches - await Mobile(account) - log('MAIN', `Completed tasks for account ${account.email}`) + // Clean exit + log('MAIN', 'Completed tasks for ALL accounts') + log('MAIN', 'Bot exited') + process.exit(0) } - // Clean exit - log('MAIN', 'Completed tasks for ALL accounts') - log('MAIN', 'Bot exited') - process.exit(0) -} + // Desktop + async Desktop(account: Account) { + const browser = await this.browserFactory.createBrowser(account.email, false) + const page = await browser.newPage() + log('MAIN', 'Starting DESKTOP browser') -// Desktop -async function Desktop(account: Account) { - const browser = await Browser(account.email) - const page = await browser.newPage() + // Login into MS Rewards + await login(page, account.email, account.password) - log('MAIN', 'Starting DESKTOP browser') + const wentHome = await goHome(page) + if (!wentHome) { + throw log('MAIN', 'Unable to get dashboard page', 'error') + } - // Login into MS Rewards - await login(page, account.email, account.password) + const data = await getDashboardData(page) + log('MAIN-POINTS', `Current point count: ${data.userStatus.availablePoints}`) - const wentHome = await goHome(page) - if (!wentHome) { - throw log('MAIN', 'Unable to get dashboard page', 'error') - } + const earnablePoints = await getEarnablePoints(data) + this.collectedPoints = earnablePoints + log('MAIN-POINTS', `You can earn ${earnablePoints} points today`) - const data = await getDashboardData(page) - log('MAIN-POINTS', `Current point count: ${data.userStatus.availablePoints}`) + // If runOnZeroPoints is false and 0 points to earn, don't continue + if (!runOnZeroPoints && this.collectedPoints === 0) { + log('MAIN', 'No points to earn and "runOnZeroPoints" is set to "false", stopping') - const earnablePoints = await getEarnablePoints(data) - collectedPoints = earnablePoints - log('MAIN-POINTS', `You can earn ${earnablePoints} points today`) + // Close desktop browser + return await browser.close() + } - // If runOnZeroPoints is false and 0 points to earn, don't continue - if (!runOnZeroPoints && collectedPoints === 0) { - log('MAIN', 'No points to earn and "runOnZeroPoints" is set to "false", stopping') + // Complete daily set + await doDailySet(page, data) + + // Complete more promotions + await doMorePromotions(page, data) + + // Do desktop searches + if (searches.doDesktop) { + await doSearch(page, data, false) + } // Close desktop browser - return await browser.close() + await browser.close() } - // Complete daily set - await doDailySet(page, data) + async Mobile(account: Account) { + const browser = await this.browserFactory.createBrowser(account.email, true) + const page = await browser.newPage() - // Complete more promotions - await doMorePromotions(page, data) + log('MAIN', 'Starting MOBILE browser') - // Do desktop searches - if (searches.doDesktop) { - await doSearch(page, data, false) - } + // Login into MS Rewards + await login(page, account.email, account.password) - // Close desktop browser - await browser.close() -} + await goHome(page) -async function Mobile(account: Account) { - const browser = await mobileBrowser(account.email) - const page = await browser.newPage() + const data = await getDashboardData(page) - log('MAIN', 'Starting MOBILE browser') + // If no mobile searches data found, stop (Does not exist on new accounts) + if (!data.userStatus.counters.mobileSearch) { + log('MAIN', 'No mobile searches found, stopping') - // Login into MS Rewards - await login(page, account.email, account.password) + // Close mobile browser + return await browser.close() + } - await goHome(page) + // Do mobile searches + if (searches.doMobile) { + await doSearch(page, data, true) + } - const data = await getDashboardData(page) + // Fetch new points + const earnablePoints = await getEarnablePoints(data, page) - // If no mobile searches data found, stop (Does not exist on new accounts) - if (!data.userStatus.counters.mobileSearch) { - log('MAIN', 'No mobile searches found, stopping') + // If the new earnable is 0, means we got all the points, else retract + this.collectedPoints = earnablePoints === 0 ? this.collectedPoints : (this.collectedPoints - earnablePoints) + log('MAIN-POINTS', `The script collected ${this.collectedPoints} points today`) // Close mobile browser - return await browser.close() + await browser.close() } - - // Do mobile searches - if (searches.doMobile) { - await doSearch(page, data, true) - } - - // Fetch new points - const earnablePoints = await getEarnablePoints(data, page) - // If the new earnable is 0, means we got all the points, else retract - collectedPoints = earnablePoints === 0 ? collectedPoints : (collectedPoints - earnablePoints) - log('MAIN-POINTS', `The script collected ${collectedPoints} points today`) - - // Close mobile browser - await browser.close() } -// Run main script -main() \ No newline at end of file +new MicrosoftRewardsBot().run() \ No newline at end of file