This commit is contained in:
TheNetsky
2023-10-05 15:35:25 +02:00
parent 78a7566978
commit e982e6e25f
14 changed files with 87 additions and 48 deletions

42
src/browser/Browser.ts Normal file
View 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
View 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
}
}

View 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')
}
}