diff --git a/README.md b/README.md index 3b44771..9659eb4 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,9 @@ Under development, however mainly for personal use! - [x] Completing Polls - [ ] Completing Punchcards - [ ] Solving This Or That Quiz -- [ ] Clicking Promotional Items +- [x] Clicking Promotional Items - [ ] Completing Shop And Earn +- [ ] Proxy Support ## Disclaimer ## Your account may be at risk of getting banned or suspended using this script, you've been warned! diff --git a/package.json b/package.json index 0fb74ef..53d3fd0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "microsoft-rewards-script", - "version": "1.0.5", + "version": "1.0.6", "description": "Automatically do tasks for Microsoft Rewards but in TS", "main": "index.js", "scripts": { @@ -26,7 +26,7 @@ "cheerio": "^1.0.0-rc.12", "eslint": "^8.49.0", "eslint-plugin-modules-newline": "^0.0.6", - "puppeteer": "^21.3.6", + "puppeteer": "^21.2.1", "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", "ts-node": "^10.9.1" diff --git a/src/Browser.ts b/src/browser/Browser.ts similarity index 91% rename from src/Browser.ts rename to src/browser/Browser.ts index d460d99..653703e 100644 --- a/src/Browser.ts +++ b/src/browser/Browser.ts @@ -1,10 +1,10 @@ import puppeteer from 'puppeteer-extra' import StealthPlugin from 'puppeteer-extra-plugin-stealth' -import { getUserAgent } from './util/UserAgent' +import { getUserAgent } from '../util/UserAgent' import { loadSesion } from './BrowserFunc' -import { headless } from './config.json' +import { headless } from '../config.json' puppeteer.use(StealthPlugin()) diff --git a/src/BrowserFunc.ts b/src/browser/BrowserFunc.ts similarity index 87% rename from src/BrowserFunc.ts rename to src/browser/BrowserFunc.ts index 9111e6a..1428c78 100644 --- a/src/BrowserFunc.ts +++ b/src/browser/BrowserFunc.ts @@ -4,13 +4,13 @@ import path from 'path' import { load } from 'cheerio' import { tryDismissAllMessages, tryDismissCookieBanner } from './BrowserUtil' -import { getFormattedDate, wait } from './util/Utils' -import { log } from './util/Logger' +import { getFormattedDate, wait } from './../util/Utils' +import { log } from './../util/Logger' -import { Counters, DashboardData } from './interface/DashboardData' -import { QuizData } from './interface/QuizData' +import { Counters, DashboardData } from './../interface/DashboardData' +import { QuizData } from './../interface/QuizData' -import { baseURL, sessionPath } from './config.json' +import { baseURL, sessionPath } from './../config.json' export async function goHome(page: Page): Promise { @@ -55,7 +55,7 @@ export async function goHome(page: Page): Promise { } } catch (error) { - console.error('An error occurred:', JSON.stringify(error, null, 2)) + console.error('An error occurred:', error) return false } @@ -126,7 +126,7 @@ export async function getQuizData(page: Page): Promise { } } catch (error) { - throw log('GET-QUIZ-DATA', 'An error occurred:' + JSON.stringify(error, null, 2), 'error') + throw log('GET-QUIZ-DATA', 'An error occurred:' + error, 'error') } } @@ -168,7 +168,20 @@ export async function getEarnablePoints(data: DashboardData, page: null | Page = return totalEarnablePoints } catch (error) { - throw log('GET-EARNABLE-POINTS', 'An error occurred:' + JSON.stringify(error, null, 2), 'error') + throw log('GET-EARNABLE-POINTS', 'An error occurred:' + error, 'error') + } +} + +export async function getCurrentPoints(data: DashboardData, page: null | Page = null): Promise { + try { + // Fetch new data if page is provided + if (page) { + data = await getDashboardData(page) + } + + return data.userStatus.availablePoints + } catch (error) { + throw log('GET-CURRENT-POINTS', 'An error occurred:' + error, 'error') } } @@ -193,7 +206,7 @@ export async function waitForQuizRefresh(page: Page) { await page.waitForSelector('#rqHeaderCredits', { timeout: 5000 }) return true } catch (error) { - log('QUIZ-REFRESH', 'An error occurred:' + JSON.stringify(error, null, 2), 'error') + log('QUIZ-REFRESH', 'An error occurred:' + error, 'error') return false } } diff --git a/src/BrowserUtil.ts b/src/browser/BrowserUtil.ts similarity index 79% rename from src/BrowserUtil.ts rename to src/browser/BrowserUtil.ts index c15b368..c50d9fb 100644 --- a/src/BrowserUtil.ts +++ b/src/browser/BrowserUtil.ts @@ -1,6 +1,7 @@ import { Page } from 'puppeteer' -import { wait } from './util/Utils' +import { wait } from './../util/Utils' +import { log } from './../util/Logger' export async function tryDismissAllMessages(page: Page): Promise { const buttons = [ @@ -62,10 +63,19 @@ export async function tryDismissBingCookieBanner(page: Page): Promise { } export async function getLatestTab(page: Page) { - await wait(2000) - const browser = page.browser() - const pages = await browser.pages() - const newTab = pages[pages.length - 1] as Page + try { + await wait(500) - return newTab + const browser = page.browser() + const pages = await browser.pages() + const newTab = pages[pages.length - 1] + + if (newTab) { + return newTab + } + + throw log('GET-NEW-TAB', 'Unable to get latest tab', 'error') + } catch (error) { + throw log('GET-NEW-TAB', 'An error occurred:' + error, 'error') + } } \ No newline at end of file diff --git a/src/functions/Login.ts b/src/functions/Login.ts index 4660483..ace22c6 100644 --- a/src/functions/Login.ts +++ b/src/functions/Login.ts @@ -6,7 +6,7 @@ const rl = readline.createInterface({ output: process.stdout }) -import { tryDismissAllMessages, tryDismissBingCookieBanner } from '../BrowserUtil' +import { tryDismissAllMessages, tryDismissBingCookieBanner } from '../browser/BrowserUtil' import { wait } from '../util/Utils' import { log } from '../util/Logger' @@ -34,7 +34,7 @@ export async function login(page: Page, email: string, password: string) { log('LOGIN', 'Logged in successfully') } catch (error) { - log('LOGIN', 'An error occurred:' + JSON.stringify(error, null, 2), 'error') + log('LOGIN', 'An error occurred:' + error, 'error') } } @@ -103,7 +103,7 @@ async function checkBingLogin(page: Page): Promise { } } catch (error) { - log('LOGIN-BING', 'An error occurred:' + JSON.stringify(error, null, 2), 'error') + log('LOGIN-BING', 'An error occurred:' + error, 'error') } } diff --git a/src/functions/MorePromotions.ts b/src/functions/MorePromotions.ts index 859c2af..aee925e 100644 --- a/src/functions/MorePromotions.ts +++ b/src/functions/MorePromotions.ts @@ -8,11 +8,16 @@ import { doThisOrThat } from './activities/ThisOrThat' import { wait } from '../util/Utils' import { log } from '../util/Logger' -import { DashboardData } from '../interface/DashboardData' +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) { diff --git a/src/functions/activities/Poll.ts b/src/functions/activities/Poll.ts index 461df33..25011c6 100644 --- a/src/functions/activities/Poll.ts +++ b/src/functions/activities/Poll.ts @@ -1,6 +1,6 @@ import { Page } from 'puppeteer' -import { getLatestTab } from '../../BrowserUtil' +import { getLatestTab } from '../../browser/BrowserUtil' import { log } from '../../util/Logger' import { randomNumber, wait } from '../../util/Utils' @@ -20,7 +20,9 @@ export async function doPoll(page: Page, data: PromotionalItem | MorePromotion) const buttonId = `#btoption${Math.floor(randomNumber(0, 1))}` + await pollPage.waitForNetworkIdle({ timeout: 5000 }) await pollPage.waitForSelector(buttonId, { visible: true, timeout: 5000 }) + await pollPage.click(buttonId) await wait(2000) @@ -28,6 +30,8 @@ export async function doPoll(page: Page, data: PromotionalItem | MorePromotion) log('POLL', 'Completed the poll successfully') } catch (error) { - log('POLL', 'An error occurred:' + JSON.stringify(error, null, 2), 'error') + const pollPage = await getLatestTab(page) + await pollPage.close() + log('POLL', 'An error occurred:' + error, 'error') } } \ No newline at end of file diff --git a/src/functions/activities/Quiz.ts b/src/functions/activities/Quiz.ts index 342c2f9..6d14789 100644 --- a/src/functions/activities/Quiz.ts +++ b/src/functions/activities/Quiz.ts @@ -1,7 +1,7 @@ import { Page } from 'puppeteer' -import { getLatestTab } from '../../BrowserUtil' -import { getQuizData, waitForQuizRefresh } from '../../BrowserFunc' +import { getLatestTab } from '../../browser/BrowserUtil' +import { getQuizData, waitForQuizRefresh } from '../../browser/BrowserFunc' import { wait } from '../../util/Utils' import { log } from '../../util/Logger' @@ -18,6 +18,7 @@ export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion) await page.click(selector) const quizPage = await getLatestTab(page) + await quizPage.waitForNetworkIdle({ timeout: 5000 }) // Check if the quiz has been started or not const quizNotStarted = await quizPage.waitForSelector('#rqStartQuiz', { visible: true, timeout: 3000 }).then(() => true).catch(() => false) @@ -29,7 +30,7 @@ export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion) await wait(2000) - const quizData = await getQuizData(quizPage) + let quizData = await getQuizData(quizPage) const questionsRemaining = quizData.maxQuestions - quizData.CorrectlyAnsweredQuestionCount // Amount of questions remaining // All questions @@ -41,7 +42,6 @@ export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion) for (let i = 0; i < quizData.numberOfOptions; i++) { const answerSelector = await quizPage.waitForSelector(`#rqAnswerOption${i}`, { visible: true, timeout: 5000 }) const answerAttribute = await answerSelector?.evaluate(el => el.getAttribute('iscorrectoption')) - await wait(500) if (answerAttribute && answerAttribute.toLowerCase() === 'true') { answers.push(`#rqAnswerOption${i}`) @@ -65,6 +65,7 @@ export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion) // Other type quiz } else if ([2, 3, 4].includes(quizData.numberOfOptions)) { + quizData = await getQuizData(quizPage) // Refresh Quiz Data const correctOption = quizData.correctAnswer for (let i = 0; i < quizData.numberOfOptions; i++) { @@ -75,7 +76,6 @@ export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion) if (dataOption === correctOption) { // Click the answer on page await quizPage.click(`#rqAnswerOption${i}`) - await wait(2000) const refreshSuccess = await waitForQuizRefresh(quizPage) if (!refreshSuccess) { @@ -98,7 +98,7 @@ export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion) } catch (error) { const quizPage = await getLatestTab(page) await quizPage.close() - log('QUIZ', 'An error occurred:' + JSON.stringify(error, null, 2), 'error') + log('QUIZ', 'An error occurred:' + error, 'error') } } \ No newline at end of file diff --git a/src/functions/activities/Search.ts b/src/functions/activities/Search.ts index 8cb0933..d63ebd3 100644 --- a/src/functions/activities/Search.ts +++ b/src/functions/activities/Search.ts @@ -1,8 +1,8 @@ import { Page } from 'puppeteer' import axios from 'axios' -import { getLatestTab } from '../../BrowserUtil' -import { getSearchPoints } from '../../BrowserFunc' +import { getLatestTab } from '../../browser/BrowserUtil' +import { getSearchPoints } from '../../browser/BrowserFunc' import { log } from '../../util/Logger' import { randomNumber, shuffleArray, wait } from '../../util/Utils' @@ -169,11 +169,11 @@ async function bingSearch(page: Page, searchPage: Page, query: string, mobile: b } catch (error) { if (i === 5) { - log('SEARCH-BING', 'Failed after 5 retries... An error occurred:' + JSON.stringify(error, null, 2), 'error') + log('SEARCH-BING', 'Failed after 5 retries... An error occurred:' + error, 'error') break } - log('SEARCH-BING', 'Search failed, An error occurred:' + JSON.stringify(error, null, 2), 'error') + log('SEARCH-BING', 'Search failed, An error occurred:' + error, 'error') log('SEARCH-BING', `Retrying search, attempt ${i}/5`, 'warn') await wait(4000) @@ -215,7 +215,7 @@ async function getGoogleTrends(locale: string, queryCount: number): Promise { return response.data[1] as string[] } catch (error) { - log('SEARCH-BING-RELTATED', 'An error occurred:' + JSON.stringify(error, null, 2), 'error') + log('SEARCH-BING-RELTATED', 'An error occurred:' + error, 'error') } return [] } @@ -256,7 +256,7 @@ async function randomScroll(page: Page) { await page.keyboard.press('ArrowDown') } } catch (error) { - log('SEARCH-RANDOM-SCROLL', 'An error occurred:' + JSON.stringify(error, null, 2), 'error') + log('SEARCH-RANDOM-SCROLL', 'An error occurred:' + error, 'error') } } @@ -302,6 +302,6 @@ async function clickRandomLink(page: Page, mobile: boolean) { } } } catch (error) { - log('SEARCH-RANDOM-CLICK', 'An error occurred:' + JSON.stringify(error, null, 2), 'error') + log('SEARCH-RANDOM-CLICK', 'An error occurred:' + error, 'error') } } \ No newline at end of file diff --git a/src/functions/activities/ThisOrThat.ts b/src/functions/activities/ThisOrThat.ts index 92d9261..29fdae2 100644 --- a/src/functions/activities/ThisOrThat.ts +++ b/src/functions/activities/ThisOrThat.ts @@ -1,6 +1,6 @@ import { Page } from 'puppeteer' -import { getLatestTab } from '../../BrowserUtil' +import { getLatestTab } from '../../browser/BrowserUtil' import { wait } from '../../util/Utils' import { log } from '../../util/Logger' @@ -18,6 +18,7 @@ export async function doThisOrThat(page: Page, data: PromotionalItem | MorePromo await page.click(selector) const thisorthatPage = await getLatestTab(page) + await thisorthatPage.waitForNetworkIdle({ timeout: 5000 }) // Check if the quiz has been started or not const quizNotStarted = await thisorthatPage.waitForSelector('#rqStartQuiz', { visible: true, timeout: 3000 }).then(() => true).catch(() => false) @@ -33,7 +34,9 @@ export async function doThisOrThat(page: Page, data: PromotionalItem | MorePromo log('THIS-OR-THAT', 'Completed the ThisOrthat successfully') } catch (error) { - log('THIS-OR-THAT', 'An error occurred:' + JSON.stringify(error, null, 2), 'error') + const thisorthatPage = await getLatestTab(page) + await thisorthatPage.close() + log('THIS-OR-THAT', 'An error occurred:' + error, 'error') } } \ No newline at end of file diff --git a/src/functions/activities/UrlReward.ts b/src/functions/activities/UrlReward.ts index 57cace6..eb7b7d5 100644 --- a/src/functions/activities/UrlReward.ts +++ b/src/functions/activities/UrlReward.ts @@ -1,6 +1,6 @@ import { Page } from 'puppeteer' -import { getLatestTab } from '../../BrowserUtil' +import { getLatestTab } from '../../browser/BrowserUtil' import { log } from '../../util/Logger' import { PromotionalItem, MorePromotion } from '../../interface/DashboardData' @@ -17,11 +17,14 @@ 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.close() log('URL-REWARD', 'Completed the UrlReward successfully') } catch (error) { - log('URL-REWARD', 'An error occurred:' + JSON.stringify(error, null, 2), 'error') + const visitPage = await getLatestTab(page) + await visitPage.close() + log('URL-REWARD', 'An error occurred:' + error, 'error') } } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 21fcd01..e25b7c0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ -import { Browser, mobileBrowser } from './Browser' -import { getDashboardData, getEarnablePoints, goHome } from './BrowserFunc' +import { Browser, mobileBrowser } from './browser/Browser' +import { getDashboardData, getEarnablePoints, goHome } from './browser/BrowserFunc' import { log } from './util/Logger' import { login } from './functions/Login' diff --git a/src/util/UserAgent.ts b/src/util/UserAgent.ts index 21864db..56ae83f 100644 --- a/src/util/UserAgent.ts +++ b/src/util/UserAgent.ts @@ -51,7 +51,7 @@ export async function getChromeVersion(): Promise { return data.channels.Stable.version } catch (error) { - throw log('USERAGENT-CHROME-VERSION', 'An error occurred:' + JSON.stringify(error, null, 2), 'error') + throw log('USERAGENT-CHROME-VERSION', 'An error occurred:' + error, 'error') } } @@ -75,7 +75,7 @@ export async function getEdgeVersions() { } catch (error) { - throw log('USERAGENT-EDGE-VERSION', 'An error occurred:' + JSON.stringify(error, null, 2), 'error') + throw log('USERAGENT-EDGE-VERSION', 'An error occurred:' + error, 'error') } }