This commit is contained in:
TheNetsky
2023-10-02 11:52:12 +02:00
parent 8fef05a2b7
commit e8cb195f54
17 changed files with 341 additions and 149 deletions

2
.gitignore vendored
View File

@@ -3,3 +3,5 @@ dist/
node_modules/ node_modules/
package-lock.json package-lock.json
accounts.json accounts.json
notes
accounts.dev.json

View File

@@ -1,6 +1,6 @@
{ {
"name": "microsoft-rewards-script", "name": "microsoft-rewards-script",
"version": "1.0.4", "version": "1.0.5",
"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",
"scripts": { "scripts": {
@@ -22,10 +22,11 @@
"typescript": "^5.2.2" "typescript": "^5.2.2"
}, },
"dependencies": { "dependencies": {
"axios": "^1.5.0", "axios": "^1.5.1",
"cheerio": "^1.0.0-rc.12",
"eslint": "^8.49.0", "eslint": "^8.49.0",
"eslint-plugin-modules-newline": "^0.0.6", "eslint-plugin-modules-newline": "^0.0.6",
"puppeteer": "^21.2.1", "puppeteer": "^21.3.6",
"puppeteer-extra": "^3.3.6", "puppeteer-extra": "^3.3.6",
"puppeteer-extra-plugin-stealth": "^2.11.2", "puppeteer-extra-plugin-stealth": "^2.11.2",
"ts-node": "^10.9.1" "ts-node": "^10.9.1"

View File

@@ -1,15 +1,17 @@
import { Page } from 'puppeteer' import { Page } from 'puppeteer'
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import { load } from 'cheerio'
import { baseURL, sessionPath } from './config.json'
import { getFormattedDate, wait } from './util/Utils'
import { tryDismissAllMessages, tryDismissCookieBanner } from './BrowserUtil' import { tryDismissAllMessages, tryDismissCookieBanner } from './BrowserUtil'
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 } from './interface/DashboardData'
import { QuizData } from './interface/QuizData' import { QuizData } from './interface/QuizData'
import { baseURL, sessionPath } from './config.json'
export async function goHome(page: Page): Promise<boolean> { export async function goHome(page: Page): Promise<boolean> {
try { try {
@@ -53,7 +55,7 @@ export async function goHome(page: Page): Promise<boolean> {
} }
} catch (error) { } catch (error) {
console.error('An error occurred:', error) console.error('An error occurred:', JSON.stringify(error, null, 2))
return false return false
} }
@@ -101,30 +103,32 @@ export async function getDashboardData(page: Page): Promise<DashboardData> {
} }
export async function getQuizData(page: Page): Promise<QuizData> { export async function getQuizData(page: Page): Promise<QuizData> {
const scriptContent = await page.evaluate(() => { try {
const scripts = Array.from(document.querySelectorAll('script')) const html = await page.content()
const targetScript = scripts.find(script => script.innerText.includes('_w.rewardsQuizRenderInfo')) const $ = load(html)
if (targetScript) { const scriptContent = $('script').filter((index, element) => {
return targetScript.innerText 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 { } else {
throw log('GET-QUIZ-DATA', 'Script containing quiz data not found', 'error') throw log('GET-QUIZ-DATA', 'Script containing quiz data not found', 'error')
} }
})
const quizData = await page.evaluate(scriptContent => { } catch (error) {
// Extract the dashboard object using regex throw log('GET-QUIZ-DATA', 'An error occurred:' + JSON.stringify(error, null, 2), 'error')
const regex = /_w\.rewardsQuizRenderInfo\s*=\s*({.*?});/s }
const match = regex.exec(scriptContent)
if (match && match[1]) {
return JSON.parse(match[1])
} else {
throw log('GET-QUIZ-DATA', 'Quiz data not found within script', 'error')
}
}, scriptContent)
return quizData
} }
export async function getSearchPoints(page: Page): Promise<Counters> { export async function getSearchPoints(page: Page): Promise<Counters> {
@@ -134,32 +138,38 @@ export async function getSearchPoints(page: Page): Promise<Counters> {
} }
export async function getEarnablePoints(data: DashboardData, page: null | Page = null): Promise<number> { export async function getEarnablePoints(data: DashboardData, page: null | Page = null): Promise<number> {
// Fetch new data if page is provided try {
if (page) { // Fetch new data if page is provided
data = await getDashboardData(page) 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
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 // 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:' + JSON.stringify(error, null, 2), 'error')
}
} }
export async function loadSesion(email: string): Promise<string> { export async function loadSesion(email: string): Promise<string> {
@@ -177,3 +187,22 @@ export async function loadSesion(email: string): Promise<string> {
throw new Error(error as string) 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:' + JSON.stringify(error, null, 2), 'error')
return false
}
}
export async function checkQuizCompleted(page: Page) {
try {
await page.waitForSelector('#quizCompleteContainer', { timeout: 1000 })
return true
} catch (error) {
return false
}
}

View File

@@ -1,4 +1,5 @@
import { Page } from 'puppeteer' import { Page } from 'puppeteer'
import { wait } from './util/Utils' import { wait } from './util/Utils'
export async function tryDismissAllMessages(page: Page): Promise<boolean> { export async function tryDismissAllMessages(page: Page): Promise<boolean> {
@@ -15,7 +16,7 @@ export async function tryDismissAllMessages(page: Page): Promise<boolean> {
for (const button of buttons) { for (const button of buttons) {
try { try {
const element = await page.waitForSelector(button.selector, { timeout: 1000 }) const element = await page.waitForSelector(button.selector, { visible: true, timeout: 1000 })
if (element) { if (element) {
await element.click() await element.click()
result = true result = true

View File

@@ -1,11 +1,14 @@
import { Page } from 'puppeteer' import { Page } from 'puppeteer'
import { DashboardData } from '../interface/DashboardData'
import { doPoll } from './activities/Poll'
import { getFormattedDate, wait } from '../util/Utils'
import { doQuiz } from './activities/Quiz'
import { log } from '../util/Logger'
import { doUrlReward } from './activities/UrlReward'
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) { export async function doDailySet(page: Page, data: DashboardData) {
const todayData = data.dailySetPromotions[getFormattedDate()] const todayData = data.dailySetPromotions[getFormattedDate()]
@@ -19,6 +22,12 @@ export async function doDailySet(page: Page, data: DashboardData) {
for (const activity of activitiesUncompleted) { for (const activity of activitiesUncompleted) {
log('DAILY-SET', 'Started doing daily set items') log('DAILY-SET', 'Started doing daily set items')
// If activity does not give points, skip
if (activity.pointProgressMax <= 0) {
continue
}
switch (activity.promotionType) { switch (activity.promotionType) {
// Quiz (Poll/Quiz) // Quiz (Poll/Quiz)
case 'quiz': case 'quiz':
@@ -30,6 +39,12 @@ export async function doDailySet(page: Page, data: DashboardData) {
await doPoll(page, activity) await doPoll(page, activity)
break 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 // Quizzes are usually 30-40 points
default: default:
log('ACTIVITY', 'Found daily activity type: Quiz') log('ACTIVITY', 'Found daily activity type: Quiz')

View File

@@ -6,8 +6,8 @@ const rl = readline.createInterface({
output: process.stdout output: process.stdout
}) })
import { wait } from '../util/Utils'
import { tryDismissAllMessages, tryDismissBingCookieBanner } from '../BrowserUtil' import { tryDismissAllMessages, tryDismissBingCookieBanner } from '../BrowserUtil'
import { wait } from '../util/Utils'
import { log } from '../util/Logger' import { log } from '../util/Logger'
export async function login(page: Page, email: string, password: string) { export async function login(page: Page, email: string, password: string) {
@@ -34,7 +34,7 @@ export async function login(page: Page, email: string, password: string) {
log('LOGIN', 'Logged in successfully') log('LOGIN', 'Logged in successfully')
} catch (error) { } catch (error) {
log('LOGIN', 'An error occurred:' + error, 'error') log('LOGIN', 'An error occurred:' + JSON.stringify(error, null, 2), 'error')
} }
} }
@@ -103,7 +103,7 @@ async function checkBingLogin(page: Page): Promise<void> {
} }
} catch (error) { } catch (error) {
log('LOGIN-BING', 'An error occurred:' + error, 'error') log('LOGIN-BING', 'An error occurred:' + JSON.stringify(error, null, 2), 'error')
} }
} }

View File

@@ -1,11 +1,14 @@
import { Page } from 'puppeteer' import { Page } from 'puppeteer'
import { DashboardData } from '../interface/DashboardData'
import { doPoll } from './activities/Poll' import { doPoll } from './activities/Poll'
import { doQuiz } from './activities/Quiz' import { doQuiz } from './activities/Quiz'
import { log } from '../util/Logger'
import { doUrlReward } from './activities/UrlReward' import { doUrlReward } from './activities/UrlReward'
import { wait } from '../util/Utils' import { doThisOrThat } from './activities/ThisOrThat'
import { wait } from '../util/Utils'
import { log } from '../util/Logger'
import { DashboardData } from '../interface/DashboardData'
export async function doMorePromotions(page: Page, data: DashboardData) { export async function doMorePromotions(page: Page, data: DashboardData) {
const morePromotions = data.morePromotions const morePromotions = data.morePromotions
@@ -18,6 +21,10 @@ export async function doMorePromotions(page: Page, data: DashboardData) {
} }
for (const activity of activitiesUncompleted) { for (const activity of activitiesUncompleted) {
// If activity does not give points, skip
if (activity.pointProgressMax <= 0) {
continue
}
switch (activity.promotionType) { switch (activity.promotionType) {
// Quiz (Poll/Quiz) // Quiz (Poll/Quiz)
@@ -30,6 +37,12 @@ export async function doMorePromotions(page: Page, data: DashboardData) {
await doPoll(page, activity) await doPoll(page, activity)
break 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 // Quizzes are usually 30-40 points
default: default:
log('ACTIVITY', 'Found promotion activity type: Quiz') log('ACTIVITY', 'Found promotion activity type: Quiz')

View File

@@ -0,0 +1,72 @@
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')
}

View File

@@ -1,9 +1,11 @@
import { Page } from 'puppeteer' import { Page } from 'puppeteer'
import { MorePromotion, PromotionalItem } from '../../interface/DashboardData'
import { getLatestTab } from '../../BrowserUtil' import { getLatestTab } from '../../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, data: PromotionalItem | MorePromotion) { export async function doPoll(page: Page, data: PromotionalItem | MorePromotion) {
log('POLL', 'Trying to complete poll') log('POLL', 'Trying to complete poll')
@@ -18,7 +20,7 @@ export async function doPoll(page: Page, data: PromotionalItem | MorePromotion)
const buttonId = `#btoption${Math.floor(randomNumber(0, 1))}` const buttonId = `#btoption${Math.floor(randomNumber(0, 1))}`
await pollPage.waitForSelector(buttonId) await pollPage.waitForSelector(buttonId, { visible: true, timeout: 5000 })
await pollPage.click(buttonId) await pollPage.click(buttonId)
await wait(2000) await wait(2000)
@@ -26,6 +28,6 @@ export async function doPoll(page: Page, data: PromotionalItem | MorePromotion)
log('POLL', 'Completed the poll successfully') log('POLL', 'Completed the poll successfully')
} catch (error) { } catch (error) {
log('POLL', 'An error occurred:' + error, 'error') log('POLL', 'An error occurred:' + JSON.stringify(error, null, 2), 'error')
} }
} }

View File

@@ -1,10 +1,12 @@
import { Page } from 'puppeteer' import { Page } from 'puppeteer'
import { MorePromotion, PromotionalItem } from '../../interface/DashboardData'
import { getQuizData } from '../../BrowserFunc'
import { wait } from '../../util/Utils'
import { getLatestTab } from '../../BrowserUtil' import { getLatestTab } from '../../BrowserUtil'
import { getQuizData, waitForQuizRefresh } from '../../BrowserFunc'
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, data: PromotionalItem | MorePromotion) { export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion) {
log('QUIZ', 'Trying to complete quiz') log('QUIZ', 'Trying to complete quiz')
@@ -28,7 +30,6 @@ export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion)
await wait(2000) await wait(2000)
const quizData = await getQuizData(quizPage) const quizData = await getQuizData(quizPage)
const questionsRemaining = quizData.maxQuestions - quizData.CorrectlyAnsweredQuestionCount // Amount of questions remaining const questionsRemaining = quizData.maxQuestions - quizData.CorrectlyAnsweredQuestionCount // Amount of questions remaining
// All questions // All questions
@@ -38,7 +39,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}`) const answerSelector = await quizPage.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'))
await wait(500) await wait(500)
@@ -49,7 +50,7 @@ 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 wait(2000) await quizPage.waitForSelector(answer, { visible: true, timeout: 2000 })
// Click the answer on page // Click the answer on page
await quizPage.click(answer) await quizPage.click(answer)
@@ -68,7 +69,7 @@ export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion)
for (let i = 0; i < quizData.numberOfOptions; i++) { for (let i = 0; i < quizData.numberOfOptions; i++) {
const answerSelector = await quizPage.waitForSelector(`#rqAnswerOption${i}`) const answerSelector = await quizPage.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) {
@@ -97,27 +98,7 @@ export async function doQuiz(page: Page, data: PromotionalItem | MorePromotion)
} catch (error) { } catch (error) {
const quizPage = await getLatestTab(page) const quizPage = await getLatestTab(page)
await quizPage.close() await quizPage.close()
log('QUIZ', 'An error occurred:' + error, 'error') log('QUIZ', 'An error occurred:' + JSON.stringify(error, null, 2), 'error')
} }
} }
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
}
}
async function checkQuizCompleted(page: Page) {
try {
await page.waitForSelector('#quizCompleteContainer', { timeout: 1000 })
return true
} catch (error) {
return false
}
}
checkQuizCompleted

View File

@@ -1,16 +1,16 @@
import { Page } from 'puppeteer' import { Page } from 'puppeteer'
import axios from 'axios' import axios from 'axios'
import { getLatestTab } from '../../BrowserUtil'
import { getSearchPoints } from '../../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 { getSearchPoints } from '../../BrowserFunc'
import { searches } from '../../config.json' import { searches } 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'
import { GoogleSearch } from '../../interface/Search' import { GoogleSearch } from '../../interface/Search'
import { getLatestTab } from '../../BrowserUtil'
export async function doSearch(page: Page, data: DashboardData, mobile: boolean) { export async function doSearch(page: Page, data: DashboardData, mobile: boolean) {
const locale = await page.evaluate(() => { const locale = await page.evaluate(() => {
@@ -19,11 +19,11 @@ export async function doSearch(page: Page, data: DashboardData, mobile: boolean)
log('SEARCH-BING', 'Starting bing searches') log('SEARCH-BING', 'Starting bing searches')
const mobileData = data.userStatus.counters.mobileSearch[0] as DashboardImpression // Mobile searches const mobileData = data.userStatus.counters?.mobileSearch ? data.userStatus.counters.mobileSearch[0] : null // Mobile searches
const edgeData = data.userStatus.counters.pcSearch[1] as DashboardImpression // Edge searches const edgeData = data.userStatus.counters.pcSearch[1] as DashboardImpression // Edge searches
const genericData = data.userStatus.counters.pcSearch[0] as DashboardImpression // Normal searches const genericData = data.userStatus.counters.pcSearch[0] as DashboardImpression // Normal searches
let missingPoints = mobile ? let missingPoints = (mobile && mobileData) ?
(mobileData.pointProgressMax - mobileData.pointProgress) : (mobileData.pointProgressMax - mobileData.pointProgress) :
(edgeData.pointProgressMax - edgeData.pointProgress) + (genericData.pointProgressMax - genericData.pointProgress) (edgeData.pointProgressMax - edgeData.pointProgress) + (genericData.pointProgressMax - genericData.pointProgress)
@@ -59,11 +59,11 @@ export async function doSearch(page: Page, data: DashboardData, mobile: boolean)
const newData = await bingSearch(page, searchPage, query, mobile) const newData = await bingSearch(page, searchPage, query, mobile)
const newMobileData = newData.mobileSearch[0] as DashboardImpression // Mobile searches const newMobileData = newData.mobileSearch ? newData.mobileSearch[0] : null // Mobile searches
const newEdgeData = newData.pcSearch[1] as DashboardImpression // Edge searches const newEdgeData = newData.pcSearch[1] as DashboardImpression // Edge searches
const newGenericData = newData.pcSearch[0] as DashboardImpression // Normal searches const newGenericData = newData.pcSearch[0] as DashboardImpression // Normal searches
const newMissingPoints = mobile ? const newMissingPoints = (mobile && newMobileData) ?
(newMobileData.pointProgressMax - newMobileData.pointProgress) : (newMobileData.pointProgressMax - newMobileData.pointProgress) :
(newEdgeData.pointProgressMax - newEdgeData.pointProgress) + (newGenericData.pointProgressMax - newGenericData.pointProgress) (newEdgeData.pointProgressMax - newEdgeData.pointProgress) + (newGenericData.pointProgressMax - newGenericData.pointProgress)
@@ -103,11 +103,11 @@ export async function doSearch(page: Page, data: DashboardData, mobile: boolean)
log('SEARCH-BING-EXTRA', `${missingPoints} Points Remaining | Query: ${term} | Mobile: ${mobile}`) log('SEARCH-BING-EXTRA', `${missingPoints} Points Remaining | Query: ${term} | Mobile: ${mobile}`)
const newData = await bingSearch(page, searchPage, query.topic, mobile) const newData = await bingSearch(page, searchPage, query.topic, mobile)
const newMobileData = newData.mobileSearch[0] as DashboardImpression // Mobile searches const newMobileData = newData.mobileSearch ? newData.mobileSearch[0] : null // Mobile searches
const newEdgeData = newData.pcSearch[1] as DashboardImpression // Edge searches const newEdgeData = newData.pcSearch[1] as DashboardImpression // Edge searches
const newGenericData = newData.pcSearch[0] as DashboardImpression // Normal searches const newGenericData = newData.pcSearch[0] as DashboardImpression // Normal searches
const newMissingPoints = mobile ? const newMissingPoints = (mobile && newMobileData) ?
(newMobileData.pointProgressMax - newMobileData.pointProgress) : (newMobileData.pointProgressMax - newMobileData.pointProgress) :
(newEdgeData.pointProgressMax - newEdgeData.pointProgress) + (newGenericData.pointProgressMax - newGenericData.pointProgress) (newEdgeData.pointProgressMax - newEdgeData.pointProgress) + (newGenericData.pointProgressMax - newGenericData.pointProgress)
@@ -143,7 +143,7 @@ async function bingSearch(page: Page, searchPage: Page, query: string, mobile: b
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
try { try {
const searchBar = '#sb_form_q' const searchBar = '#sb_form_q'
await searchPage.waitForSelector(searchBar, { timeout: 3000 }) await searchPage.waitForSelector(searchBar, { visible: true, timeout: 3000 })
await searchPage.click(searchBar) // Focus on the textarea await searchPage.click(searchBar) // Focus on the textarea
await wait(500) await wait(500)
await searchPage.keyboard.down('Control') await searchPage.keyboard.down('Control')
@@ -169,10 +169,11 @@ async function bingSearch(page: Page, searchPage: Page, query: string, mobile: b
} catch (error) { } catch (error) {
if (i === 5) { if (i === 5) {
log('SEARCH-BING', 'Failed after 5 retries... An error occurred:' + error, 'error') log('SEARCH-BING', 'Failed after 5 retries... An error occurred:' + JSON.stringify(error, null, 2), 'error')
return await getSearchPoints(page) break
} }
log('SEARCH-BING', 'Search failed, An error occurred:' + error, 'error') log('SEARCH-BING', 'Search failed, An error occurred:' + JSON.stringify(error, null, 2), 'error')
log('SEARCH-BING', `Retrying search, attempt ${i}/5`, 'warn') log('SEARCH-BING', `Retrying search, attempt ${i}/5`, 'warn')
await wait(4000) await wait(4000)
@@ -214,7 +215,7 @@ async function getGoogleTrends(locale: string, queryCount: number): Promise<Goog
} }
} catch (error) { } catch (error) {
log('SEARCH-GOOGLE-TRENDS', 'An error occurred:' + error, 'error') log('SEARCH-GOOGLE-TRENDS', 'An error occurred:' + JSON.stringify(error, null, 2), 'error')
} }
} }
@@ -235,7 +236,7 @@ async function getRelatedTerms(term: string): Promise<string[]> {
return response.data[1] as string[] return response.data[1] as string[]
} catch (error) { } catch (error) {
log('SEARCH-BING-RELTATED', 'An error occurred:' + error, 'error') log('SEARCH-BING-RELTATED', 'An error occurred:' + JSON.stringify(error, null, 2), 'error')
} }
return [] return []
} }
@@ -255,7 +256,7 @@ async function randomScroll(page: Page) {
await page.keyboard.press('ArrowDown') await page.keyboard.press('ArrowDown')
} }
} catch (error) { } catch (error) {
log('SEARCH-RANDOM-SCROLL', 'An error occurred:' + error, 'error') log('SEARCH-RANDOM-SCROLL', 'An error occurred:' + JSON.stringify(error, null, 2), 'error')
} }
} }
@@ -301,6 +302,6 @@ async function clickRandomLink(page: Page, mobile: boolean) {
} }
} }
} catch (error) { } catch (error) {
log('SEARCH-RANDOM-CLICK', 'An error occurred:' + error, 'error') log('SEARCH-RANDOM-CLICK', 'An error occurred:' + JSON.stringify(error, null, 2), 'error')
} }
} }

