mirror of
https://github.com/TheNetsky/Microsoft-Rewards-Script.git
synced 2026-01-18 14:03:58 +00:00
1.0.6
This commit is contained in:
42
src/browser/Browser.ts
Normal file
42
src/browser/Browser.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import puppeteer from 'puppeteer-extra'
|
||||
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
|
||||
|
||||
import { getUserAgent } from '../util/UserAgent'
|
||||
import { loadSesion } from './BrowserFunc'
|
||||
|
||||
import { headless } from '../config.json'
|
||||
|
||||
puppeteer.use(StealthPlugin())
|
||||
|
||||
export async function Browser(email: string) {
|
||||
const userAgent = await getUserAgent(false)
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
headless: headless,
|
||||
userDataDir: await loadSesion(email),
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
`--user-agent=${userAgent.userAgent}`
|
||||
]
|
||||
})
|
||||
|
||||
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
|
||||
}
|
||||
221
src/browser/BrowserFunc.ts
Normal file
221
src/browser/BrowserFunc.ts
Normal file
@@ -0,0 +1,221 @@
|
||||
import { Page } from 'puppeteer'
|
||||
import fs from 'fs'
|
||||
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 { Counters, DashboardData } from './../interface/DashboardData'
|
||||
import { QuizData } from './../interface/QuizData'
|
||||
|
||||
import { baseURL, sessionPath } from './../config.json'
|
||||
|
||||
export async function goHome(page: Page): Promise<boolean> {
|
||||
|
||||
try {
|
||||
const dashboardURL = new URL(baseURL)
|
||||
|
||||
await page.goto(baseURL)
|
||||
|
||||
const maxIterations = 5 // Maximum iterations set to 5
|
||||
|
||||
for (let iteration = 1; iteration <= maxIterations; iteration++) {
|
||||
await wait(3000)
|
||||
await tryDismissCookieBanner(page)
|
||||
|
||||
// Check if account is suspended
|
||||
const isSuspended = await page.waitForSelector('#suspendedAccountHeader', { visible: true, timeout: 3000 }).then(() => true).catch(() => false)
|
||||
if (isSuspended) {
|
||||
log('GO-HOME', 'This account is suspended!')
|
||||
throw new Error('Account has been suspended!')
|
||||
}
|
||||
|
||||
try {
|
||||
// If activities are found, exit the loop
|
||||
await page.waitForSelector('#more-activities', { timeout: 1000 })
|
||||
break
|
||||
|
||||
} catch (error) {
|
||||
// Continue if element is not found
|
||||
}
|
||||
|
||||
const currentURL = new URL(page.url())
|
||||
|
||||
if (currentURL.hostname !== dashboardURL.hostname) {
|
||||
await tryDismissAllMessages(page)
|
||||
|
||||
await wait(2000)
|
||||
await page.goto(baseURL)
|
||||
}
|
||||
|
||||
await wait(5000)
|
||||
log('GO-HOME', 'Visited homepage successfully')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('An error occurred:', error)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export async function getDashboardData(page: Page): Promise<DashboardData> {
|
||||
const dashboardURL = new URL(baseURL)
|
||||
const currentURL = new URL(page.url())
|
||||
|
||||
// Should never happen since tasks are opened in a new tab!
|
||||
if (currentURL.hostname !== dashboardURL.hostname) {
|
||||
log('DASHBOARD-DATA', 'Provided page did not equal dashboard page, redirecting to dashboard page')
|
||||
await goHome(page)
|
||||
}
|
||||
|
||||
// Reload the page to get new data
|
||||
await page.reload({ waitUntil: 'networkidle2' })
|
||||
|
||||
const scriptContent = await page.evaluate(() => {
|
||||
const scripts = Array.from(document.querySelectorAll('script'))
|
||||
const targetScript = scripts.find(script => script.innerText.includes('var dashboard'))
|
||||
|
||||
if (targetScript) {
|
||||
return targetScript.innerText
|
||||
} else {
|
||||
throw log('GET-DASHBOARD-DATA', 'Script containing dashboard data not found', 'error')
|
||||
}
|
||||
})
|
||||
|
||||
// Extract the dashboard object from the script content
|
||||
const dashboardData = await page.evaluate(scriptContent => {
|
||||
// Extract the dashboard object using regex
|
||||
const regex = /var dashboard = (\{.*?\});/s
|
||||
const match = regex.exec(scriptContent)
|
||||
|
||||
if (match && match[1]) {
|
||||
return JSON.parse(match[1])
|
||||
} else {
|
||||
throw log('GET-DASHBOARD-DATA', 'Dashboard data not found within script', 'error')
|
||||
}
|
||||
}, scriptContent)
|
||||
|
||||
return dashboardData
|
||||
}
|
||||
|
||||
export async function getQuizData(page: Page): Promise<QuizData> {
|
||||
try {
|
||||
const html = await page.content()
|
||||
const $ = load(html)
|
||||
|
||||
const scriptContent = $('script').filter((index, element) => {
|
||||
return $(element).text().includes('_w.rewardsQuizRenderInfo')
|
||||
}).text()
|
||||
|
||||
if (scriptContent) {
|
||||
const regex = /_w\.rewardsQuizRenderInfo\s*=\s*({.*?});/s
|
||||
const match = regex.exec(scriptContent)
|
||||
|
||||
if (match && match[1]) {
|
||||
const quizData = JSON.parse(match[1])
|
||||
return quizData
|
||||
} else {
|
||||
throw log('GET-QUIZ-DATA', 'Quiz data not found within script', 'error')
|
||||
}
|
||||
} else {
|
||||
throw log('GET-QUIZ-DATA', 'Script containing quiz data not found', 'error')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
throw log('GET-QUIZ-DATA', 'An error occurred:' + error, 'error')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export async function getSearchPoints(page: Page): Promise<Counters> {
|
||||
const dashboardData = await getDashboardData(page) // Always fetch newest data
|
||||
|
||||
return dashboardData.userStatus.counters
|
||||
}
|
||||
|
||||
export async function getEarnablePoints(data: DashboardData, page: null | Page = null): Promise<number> {
|
||||
try {
|
||||
// Fetch new data if page is provided
|
||||
if (page) {
|
||||
data = await getDashboardData(page)
|
||||
}
|
||||
|
||||
// These only include the points from tasks that the script can complete!
|
||||
let totalEarnablePoints = 0
|
||||
|
||||
// Desktop Search Points
|
||||
data.userStatus.counters.pcSearch.forEach(x => totalEarnablePoints += (x.pointProgressMax - x.pointProgress))
|
||||
|
||||
// Mobile Search Points
|
||||
if (data.userStatus.counters.mobileSearch?.length) {
|
||||
data.userStatus.counters.mobileSearch.forEach(x => totalEarnablePoints += (x.pointProgressMax - x.pointProgress))
|
||||
}
|
||||
|
||||
// Daily Set
|
||||
data.dailySetPromotions[getFormattedDate()]?.forEach(x => totalEarnablePoints += (x.pointProgressMax - x.pointProgress))
|
||||
|
||||
// More Promotions
|
||||
data.morePromotions.forEach(x => {
|
||||
// Only count points from supported activities
|
||||
if (['quiz', 'urlreward'].includes(x.activityType)) {
|
||||
totalEarnablePoints += (x.pointProgressMax - x.pointProgress)
|
||||
}
|
||||
})
|
||||
|
||||
return totalEarnablePoints
|
||||
} catch (error) {
|
||||
throw log('GET-EARNABLE-POINTS', 'An error occurred:' + error, 'error')
|
||||
}
|
||||
}
|
||||
|
||||
export async function getCurrentPoints(data: DashboardData, page: null | Page = null): Promise<number> {
|
||||
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')
|
||||
}
|
||||
}
|
||||
|
||||
export async function loadSesion(email: string): Promise<string> {
|
||||
const sessionDir = path.join(__dirname, sessionPath, email)
|
||||
|
||||
try {
|
||||
// Create session dir
|
||||
if (!fs.existsSync(sessionDir)) {
|
||||
await fs.promises.mkdir(sessionDir, { recursive: true })
|
||||
}
|
||||
|
||||
return sessionDir
|
||||
|
||||
} catch (error) {
|
||||
throw new Error(error as string)
|
||||
}
|
||||
}
|
||||
|
||||
export async function waitForQuizRefresh(page: Page) {
|
||||
try {
|
||||
await page.waitForSelector('#rqHeaderCredits', { timeout: 5000 })
|
||||
return true
|
||||
} catch (error) {
|
||||
log('QUIZ-REFRESH', 'An error occurred:' + error, 'error')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkQuizCompleted(page: Page) {
|
||||
try {
|
||||
await page.waitForSelector('#quizCompleteContainer', { timeout: 1000 })
|
||||
return true
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
81
src/browser/BrowserUtil.ts
Normal file
81
src/browser/BrowserUtil.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { Page } from 'puppeteer'
|
||||
|
||||
import { wait } from './../util/Utils'
|
||||
import { log } from './../util/Logger'
|
||||
|
||||
export async function tryDismissAllMessages(page: Page): Promise<boolean> {
|
||||
const buttons = [
|
||||
{ selector: '#iLandingViewAction', label: 'iLandingViewAction' },
|
||||
{ selector: '#iShowSkip', label: 'iShowSkip' },
|
||||
{ selector: '#iNext', label: 'iNext' },
|
||||
{ selector: '#iLooksGood', label: 'iLooksGood' },
|
||||
{ selector: '#idSIButton9', label: 'idSIButton9' },
|
||||
{ selector: '.ms-Button.ms-Button--primary', label: 'Primary Button' }
|
||||
]
|
||||
|
||||
let result = false
|
||||
|
||||
for (const button of buttons) {
|
||||
try {
|
||||
const element = await page.waitForSelector(button.selector, { visible: true, timeout: 1000 })
|
||||
if (element) {
|
||||
await element.click()
|
||||
result = true
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export async function tryDismissCookieBanner(page: Page): Promise<void> {
|
||||
try {
|
||||
await page.waitForSelector('#cookieConsentContainer', { timeout: 1000 })
|
||||
const cookieBanner = await page.$('#cookieConsentContainer')
|
||||
|
||||
if (cookieBanner) {
|
||||
const button = await cookieBanner.$('button')
|
||||
if (button) {
|
||||
await button.click()
|
||||
await wait(2000)
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// Continue if element is not found or other error occurs
|
||||
}
|
||||
}
|
||||
|
||||
export async function tryDismissBingCookieBanner(page: Page): Promise<void> {
|
||||
try {
|
||||
await page.waitForSelector('#bnp_btn_accept', { timeout: 1000 })
|
||||
const cookieBanner = await page.$('#bnp_btn_accept')
|
||||
|
||||
if (cookieBanner) {
|
||||
await cookieBanner.click()
|
||||
}
|
||||
} catch (error) {
|
||||
// Continue if element is not found or other error occurs
|
||||
}
|
||||
}
|
||||
|
||||
export async function getLatestTab(page: Page) {
|
||||
try {
|
||||
await wait(500)
|
||||
|
||||
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')
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user