View File

@@ -0,0 +1,39 @@
import { Page } from 'puppeteer'
import { getLatestTab } from '../../BrowserUtil'
import { wait } from '../../util/Utils'
import { log } from '../../util/Logger'
import { PromotionalItem, MorePromotion } from '../../interface/DashboardData'
export async function doThisOrThat(page: Page, data: PromotionalItem | MorePromotion) {
return // Todo
log('THIS-OR-THAT', 'Trying to complete ThisOrThat')
try {
const selector = `[data-bi-id="${data.offerId}"]`
// Wait for page to load and click to load the this or that quiz in a new tab
await page.waitForSelector(selector, { timeout: 5000 })
await page.click(selector)
const thisorthatPage = await getLatestTab(page)
// Check if the quiz has been started or not
const quizNotStarted = await thisorthatPage.waitForSelector('#rqStartQuiz', { visible: true, timeout: 3000 }).then(() => true).catch(() => false)
if (quizNotStarted) {
await thisorthatPage.click('#rqStartQuiz')
} else {
log('THIS-OR-THAT', 'ThisOrThat has already been started, trying to finish it')
}
await wait(2000)
// Solving
log('THIS-OR-THAT', 'Completed the ThisOrthat successfully')
} catch (error) {
log('THIS-OR-THAT', 'An error occurred:' + JSON.stringify(error, null, 2), 'error')
}
}

View File

@@ -1,8 +1,9 @@
import { Page } from 'puppeteer' import { Page } from 'puppeteer'
import { getLatestTab } from '../../BrowserUtil' import { getLatestTab } from '../../BrowserUtil'
import { log } from '../../util/Logger' import { log } from '../../util/Logger'
import { PromotionalItem, MorePromotion } from '../../interface/DashboardData'
import { PromotionalItem, MorePromotion } from '../../interface/DashboardData'
export async function doUrlReward(page: Page, data: PromotionalItem | MorePromotion) { export async function doUrlReward(page: Page, data: PromotionalItem | MorePromotion) {
log('URL-REWARD', 'Trying to complete UrlReward') log('URL-REWARD', 'Trying to complete UrlReward')
@@ -20,7 +21,7 @@ export async function doUrlReward(page: Page, data: PromotionalItem | MorePromot
log('URL-REWARD', 'Completed the UrlReward successfully') log('URL-REWARD', 'Completed the UrlReward successfully')
} catch (error) { } catch (error) {
log('URL-REWARD', 'An error occurred:' + error, 'error') log('URL-REWARD', 'An error occurred:' + JSON.stringify(error, null, 2), 'error')
} }
} }

View File

@@ -99,6 +99,14 @@ async function Mobile(account: Account) {
const data = await getDashboardData(page) const data = await getDashboardData(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')
// Close mobile browser
return await browser.close()
}
// Do mobile searches // Do mobile searches
if (searches.doMobile) { if (searches.doMobile) {
await doSearch(page, data, true) await doSearch(page, data, true)

View File

@@ -1,11 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
export interface DashboardData { export interface DashboardData {
userStatus: UserStatus; userStatus: UserStatus;
promotionalItem: PromotionalItem; promotionalItem: PromotionalItem;
dailySetPromotions: { [key: string]: PromotionalItem[] }; dailySetPromotions: { [key: string]: PromotionalItem[] };
streakPromotion: StreakPromotion; streakPromotion: StreakPromotion;
streakBonusPromotions: StreakBonusPromotion[]; streakBonusPromotions: StreakBonusPromotion[];
punchCards: any[]; punchCards: PunchCard[];
dashboardFlights: DashboardFlights; dashboardFlights: DashboardFlights;
morePromotions: MorePromotion[]; morePromotions: MorePromotion[];
suggestedRewards: AutoRedeemItem[]; suggestedRewards: AutoRedeemItem[];
@@ -188,7 +187,7 @@ export interface Slide {
aboutPageLink: null; aboutPageLink: null;
redeemLink: null; redeemLink: null;
rewardsLink: null; rewardsLink: null;
quizLinks?: any[]; quizLinks?: string[];
quizCorrectAnswerTitle?: string; quizCorrectAnswerTitle?: string;
quizWrongAnswerTitle?: string; quizWrongAnswerTitle?: string;
quizAnswerDescription?: string; quizAnswerDescription?: string;
@@ -210,7 +209,7 @@ export interface PromotionalItem {
activityProgressMax: number; activityProgressMax: number;
pointProgressMax: number; pointProgressMax: number;
pointProgress: number; pointProgress: number;
promotionType: string; promotionType: Type;
promotionSubtype: string; promotionSubtype: string;
title: string; title: string;
extBannerTitle: string; extBannerTitle: string;
@@ -248,9 +247,9 @@ export interface PromotionalItem {
} }
export interface PromotionalItemAttributes { export interface PromotionalItemAttributes {
animated_icon: string; animated_icon?: string;
bg_image: string; bg_image: string;
complete: string; complete: GiveEligible;
daily_set_date?: string; daily_set_date?: string;
description: string; description: string;
destination: string; destination: string;
@@ -263,22 +262,43 @@ export interface PromotionalItemAttributes {
sc_bg_image: string; sc_bg_image: string;
sc_bg_large_image: string; sc_bg_large_image: string;
small_image: string; small_image: string;
state: string; state: State;
title: string; title: string;
type: string; type: Type;
give_eligible: string; give_eligible: GiveEligible;
sc_title?: string; activity_max?: string;
sc_description?: string; activity_progress?: string;
legal_text?: string; is_wot?: GiveEligible;
legal_link_text?: string; offer_counter?: string;
promotional?: string; promotional?: GiveEligible;
parentPunchcards?: string;
'classification.DescriptionText'?: string;
'classification.PunchcardChildrenCount'?: string;
'classification.PunchcardEndDate'?: Date;
'classification.Template'?: string;
'classification.TitleText'?: string;
}
export enum GiveEligible {
False = 'False',
True = 'True'
}
export enum State {
Default = 'Default'
}
export enum Type {
Quiz = 'quiz',
Urlreward = 'urlreward',
UrlrewardUrlrewardUrlrewardUrlrewardUrlreward = 'urlreward,urlreward,urlreward,urlreward,urlreward'
} }
export interface DashboardFlights { export interface DashboardFlights {
dashboardbannernav: string; dashboardbannernav: string;
togglegiveuser: string; togglegiveuser: string;
spotifyRedirect: string; spotifyRedirect: string;
give_eligible: string; give_eligible: GiveEligible;
destination: string; destination: string;
} }
@@ -333,6 +353,12 @@ export interface MorePromotion {
deviceType: string; deviceType: string;
} }
export interface PunchCard {
name: string;
parentPromotion: PromotionalItem;
childPromotions: PromotionalItem[];
}
export interface StreakBonusPromotion { export interface StreakBonusPromotion {
name: string; name: string;
priority: number; priority: number;
@@ -382,17 +408,18 @@ export interface StreakBonusPromotion {
} }
export interface StreakBonusPromotionAttributes { export interface StreakBonusPromotionAttributes {
hidden: string; hidden: GiveEligible;
type: string; type: string;
title: string; title: string;
description: string; description: string;
description_localizedkey: string;
image: string; image: string;
animated_icon: string; animated_icon: string;
activity_progress: string; activity_progress: string;
activity_max: string; activity_max: string;
bonus_earned: string; bonus_earned: string;
break_description: string; break_description: string;
give_eligible: string; give_eligible: GiveEligible;
destination: string; destination: string;
} }
@@ -449,7 +476,7 @@ export interface StreakPromotion {
} }
export interface StreakPromotionAttributes { export interface StreakPromotionAttributes {
hidden: string; hidden: GiveEligible;
type: string; type: string;
title: string; title: string;
image: string; image: string;
@@ -458,7 +485,7 @@ export interface StreakPromotionAttributes {
break_image: string; break_image: string;
lifetime_max: string; lifetime_max: string;
bonus_points: string; bonus_points: string;
give_eligible: string; give_eligible: GiveEligible;
destination: string; destination: string;
} }
@@ -511,8 +538,8 @@ export interface UserInterests {
} }
export interface UserInterestsAttributes { export interface UserInterestsAttributes {
hidden: string; hidden: GiveEligible;
give_eligible: string; give_eligible: GiveEligible;
destination: string; destination: string;
} }
@@ -543,10 +570,9 @@ export interface UserProfileAttributes {
epuid_upd: Date; epuid_upd: Date;
waitlistattributes: string; waitlistattributes: string;
waitlistattributes_upd: Date; waitlistattributes_upd: Date;
serpbotscore: string; cbedc: GiveEligible;
serpbotscore_upd: Date; iscashbackeligible: GiveEligible;
iscashbackeligible: string; give_user: GiveEligible;
give_user: string;
} }
export interface UserStatus { export interface UserStatus {
@@ -571,7 +597,7 @@ export interface UserStatus {
export interface Counters { export interface Counters {
pcSearch: DashboardImpression[]; pcSearch: DashboardImpression[];
mobileSearch: DashboardImpression[]; mobileSearch?: DashboardImpression[];
shopAndEarn: DashboardImpression[]; shopAndEarn: DashboardImpression[];
activityAndQuiz: ActivityAndQuiz[]; activityAndQuiz: ActivityAndQuiz[];
dailyPoint: DashboardImpression[]; dailyPoint: DashboardImpression[];
@@ -634,9 +660,9 @@ export interface ActivityAndQuizAttributes {
image: string; image: string;
recurring: string; recurring: string;
destination: string; destination: string;
'classification.ShowProgress': string; 'classification.ShowProgress': GiveEligible;
hidden: string; hidden: GiveEligible;
give_eligible: string; give_eligible: GiveEligible;
} }
export interface LastOrder { export interface LastOrder {
@@ -669,5 +695,5 @@ export interface ReferrerProgressInfo {
pointsEarned: number; pointsEarned: number;
pointsMax: number; pointsMax: number;
isComplete: boolean; isComplete: boolean;
promotions: any[]; promotions: string[];
} }

View File

@@ -1,5 +1,7 @@
import axios from 'axios' import axios from 'axios'
import { log } from './Logger' import { log } from './Logger'
import { ChromeVersion, EdgeVersion } from '../interface/UserAgentUtil' import { ChromeVersion, EdgeVersion } from '../interface/UserAgentUtil'
export async function getUserAgent(mobile: boolean) { export async function getUserAgent(mobile: boolean) {
@@ -49,7 +51,7 @@ export async function getChromeVersion(): Promise<string> {
return data.channels.Stable.version return data.channels.Stable.version
} catch (error) { } catch (error) {
throw log('USERAGENT-CHROME-VERSION', 'An error occurred:' + error, 'error') throw log('USERAGENT-CHROME-VERSION', 'An error occurred:' + JSON.stringify(error, null, 2), 'error')
} }
} }
@@ -73,7 +75,7 @@ export async function getEdgeVersions() {
} catch (error) { } catch (error) {
throw log('USERAGENT-EDGE-VERSION', 'An error occurred:' + error, 'error') throw log('USERAGENT-EDGE-VERSION', 'An error occurred:' + JSON.stringify(error, null, 2), 'error')
} }
} }

View File

@@ -3,8 +3,7 @@ import axios from 'axios'
import { webhook } from '../config.json' import { webhook } from '../config.json'
export async function Webhook(content: string) { export async function Webhook(content: string) {
if (!webhook.enabled) return if (!webhook.enabled || webhook.url.length < 10) return
if (webhook.url.length < 10) return
const request = { const request = {
method: 'POST', method: 'POST',