mirror of
https://github.com/TheNetsky/Microsoft-Rewards-Script.git
synced 2026-01-11 10:56:17 +00:00
1.2.2
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "microsoft-rewards-script",
|
"name": "microsoft-rewards-script",
|
||||||
"version": "1.2.1",
|
"version": "1.2.2",
|
||||||
"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",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -32,9 +32,9 @@
|
|||||||
"cheerio": "^1.0.0-rc.12",
|
"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.4.1",
|
||||||
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,28 @@
|
|||||||
import puppeteer from 'puppeteer-extra'
|
import puppeteer from 'puppeteer-extra'
|
||||||
import stealthPlugin from 'puppeteer-extra-plugin-stealth'
|
import stealthPlugin from 'puppeteer-extra-plugin-stealth'
|
||||||
|
|
||||||
|
import { MicrosoftRewardsBot } from '../index'
|
||||||
|
|
||||||
import { getUserAgent } from '../util/UserAgent'
|
import { getUserAgent } from '../util/UserAgent'
|
||||||
import { loadSesion } from './BrowserFunc'
|
|
||||||
|
|
||||||
import { AccountProxy } from '../interface/Account'
|
import { AccountProxy } from '../interface/Account'
|
||||||
|
|
||||||
import { headless } from '../config.json'
|
|
||||||
|
|
||||||
puppeteer.use(stealthPlugin())
|
puppeteer.use(stealthPlugin())
|
||||||
|
|
||||||
|
|
||||||
class Browser {
|
class Browser {
|
||||||
|
private bot: MicrosoftRewardsBot
|
||||||
|
|
||||||
|
constructor(bot: MicrosoftRewardsBot) {
|
||||||
|
this.bot = bot
|
||||||
|
}
|
||||||
|
|
||||||
async createBrowser(email: string, proxy: AccountProxy, isMobile: boolean) {
|
async createBrowser(email: string, proxy: AccountProxy, isMobile: boolean) {
|
||||||
const userAgent = await getUserAgent(isMobile)
|
const userAgent = await getUserAgent(isMobile)
|
||||||
|
|
||||||
const browser = await puppeteer.launch({
|
const browser = await puppeteer.launch({
|
||||||
headless: headless,
|
headless: this.bot.config.headless,
|
||||||
userDataDir: await loadSesion(email),
|
userDataDir: await this.bot.browser.func.loadSesion(email),
|
||||||
args: [
|
args: [
|
||||||
'--no-sandbox',
|
'--no-sandbox',
|
||||||
'--mute-audio',
|
'--mute-audio',
|
||||||
|
|||||||
@@ -1,248 +1,254 @@
|
|||||||
import { Page } from 'puppeteer'
|
import { Page } from 'puppeteer'
|
||||||
|
import { CheerioAPI, load } from 'cheerio'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { CheerioAPI, load } from 'cheerio'
|
|
||||||
|
|
||||||
import { tryDismissAllMessages, tryDismissCookieBanner } from './BrowserUtil'
|
import { MicrosoftRewardsBot } from '../index'
|
||||||
import { getFormattedDate, wait } from './../util/Utils'
|
|
||||||
import { log } from './../util/Logger'
|
|
||||||
|
|
||||||
import { Counters, DashboardData, MorePromotion, PromotionalItem } from './../interface/DashboardData'
|
import { Counters, DashboardData, MorePromotion, PromotionalItem } 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 default class BrowserFunc {
|
||||||
|
private bot: MicrosoftRewardsBot
|
||||||
|
|
||||||
try {
|
constructor(bot: MicrosoftRewardsBot) {
|
||||||
const dashboardURL = new URL(baseURL)
|
this.bot = bot
|
||||||
|
|
||||||
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!', 'error')
|
|
||||||
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
|
async goHome(page: Page): Promise<boolean> {
|
||||||
}
|
|
||||||
|
|
||||||
export async function getDashboardData(page: Page): Promise<DashboardData> {
|
try {
|
||||||
const dashboardURL = new URL(baseURL)
|
const dashboardURL = new URL(this.bot.config.baseURL)
|
||||||
const currentURL = new URL(page.url())
|
|
||||||
|
|
||||||
// Should never happen since tasks are opened in a new tab!
|
await page.goto(this.bot.config.baseURL)
|
||||||
if (currentURL.hostname !== dashboardURL.hostname) {
|
|
||||||
log('DASHBOARD-DATA', 'Provided page did not equal dashboard page, redirecting to dashboard page')
|
const maxIterations = 5 // Maximum iterations set to 5
|
||||||
await goHome(page)
|
|
||||||
|
for (let iteration = 1; iteration <= maxIterations; iteration++) {
|
||||||
|
await this.bot.utils.wait(3000)
|
||||||
|
await this.bot.browser.utils.tryDismissCookieBanner(page)
|
||||||
|
|
||||||
|
// Check if account is suspended
|
||||||
|
const isSuspended = await page.waitForSelector('#suspendedAccountHeader', { visible: true, timeout: 3000 }).then(() => true).catch(() => false)
|
||||||
|
if (isSuspended) {
|
||||||
|
this.bot.log('GO-HOME', 'This account is suspended!', 'error')
|
||||||
|
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 this.bot.browser.utils.tryDismissAllMessages(page)
|
||||||
|
|
||||||
|
await this.bot.utils.wait(2000)
|
||||||
|
await page.goto(this.bot.config.baseURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.bot.utils.wait(5000)
|
||||||
|
this.bot.log('GO-HOME', 'Visited homepage successfully')
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('An error occurred:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload the page to get new data
|
async getDashboardData(page: Page): Promise<DashboardData> {
|
||||||
await page.reload({ waitUntil: 'networkidle2' })
|
const dashboardURL = new URL(this.bot.config.baseURL)
|
||||||
|
const currentURL = new URL(page.url())
|
||||||
|
|
||||||
const scriptContent = await page.evaluate(() => {
|
// Should never happen since tasks are opened in a new tab!
|
||||||
const scripts = Array.from(document.querySelectorAll('script'))
|
if (currentURL.hostname !== dashboardURL.hostname) {
|
||||||
const targetScript = scripts.find(script => script.innerText.includes('var dashboard'))
|
this.bot.log('DASHBOARD-DATA', 'Provided page did not equal dashboard page, redirecting to dashboard page')
|
||||||
|
await this.goHome(page)
|
||||||
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
|
// Reload the page to get new data
|
||||||
const dashboardData = await page.evaluate(scriptContent => {
|
await page.reload({ waitUntil: 'networkidle2' })
|
||||||
// Extract the dashboard object using regex
|
|
||||||
const regex = /var dashboard = (\{.*?\});/s
|
|
||||||
const match = regex.exec(scriptContent)
|
|
||||||
|
|
||||||
if (match && match[1]) {
|
const scriptContent = await page.evaluate(() => {
|
||||||
return JSON.parse(match[1])
|
const scripts = Array.from(document.querySelectorAll('script'))
|
||||||
} else {
|
const targetScript = scripts.find(script => script.innerText.includes('var dashboard'))
|
||||||
throw log('GET-DASHBOARD-DATA', 'Dashboard data not found within script', 'error')
|
|
||||||
}
|
|
||||||
}, scriptContent)
|
|
||||||
|
|
||||||
return dashboardData
|
if (targetScript) {
|
||||||
}
|
return targetScript.innerText
|
||||||
|
|
||||||
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 {
|
} else {
|
||||||
throw log('GET-QUIZ-DATA', 'Quiz data not found within script', 'error')
|
throw this.bot.log('GET-DASHBOARD-DATA', 'Script containing dashboard data not found', '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
|
// Extract the dashboard object from the script content
|
||||||
} catch (error) {
|
const dashboardData = await page.evaluate(scriptContent => {
|
||||||
throw log('GET-EARNABLE-POINTS', 'An error occurred:' + error, 'error')
|
// Extract the dashboard object using regex
|
||||||
}
|
const regex = /var dashboard = (\{.*?\});/s
|
||||||
}
|
const match = regex.exec(scriptContent)
|
||||||
|
|
||||||
export async function getCurrentPoints(data: DashboardData, page: null | Page = null): Promise<number> {
|
if (match && match[1]) {
|
||||||
try {
|
return JSON.parse(match[1])
|
||||||
// Fetch new data if page is provided
|
} else {
|
||||||
if (page) {
|
throw this.bot.log('GET-DASHBOARD-DATA', 'Dashboard data not found within script', 'error')
|
||||||
data = await getDashboardData(page)
|
}
|
||||||
|
}, scriptContent)
|
||||||
|
|
||||||
|
return dashboardData
|
||||||
|
}
|
||||||
|
|
||||||
|
async 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 this.bot.log('GET-QUIZ-DATA', 'Quiz data not found within script', 'error')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw this.bot.log('GET-QUIZ-DATA', 'Script containing quiz data not found', 'error')
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
throw this.bot.log('GET-QUIZ-DATA', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
return data.userStatus.availablePoints
|
|
||||||
} catch (error) {
|
|
||||||
throw log('GET-CURRENT-POINTS', 'An error occurred:' + error, 'error')
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export async function loadSesion(email: string): Promise<string> {
|
async getSearchPoints(page: Page): Promise<Counters> {
|
||||||
const sessionDir = path.join(__dirname, sessionPath, email)
|
const dashboardData = await this.getDashboardData(page) // Always fetch newest data
|
||||||
|
|
||||||
try {
|
return dashboardData.userStatus.counters
|
||||||
// Create session dir
|
}
|
||||||
if (!fs.existsSync(sessionDir)) {
|
|
||||||
await fs.promises.mkdir(sessionDir, { recursive: true })
|
async getEarnablePoints(data: DashboardData, page: null | Page = null): Promise<number> {
|
||||||
|
try {
|
||||||
|
// Fetch new data if page is provided
|
||||||
|
if (page) {
|
||||||
|
data = await this.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[this.bot.utils.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 this.bot.log('GET-EARNABLE-POINTS', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
return sessionDir
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error(error as string)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export async function waitForQuizRefresh(page: Page): Promise<boolean> {
|
async getCurrentPoints(data: DashboardData, page: null | Page = null): Promise<number> {
|
||||||
try {
|
try {
|
||||||
await page.waitForSelector('#rqHeaderCredits', { visible: true, timeout: 5000 })
|
// Fetch new data if page is provided
|
||||||
await wait(2000)
|
if (page) {
|
||||||
|
data = await this.getDashboardData(page)
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return data.userStatus.availablePoints
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log('QUIZ-REFRESH', 'An error occurred:' + error, 'error')
|
throw this.bot.log('GET-CURRENT-POINTS', 'An error occurred:' + error, 'error')
|
||||||
return false
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export async function checkQuizCompleted(page: Page): Promise<boolean> {
|
async loadSesion(email: string): Promise<string> {
|
||||||
try {
|
const sessionDir = path.join(__dirname, this.bot.config.sessionPath, email)
|
||||||
await page.waitForSelector('#quizCompleteContainer', { visible: true, timeout: 1000 })
|
|
||||||
await wait(2000)
|
|
||||||
|
|
||||||
return true
|
try {
|
||||||
} catch (error) {
|
// Create session dir
|
||||||
return false
|
if (!fs.existsSync(sessionDir)) {
|
||||||
|
await fs.promises.mkdir(sessionDir, { recursive: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
return sessionDir
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error as string)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export async function refreshCheerio(page: Page): Promise<CheerioAPI> {
|
async waitForQuizRefresh(page: Page): Promise<boolean> {
|
||||||
const html = await page.content()
|
try {
|
||||||
const $ = load(html)
|
await page.waitForSelector('#rqHeaderCredits', { visible: true, timeout: 5000 })
|
||||||
|
await this.bot.utils.wait(2000)
|
||||||
|
|
||||||
return $
|
return true
|
||||||
}
|
} catch (error) {
|
||||||
|
this.bot.log('QUIZ-REFRESH', 'An error occurred:' + error, 'error')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function getPunchCardActivity(page: Page, activity: PromotionalItem | MorePromotion): Promise<string> {
|
async checkQuizCompleted(page: Page): Promise<boolean> {
|
||||||
let selector = ''
|
try {
|
||||||
try {
|
await page.waitForSelector('#quizCompleteContainer', { visible: true, timeout: 1000 })
|
||||||
|
await this.bot.utils.wait(2000)
|
||||||
|
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async refreshCheerio(page: Page): Promise<CheerioAPI> {
|
||||||
const html = await page.content()
|
const html = await page.content()
|
||||||
const $ = load(html)
|
const $ = load(html)
|
||||||
|
|
||||||
const element = $('.offer-cta').toArray().find(x => x.attribs.href?.includes(activity.offerId))
|
return $
|
||||||
if (element) {
|
}
|
||||||
selector = `a[href*="${element.attribs.href}"]`
|
|
||||||
}
|
async getPunchCardActivity(page: Page, activity: PromotionalItem | MorePromotion): Promise<string> {
|
||||||
} catch (error) {
|
let selector = ''
|
||||||
log('GET-PUNCHCARD-ACTIVITY', 'An error occurred:' + error, 'error')
|
try {
|
||||||
|
const html = await page.content()
|
||||||
|
const $ = load(html)
|
||||||
|
|
||||||
|
const element = $('.offer-cta').toArray().find(x => x.attribs.href?.includes(activity.offerId))
|
||||||
|
if (element) {
|
||||||
|
selector = `a[href*="${element.attribs.href}"]`
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.bot.log('GET-PUNCHCARD-ACTIVITY', 'An error occurred:' + error, 'error')
|
||||||
|
}
|
||||||
|
|
||||||
|
return selector
|
||||||
}
|
}
|
||||||
|
|
||||||
return selector
|
|
||||||
}
|
}
|
||||||
@@ -1,81 +1,90 @@
|
|||||||
import { Page } from 'puppeteer'
|
import { Page } from 'puppeteer'
|
||||||
|
|
||||||
import { wait } from './../util/Utils'
|
import { MicrosoftRewardsBot } from '../index'
|
||||||
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
|
export default class BrowserUtil {
|
||||||
|
private bot: MicrosoftRewardsBot
|
||||||
|
|
||||||
for (const button of buttons) {
|
constructor(bot: MicrosoftRewardsBot) {
|
||||||
|
this.bot = bot
|
||||||
|
}
|
||||||
|
|
||||||
|
async 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
|
||||||
|
}
|
||||||
|
|
||||||
|
async tryDismissCookieBanner(page: Page): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const element = await page.waitForSelector(button.selector, { visible: true, timeout: 1000 })
|
await page.waitForSelector('#cookieConsentContainer', { timeout: 1000 })
|
||||||
if (element) {
|
const cookieBanner = await page.$('#cookieConsentContainer')
|
||||||
await element.click()
|
|
||||||
result = true
|
if (cookieBanner) {
|
||||||
|
const button = await cookieBanner.$('button')
|
||||||
|
if (button) {
|
||||||
|
await button.click()
|
||||||
|
await this.bot.utils.wait(2000)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
continue
|
// Continue if element is not found or other error occurs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
async tryDismissBingCookieBanner(page: Page): Promise<void> {
|
||||||
}
|
try {
|
||||||
|
await page.waitForSelector('#bnp_btn_accept', { timeout: 1000 })
|
||||||
|
const cookieBanner = await page.$('#bnp_btn_accept')
|
||||||
|
|
||||||
export async function tryDismissCookieBanner(page: Page): Promise<void> {
|
if (cookieBanner) {
|
||||||
try {
|
await cookieBanner.click()
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
// Continue if element is not found or other error occurs
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export async function tryDismissBingCookieBanner(page: Page): Promise<void> {
|
async getLatestTab(page: Page): Promise<Page> {
|
||||||
try {
|
try {
|
||||||
await page.waitForSelector('#bnp_btn_accept', { timeout: 1000 })
|
await this.bot.utils.wait(500)
|
||||||
const cookieBanner = await page.$('#bnp_btn_accept')
|
|
||||||
|
|
||||||
if (cookieBanner) {
|
const browser = page.browser()
|
||||||
await cookieBanner.click()
|
const pages = await browser.pages()
|
||||||
|
const newTab = pages[pages.length - 1]
|
||||||
|
|
||||||
|
if (newTab) {
|
||||||
|
return newTab
|
||||||
|
}
|
||||||
|
|
||||||
|
throw this.bot.log('GET-NEW-TAB', 'Unable to get latest tab', 'error')
|
||||||
|
} catch (error) {
|
||||||
|
throw this.bot.log('GET-NEW-TAB', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
// Continue if element is not found or other error occurs
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export async function getLatestTab(page: Page): Promise<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')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
"baseURL": "https://rewards.bing.com",
|
"baseURL": "https://rewards.bing.com",
|
||||||
"sessionPath": "sessions",
|
"sessionPath": "sessions",
|
||||||
"headless": false,
|
"headless": false,
|
||||||
"runOnZeroPoints": true,
|
"runOnZeroPoints": false,
|
||||||
"clusters": 1,
|
"clusters": 1,
|
||||||
"workers": {
|
"workers": {
|
||||||
"doDailySet": true,
|
"doDailySet": true,
|
||||||
@@ -14,7 +14,11 @@
|
|||||||
"searchSettings": {
|
"searchSettings": {
|
||||||
"useGeoLocaleQueries": false,
|
"useGeoLocaleQueries": false,
|
||||||
"scrollRandomResults": true,
|
"scrollRandomResults": true,
|
||||||
"clickRandomResults": true
|
"clickRandomResults": true,
|
||||||
|
"searchDelay": {
|
||||||
|
"min": 10000,
|
||||||
|
"max": 20000
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"webhook": {
|
"webhook": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
|
|||||||
52
src/functions/Activities.ts
Normal file
52
src/functions/Activities.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { Page } from 'puppeteer'
|
||||||
|
|
||||||
|
import { MicrosoftRewardsBot } from '../index'
|
||||||
|
|
||||||
|
import { Search } from './activities/Search'
|
||||||
|
import { ABC } from './activities/ABC'
|
||||||
|
import { Poll } from './activities/Poll'
|
||||||
|
import { Quiz } from './activities/Quiz'
|
||||||
|
import { ThisOrThat } from './activities/ThisOrThat'
|
||||||
|
import { UrlReward } from './activities/UrlReward'
|
||||||
|
|
||||||
|
import { DashboardData } from '../interface/DashboardData'
|
||||||
|
|
||||||
|
|
||||||
|
export default class Activities {
|
||||||
|
private bot: MicrosoftRewardsBot
|
||||||
|
|
||||||
|
constructor(bot: MicrosoftRewardsBot) {
|
||||||
|
this.bot = bot
|
||||||
|
}
|
||||||
|
|
||||||
|
doSearch = async (page: Page, data: DashboardData, mobile: boolean): Promise<void> => {
|
||||||
|
const search = new Search(this.bot)
|
||||||
|
await search.doSearch(page, data, mobile)
|
||||||
|
}
|
||||||
|
|
||||||
|
doABC = async (page: Page): Promise<void> => {
|
||||||
|
const abc = new ABC(this.bot)
|
||||||
|
await abc.doABC(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
doPoll = async (page: Page): Promise<void> => {
|
||||||
|
const poll = new Poll(this.bot)
|
||||||
|
await poll.doPoll(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
doThisOrThat = async (page: Page): Promise<void> => {
|
||||||
|
const thisOrThat = new ThisOrThat(this.bot)
|
||||||
|
await thisOrThat.doThisOrThat(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
doQuiz = async (page: Page): Promise<void> => {
|
||||||
|
const quiz = new Quiz(this.bot)
|
||||||
|
await quiz.doQuiz(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
doUrlReward = async (page: Page): Promise<void> => {
|
||||||
|
const urlReward = new UrlReward(this.bot)
|
||||||
|
await urlReward.doUrlReward(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,125 +1,133 @@
|
|||||||
import { Page } from 'puppeteer'
|
import { Page } from 'puppeteer'
|
||||||
import readline from 'readline'
|
import readline from 'readline'
|
||||||
|
|
||||||
|
import { MicrosoftRewardsBot } from '../index'
|
||||||
|
|
||||||
const rl = readline.createInterface({
|
const rl = readline.createInterface({
|
||||||
input: process.stdin,
|
input: process.stdin,
|
||||||
output: process.stdout
|
output: process.stdout
|
||||||
})
|
})
|
||||||
|
|
||||||
import { tryDismissAllMessages, tryDismissBingCookieBanner } from '../browser/BrowserUtil'
|
|
||||||
import { wait } from '../util/Utils'
|
|
||||||
import { log } from '../util/Logger'
|
|
||||||
|
|
||||||
export async function login(page: Page, email: string, password: string) {
|
export class Login {
|
||||||
|
private bot: MicrosoftRewardsBot
|
||||||
|
|
||||||
try {
|
constructor(bot: MicrosoftRewardsBot) {
|
||||||
// Navigate to the Bing login page
|
this.bot = bot
|
||||||
await page.goto('https://login.live.com/')
|
}
|
||||||
|
|
||||||
const isLoggedIn = await page.waitForSelector('html[data-role-name="MeePortal"]', { timeout: 5000 }).then(() => true).catch(() => false)
|
async login(page: Page, email: string, password: string) {
|
||||||
|
|
||||||
if (!isLoggedIn) {
|
try {
|
||||||
const isLocked = await page.waitForSelector('.serviceAbusePageContainer', { visible: true, timeout: 5000 }).then(() => true).catch(() => false)
|
// Navigate to the Bing login page
|
||||||
if (isLocked) {
|
await page.goto('https://login.live.com/')
|
||||||
log('LOGIN', 'This account has been locked!', 'error')
|
|
||||||
throw new Error('Account has been locked!')
|
const isLoggedIn = await page.waitForSelector('html[data-role-name="MeePortal"]', { timeout: 5000 }).then(() => true).catch(() => false)
|
||||||
|
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
const isLocked = await page.waitForSelector('.serviceAbusePageContainer', { visible: true, timeout: 5000 }).then(() => true).catch(() => false)
|
||||||
|
if (isLocked) {
|
||||||
|
this.bot.log('LOGIN', 'This account has been locked!', 'error')
|
||||||
|
throw new Error('Account has been locked!')
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.waitForSelector('#loginHeader', { visible: true, timeout: 10_000 })
|
||||||
|
|
||||||
|
await this.execLogin(page, email, password)
|
||||||
|
this.bot.log('LOGIN', 'Logged into Microsoft successfully')
|
||||||
|
} else {
|
||||||
|
this.bot.log('LOGIN', 'Already logged in')
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.waitForSelector('#loginHeader', { visible: true, timeout: 10_000 })
|
// Check if logged in to bing
|
||||||
|
await this.checkBingLogin(page)
|
||||||
|
|
||||||
await execLogin(page, email, password)
|
// We're done logging in
|
||||||
log('LOGIN', 'Logged into Microsoft successfully')
|
this.bot.log('LOGIN', 'Logged in successfully')
|
||||||
} else {
|
|
||||||
log('LOGIN', 'Already logged in')
|
} catch (error) {
|
||||||
|
// Throw and don't continue
|
||||||
|
throw this.bot.log('LOGIN', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if logged in to bing
|
|
||||||
await checkBingLogin(page)
|
|
||||||
|
|
||||||
// We're done logging in
|
|
||||||
log('LOGIN', 'Logged in successfully')
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
// Throw and don't continue
|
|
||||||
throw log('LOGIN', 'An error occurred:' + error, 'error')
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async function execLogin(page: Page, email: string, password: string) {
|
private async execLogin(page: Page, email: string, password: string) {
|
||||||
await page.type('#i0116', email)
|
await page.type('#i0116', email)
|
||||||
await page.click('#idSIButton9')
|
|
||||||
|
|
||||||
log('LOGIN', 'Email entered successfully')
|
|
||||||
|
|
||||||
try {
|
|
||||||
await page.waitForSelector('#i0118', { visible: true, timeout: 2000 })
|
|
||||||
await wait(2000)
|
|
||||||
|
|
||||||
await page.type('#i0118', password)
|
|
||||||
await page.click('#idSIButton9')
|
await page.click('#idSIButton9')
|
||||||
|
|
||||||
} catch (error) {
|
this.bot.log('LOGIN', 'Email entered successfully')
|
||||||
log('LOGIN', '2FA code required')
|
|
||||||
|
|
||||||
const code = await new Promise<string>((resolve) => {
|
try {
|
||||||
rl.question('Enter 2FA code:\n', (input) => {
|
await page.waitForSelector('#i0118', { visible: true, timeout: 2000 })
|
||||||
rl.close()
|
await this.bot.utils.wait(2000)
|
||||||
resolve(input)
|
|
||||||
|
await page.type('#i0118', password)
|
||||||
|
await page.click('#idSIButton9')
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.bot.log('LOGIN', '2FA code required')
|
||||||
|
|
||||||
|
const code = await new Promise<string>((resolve) => {
|
||||||
|
rl.question('Enter 2FA code:\n', (input) => {
|
||||||
|
rl.close()
|
||||||
|
resolve(input)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
await page.type('input[name="otc"]', code)
|
await page.type('input[name="otc"]', code)
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
log('LOGIN', 'Password entered successfully')
|
this.bot.log('LOGIN', 'Password entered successfully')
|
||||||
}
|
|
||||||
|
|
||||||
const currentURL = new URL(page.url())
|
|
||||||
|
|
||||||
while (currentURL.pathname !== '/' || currentURL.hostname !== 'account.microsoft.com') {
|
|
||||||
await tryDismissAllMessages(page)
|
|
||||||
currentURL.href = page.url()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for login to complete
|
|
||||||
await page.waitForSelector('html[data-role-name="MeePortal"]', { timeout: 10_000 })
|
|
||||||
}
|
|
||||||
|
|
||||||
async function checkBingLogin(page: Page): Promise<void> {
|
|
||||||
try {
|
|
||||||
log('LOGIN-BING', 'Verifying Bing login')
|
|
||||||
await page.goto('https://www.bing.com/fd/auth/signin?action=interactive&provider=windows_live_id&return_url=https%3A%2F%2Fwww.bing.com%2F')
|
|
||||||
|
|
||||||
const maxIterations = 5
|
|
||||||
|
|
||||||
for (let iteration = 1; iteration <= maxIterations; iteration++) {
|
|
||||||
const currentUrl = new URL(page.url())
|
|
||||||
|
|
||||||
if (currentUrl.hostname === 'www.bing.com' && currentUrl.pathname === '/') {
|
|
||||||
await wait(3000)
|
|
||||||
await tryDismissBingCookieBanner(page)
|
|
||||||
|
|
||||||
const loggedIn = await checkBingLoginStatus(page)
|
|
||||||
if (loggedIn) {
|
|
||||||
log('LOGIN-BING', 'Bing login verification passed!')
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await wait(1000)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
const currentURL = new URL(page.url())
|
||||||
log('LOGIN-BING', 'An error occurred:' + error, 'error')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function checkBingLoginStatus(page: Page): Promise<boolean> {
|
while (currentURL.pathname !== '/' || currentURL.hostname !== 'account.microsoft.com') {
|
||||||
try {
|
await this.bot.browser.utils.tryDismissAllMessages(page)
|
||||||
await page.waitForSelector('#id_n', { timeout: 5000 })
|
currentURL.href = page.url()
|
||||||
return true
|
}
|
||||||
} catch (error) {
|
|
||||||
return false
|
// Wait for login to complete
|
||||||
|
await page.waitForSelector('html[data-role-name="MeePortal"]', { timeout: 10_000 })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async checkBingLogin(page: Page): Promise<void> {
|
||||||
|
try {
|
||||||
|
this.bot.log('LOGIN-BING', 'Verifying Bing login')
|
||||||
|
await page.goto('https://www.bing.com/fd/auth/signin?action=interactive&provider=windows_live_id&return_url=https%3A%2F%2Fwww.bing.com%2F')
|
||||||
|
|
||||||
|
const maxIterations = 5
|
||||||
|
|
||||||
|
for (let iteration = 1; iteration <= maxIterations; iteration++) {
|
||||||
|
const currentUrl = new URL(page.url())
|
||||||
|
|
||||||
|
if (currentUrl.hostname === 'www.bing.com' && currentUrl.pathname === '/') {
|
||||||
|
await this.bot.utils.wait(3000)
|
||||||
|
await this.bot.browser.utils.tryDismissBingCookieBanner(page)
|
||||||
|
|
||||||
|
const loggedIn = await this.checkBingLoginStatus(page)
|
||||||
|
if (loggedIn) {
|
||||||
|
this.bot.log('LOGIN-BING', 'Bing login verification passed!')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.bot.utils.wait(1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.bot.log('LOGIN-BING', 'An error occurred:' + error, 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async checkBingLoginStatus(page: Page): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await page.waitForSelector('#id_n', { timeout: 5000 })
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,173 +1,176 @@
|
|||||||
import { Page } from 'puppeteer'
|
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 { getPunchCardActivity } from '../browser/BrowserFunc'
|
|
||||||
import { getLatestTab } from '../browser/BrowserUtil'
|
|
||||||
|
|
||||||
import { getFormattedDate, wait } from '../util/Utils'
|
|
||||||
import { log } from '../util/Logger'
|
|
||||||
|
|
||||||
import { DashboardData, MorePromotion, PromotionalItem, PunchCard } from '../interface/DashboardData'
|
import { DashboardData, MorePromotion, PromotionalItem, PunchCard } from '../interface/DashboardData'
|
||||||
|
|
||||||
import { baseURL } from '../config.json'
|
import { MicrosoftRewardsBot } from '../index'
|
||||||
|
|
||||||
// Daily Set
|
export class Workers {
|
||||||
export async function doDailySet(page: Page, data: DashboardData) {
|
public bot: MicrosoftRewardsBot
|
||||||
|
|
||||||
const todayData = data.dailySetPromotions[getFormattedDate()]
|
constructor(bot: MicrosoftRewardsBot) {
|
||||||
|
this.bot = bot
|
||||||
const activitiesUncompleted = todayData?.filter(x => !x.complete && x.pointProgressMax > 0) ?? []
|
|
||||||
|
|
||||||
if (!activitiesUncompleted.length) {
|
|
||||||
log('DAILY-SET', 'All Daily Set" items have already been completed')
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Solve Activities
|
// Daily Set
|
||||||
log('DAILY-SET', 'Started solving "Daily Set" items')
|
async doDailySet(page: Page, data: DashboardData) {
|
||||||
|
const todayData = data.dailySetPromotions[this.bot.utils.getFormattedDate()]
|
||||||
|
|
||||||
await solveActivities(page, activitiesUncompleted)
|
const activitiesUncompleted = todayData?.filter(x => !x.complete && x.pointProgressMax > 0) ?? []
|
||||||
|
|
||||||
log('DAILY-SET', 'All "Daily Set" items have been completed')
|
if (!activitiesUncompleted.length) {
|
||||||
}
|
this.bot.log('DAILY-SET', 'All Daily Set" items have already been completed')
|
||||||
|
return
|
||||||
// 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 punchCard of punchCardsUncompleted) {
|
|
||||||
const activitiesUncompleted = punchCard.childPromotions.filter(x => !x.complete) // Only return uncompleted activities
|
|
||||||
|
|
||||||
// Solve Activities
|
// Solve Activities
|
||||||
log('PUNCH-CARD', `Started solving "Punch Card" items for punchcard: "${punchCard.parentPromotion.title}"`)
|
this.bot.log('DAILY-SET', 'Started solving "Daily Set" items')
|
||||||
|
|
||||||
const browser = page.browser()
|
await this.solveActivities(page, activitiesUncompleted)
|
||||||
page = await browser.newPage()
|
|
||||||
|
|
||||||
// Got to punch card index page in a new tab
|
this.bot.log('DAILY-SET', 'All "Daily Set" items have been completed')
|
||||||
await page.goto(punchCard.parentPromotion.destinationUrl, { referer: baseURL })
|
|
||||||
|
|
||||||
await solveActivities(page, activitiesUncompleted, punchCard)
|
|
||||||
|
|
||||||
// Close the punch card index page
|
|
||||||
await page.close()
|
|
||||||
|
|
||||||
log('PUNCH-CARD', `All items for punchcard: "${punchCard.parentPromotion.title}" have been completed`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log('PUNCH-CARD', 'All "Punch Card" items have been completed')
|
// Punch Card
|
||||||
}
|
async doPunchCard(page: Page, data: DashboardData) {
|
||||||
|
|
||||||
// More Promotions
|
const punchCardsUncompleted = data.punchCards?.filter(x => !x.parentPromotion.complete) ?? [] // Only return uncompleted punch cards
|
||||||
export async function doMorePromotions(page: Page, data: DashboardData) {
|
|
||||||
const morePromotions = data.morePromotions
|
|
||||||
|
|
||||||
// Check if there is a promotional item
|
if (!punchCardsUncompleted.length) {
|
||||||
if (data.promotionalItem) { // Convert and add the promotional item to the array
|
this.bot.log('PUNCH-CARD', 'All "Punch Cards" have already been completed')
|
||||||
morePromotions.push(data.promotionalItem as unknown as MorePromotion)
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const punchCard of punchCardsUncompleted) {
|
||||||
|
const activitiesUncompleted = punchCard.childPromotions.filter(x => !x.complete) // Only return uncompleted activities
|
||||||
|
|
||||||
|
// Solve Activities
|
||||||
|
this.bot.log('PUNCH-CARD', `Started solving "Punch Card" items for punchcard: "${punchCard.parentPromotion.title}"`)
|
||||||
|
|
||||||
|
const browser = page.browser()
|
||||||
|
page = await browser.newPage()
|
||||||
|
|
||||||
|
// Got to punch card index page in a new tab
|
||||||
|
await page.goto(punchCard.parentPromotion.destinationUrl, { referer: this.bot.config.baseURL })
|
||||||
|
|
||||||
|
await this.solveActivities(page, activitiesUncompleted, punchCard)
|
||||||
|
|
||||||
|
// Close the punch card index page
|
||||||
|
await page.close()
|
||||||
|
|
||||||
|
this.bot.log('PUNCH-CARD', `All items for punchcard: "${punchCard.parentPromotion.title}" have been completed`)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bot.log('PUNCH-CARD', 'All "Punch Card" items have been completed')
|
||||||
}
|
}
|
||||||
|
|
||||||
const activitiesUncompleted = morePromotions?.filter(x => !x.complete && x.pointProgressMax > 0) ?? []
|
// More Promotions
|
||||||
|
async doMorePromotions(page: Page, data: DashboardData) {
|
||||||
|
const morePromotions = data.morePromotions
|
||||||
|
|
||||||
if (!activitiesUncompleted.length) {
|
// Check if there is a promotional item
|
||||||
log('MORE-PROMOTIONS', 'All "More Promotion" items have already been completed')
|
if (data.promotionalItem) { // Convert and add the promotional item to the array
|
||||||
return
|
morePromotions.push(data.promotionalItem as unknown as MorePromotion)
|
||||||
|
}
|
||||||
|
|
||||||
|
const activitiesUncompleted = morePromotions?.filter(x => !x.complete && x.pointProgressMax > 0) ?? []
|
||||||
|
|
||||||
|
if (!activitiesUncompleted.length) {
|
||||||
|
this.bot.log('MORE-PROMOTIONS', 'All "More Promotion" items have already been completed')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Solve Activities
|
||||||
|
this.bot.log('MORE-PROMOTIONS', 'Started solving "More Promotions" item')
|
||||||
|
|
||||||
|
await this.solveActivities(page, activitiesUncompleted)
|
||||||
|
|
||||||
|
this.bot.log('MORE-PROMOTIONS', 'All "More Promotion" items have been completed')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Solve Activities
|
// Solve all the different types of activities
|
||||||
log('MORE-PROMOTIONS', 'Started solving "More Promotions" item')
|
private async solveActivities(page: Page, activities: PromotionalItem[] | MorePromotion[], punchCard?: PunchCard) {
|
||||||
|
for (const activity of activities) {
|
||||||
|
try {
|
||||||
|
|
||||||
await solveActivities(page, activitiesUncompleted)
|
let selector = `[data-bi-id="${activity.offerId}"]`
|
||||||
|
|
||||||
log('MORE-PROMOTIONS', 'All "More Promotion" items have been completed')
|
if (punchCard) {
|
||||||
}
|
selector = await this.bot.browser.func.getPunchCardActivity(page, activity)
|
||||||
|
|
||||||
// Solve all the different types of activities
|
} else if (activity.name.toLowerCase().includes('membercenter')) {
|
||||||
async function solveActivities(page: Page, activities: PromotionalItem[] | MorePromotion[], punchCard?: PunchCard) {
|
|
||||||
for (const activity of activities) {
|
|
||||||
try {
|
|
||||||
|
|
||||||
let selector = `[data-bi-id="${activity.offerId}"]`
|
// Promotion
|
||||||
|
if (activity.priority === 1) {
|
||||||
if (punchCard) {
|
selector = '#promo-item'
|
||||||
selector = await getPunchCardActivity(page, activity)
|
} else {
|
||||||
|
selector = `[data-bi-id="${activity.name}"]`
|
||||||
} else if (activity.name.toLowerCase().includes('membercenter')) {
|
|
||||||
|
|
||||||
// Promotion
|
|
||||||
if (activity.priority === 1) {
|
|
||||||
selector = '#promo-item'
|
|
||||||
} else {
|
|
||||||
selector = `[data-bi-id="${activity.name}"]`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for element to load
|
|
||||||
await page.waitForSelector(selector, { timeout: 5000 })
|
|
||||||
// Click element, it will be opened in a new tab
|
|
||||||
await page.click(selector)
|
|
||||||
|
|
||||||
// Select the new activity page
|
|
||||||
const activityPage = await getLatestTab(page)
|
|
||||||
|
|
||||||
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 activity type: "Poll" title: "${activity.title}"`)
|
|
||||||
await doPoll(activityPage)
|
|
||||||
} else { // ABC
|
|
||||||
log('ACTIVITY', `Found activity type: "ABC" title: "${activity.title}"`)
|
|
||||||
await doABC(activityPage)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
|
|
||||||
// This Or That Quiz (Usually 50 points)
|
|
||||||
case 50:
|
|
||||||
log('ACTIVITY', `Found activity type: "ThisOrThat" title: "${activity.title}"`)
|
|
||||||
await doThisOrThat(activityPage)
|
|
||||||
break
|
|
||||||
|
|
||||||
// Quizzes are usually 30-40 points
|
|
||||||
default:
|
|
||||||
log('ACTIVITY', `Found activity type: "Quiz" title: "${activity.title}"`)
|
|
||||||
await doQuiz(activityPage)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
break
|
}
|
||||||
|
|
||||||
// UrlReward (Visit)
|
// Wait for element to load
|
||||||
case 'urlreward':
|
await page.waitForSelector(selector, { timeout: 5000 })
|
||||||
log('ACTIVITY', `Found activity type: "UrlReward" title: "${activity.title}"`)
|
|
||||||
await doUrlReward(activityPage)
|
|
||||||
break
|
|
||||||
|
|
||||||
// Misc, Usually UrlReward Type
|
// Click element, it will be opened in a new tab
|
||||||
default:
|
await page.click(selector)
|
||||||
log('ACTIVITY', `Found activity type: "Misc" title: "${activity.title}"`)
|
|
||||||
await doUrlReward(activityPage)
|
// Cooldown
|
||||||
break
|
await this.bot.utils.wait(4000)
|
||||||
|
|
||||||
|
// Select the new activity page
|
||||||
|
const activityPage = await this.bot.browser.utils.getLatestTab(page)
|
||||||
|
|
||||||
|
// Wait for body to load
|
||||||
|
await activityPage.waitForSelector('body', { timeout: 10_000 })
|
||||||
|
|
||||||
|
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')) {
|
||||||
|
this.bot.log('ACTIVITY', `Found activity type: "Poll" title: "${activity.title}"`)
|
||||||
|
await this.bot.activities.doPoll(activityPage)
|
||||||
|
} else { // ABC
|
||||||
|
this.bot.log('ACTIVITY', `Found activity type: "ABC" title: "${activity.title}"`)
|
||||||
|
await this.bot.activities.doABC(activityPage)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
// This Or That Quiz (Usually 50 points)
|
||||||
|
case 50:
|
||||||
|
this.bot.log('ACTIVITY', `Found activity type: "ThisOrThat" title: "${activity.title}"`)
|
||||||
|
await this.bot.activities.doThisOrThat(activityPage)
|
||||||
|
break
|
||||||
|
|
||||||
|
// Quizzes are usually 30-40 points
|
||||||
|
default:
|
||||||
|
this.bot.log('ACTIVITY', `Found activity type: "Quiz" title: "${activity.title}"`)
|
||||||
|
await this.bot.activities.doQuiz(activityPage)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
// UrlReward (Visit)
|
||||||
|
case 'urlreward':
|
||||||
|
this.bot.log('ACTIVITY', `Found activity type: "UrlReward" title: "${activity.title}"`)
|
||||||
|
await this.bot.activities.doUrlReward(activityPage)
|
||||||
|
break
|
||||||
|
|
||||||
|
// Misc, Usually UrlReward Type
|
||||||
|
default:
|
||||||
|
this.bot.log('ACTIVITY', `Found activity type: "Misc" title: "${activity.title}"`)
|
||||||
|
await this.bot.activities.doUrlReward(activityPage)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cooldown
|
||||||
|
await this.bot.utils.wait(2000)
|
||||||
|
} catch (error) {
|
||||||
|
this.bot.log('ACTIVITY', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cooldown
|
|
||||||
await wait(1500)
|
|
||||||
} catch (error) {
|
|
||||||
log('ACTIVITY', 'An error occurred:' + error, 'error')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,52 +1,52 @@
|
|||||||
import { Page } from 'puppeteer'
|
import { Page } from 'puppeteer'
|
||||||
|
|
||||||
import { refreshCheerio } from '../../browser/BrowserFunc'
|
import { Workers } from '../Workers'
|
||||||
import { getLatestTab } from '../../browser/BrowserUtil'
|
|
||||||
import { log } from '../../util/Logger'
|
|
||||||
import { randomNumber, wait } from '../../util/Utils'
|
|
||||||
|
|
||||||
export async function doABC(page: Page) {
|
|
||||||
log('ABC', 'Trying to complete poll')
|
|
||||||
|
|
||||||
try {
|
export class ABC extends Workers {
|
||||||
await wait(2000)
|
|
||||||
let $ = await refreshCheerio(page)
|
|
||||||
|
|
||||||
// Don't loop more than 15 in case unable to solve, would lock otherwise
|
async doABC(page: Page) {
|
||||||
const maxIterations = 15
|
this.bot.log('ABC', 'Trying to complete poll')
|
||||||
let i
|
|
||||||
for (i = 0; i < maxIterations && !$('span.rw_icon').length; i++) {
|
|
||||||
await page.waitForSelector('.wk_OptionClickClass', { visible: true, timeout: 5000 })
|
|
||||||
|
|
||||||
const answers = $('.wk_OptionClickClass')
|
try {
|
||||||
const answer = answers[randomNumber(0, 2)]?.attribs['id']
|
let $ = await this.bot.browser.func.refreshCheerio(page)
|
||||||
|
|
||||||
await page.waitForSelector(`#${answer}`, { visible: true, timeout: 5000 })
|
// Don't loop more than 15 in case unable to solve, would lock otherwise
|
||||||
|
const maxIterations = 15
|
||||||
|
let i
|
||||||
|
for (i = 0; i < maxIterations && !$('span.rw_icon').length; i++) {
|
||||||
|
await page.waitForSelector('.wk_OptionClickClass', { visible: true, timeout: 5000 })
|
||||||
|
|
||||||
await wait(2000)
|
const answers = $('.wk_OptionClickClass')
|
||||||
await page.click(`#${answer}`) // Click answer
|
const answer = answers[this.bot.utils.randomNumber(0, 2)]?.attribs['id']
|
||||||
|
|
||||||
await wait(4000)
|
await page.waitForSelector(`#${answer}`, { visible: true, timeout: 5000 })
|
||||||
await page.waitForSelector('div.wk_button', { visible: true, timeout: 5000 })
|
|
||||||
await page.click('div.wk_button') // Click next question button
|
|
||||||
|
|
||||||
page = await getLatestTab(page)
|
await this.bot.utils.wait(2000)
|
||||||
$ = await refreshCheerio(page)
|
await page.click(`#${answer}`) // Click answer
|
||||||
await wait(1000)
|
|
||||||
|
await this.bot.utils.wait(4000)
|
||||||
|
await page.waitForSelector('div.wk_button', { visible: true, timeout: 5000 })
|
||||||
|
await page.click('div.wk_button') // Click next question button
|
||||||
|
|
||||||
|
page = await this.bot.browser.utils.getLatestTab(page)
|
||||||
|
$ = await this.bot.browser.func.refreshCheerio(page)
|
||||||
|
await this.bot.utils.wait(1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.bot.utils.wait(4000)
|
||||||
|
await page.close()
|
||||||
|
|
||||||
|
if (i === maxIterations) {
|
||||||
|
this.bot.log('ABC', 'Failed to solve quiz, exceeded max iterations of 15', 'warn')
|
||||||
|
} else {
|
||||||
|
this.bot.log('ABC', 'Completed the ABC successfully')
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
await page.close()
|
||||||
|
this.bot.log('ABC', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
await wait(4000)
|
|
||||||
await page.close()
|
|
||||||
|
|
||||||
if (i === maxIterations) {
|
|
||||||
log('ABC', 'Failed to solve quiz, exceeded max iterations of 15', 'warn')
|
|
||||||
} else {
|
|
||||||
log('ABC', 'Completed the ABC successfully')
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
await page.close()
|
|
||||||
log('ABC', 'An error occurred:' + error, 'error')
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,26 +1,29 @@
|
|||||||
import { Page } from 'puppeteer'
|
import { Page } from 'puppeteer'
|
||||||
|
|
||||||
import { log } from '../../util/Logger'
|
import { Workers } from '../Workers'
|
||||||
import { randomNumber, wait } from '../../util/Utils'
|
|
||||||
|
|
||||||
export async function doPoll(page: Page) {
|
|
||||||
log('POLL', 'Trying to complete poll')
|
|
||||||
|
|
||||||
try {
|
export class Poll extends Workers {
|
||||||
const buttonId = `#btoption${Math.floor(randomNumber(0, 1))}`
|
|
||||||
|
|
||||||
await page.waitForNetworkIdle({ timeout: 5000 })
|
async doPoll(page: Page) {
|
||||||
await page.waitForSelector(buttonId, { visible: true, timeout: 5000 })
|
this.bot.log('POLL', 'Trying to complete poll')
|
||||||
await wait(2000)
|
|
||||||
|
|
||||||
await page.click(buttonId)
|
try {
|
||||||
|
const buttonId = `#btoption${Math.floor(this.bot.utils.randomNumber(0, 1))}`
|
||||||
|
|
||||||
await wait(4000)
|
await page.waitForSelector(buttonId, { visible: true, timeout: 5000 })
|
||||||
await page.close()
|
await this.bot.utils.wait(2000)
|
||||||
|
|
||||||
log('POLL', 'Completed the poll successfully')
|
await page.click(buttonId)
|
||||||
} catch (error) {
|
|
||||||
await page.close()
|
await this.bot.utils.wait(4000)
|
||||||
log('POLL', 'An error occurred:' + error, 'error')
|
await page.close()
|
||||||
|
|
||||||
|
this.bot.log('POLL', 'Completed the poll successfully')
|
||||||
|
} catch (error) {
|
||||||
|
await page.close()
|
||||||
|
this.bot.log('POLL', 'An error occurred:' + error, 'error')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,94 +1,91 @@
|
|||||||
import { Page } from 'puppeteer'
|
import { Page } from 'puppeteer'
|
||||||
|
|
||||||
import { getQuizData, waitForQuizRefresh } from '../../browser/BrowserFunc'
|
import { Workers } from '../Workers'
|
||||||
import { wait } from '../../util/Utils'
|
|
||||||
import { log } from '../../util/Logger'
|
|
||||||
|
|
||||||
export async function doQuiz(page: Page) {
|
|
||||||
log('QUIZ', 'Trying to complete quiz')
|
|
||||||
|
|
||||||
try {
|
export class Quiz extends Workers {
|
||||||
await page.waitForNetworkIdle({ timeout: 5000 })
|
|
||||||
await wait(2000)
|
|
||||||
|
|
||||||
// Check if the quiz has been started or not
|
async doQuiz(page: Page) {
|
||||||
const quizNotStarted = await page.waitForSelector('#rqStartQuiz', { visible: true, timeout: 3000 }).then(() => true).catch(() => false)
|
this.bot.log('QUIZ', 'Trying to complete quiz')
|
||||||
if (quizNotStarted) {
|
|
||||||
await page.click('#rqStartQuiz')
|
|
||||||
} else {
|
|
||||||
log('QUIZ', 'Quiz has already been started, trying to finish it')
|
|
||||||
}
|
|
||||||
|
|
||||||
await wait(2000)
|
try {
|
||||||
|
// Check if the quiz has been started or not
|
||||||
|
const quizNotStarted = await page.waitForSelector('#rqStartQuiz', { visible: true, timeout: 3000 }).then(() => true).catch(() => false)
|
||||||
|
if (quizNotStarted) {
|
||||||
|
await page.click('#rqStartQuiz')
|
||||||
|
} else {
|
||||||
|
this.bot.log('QUIZ', 'Quiz has already been started, trying to finish it')
|
||||||
|
}
|
||||||
|
|
||||||
let quizData = await getQuizData(page)
|
await this.bot.utils.wait(2000)
|
||||||
const questionsRemaining = quizData.maxQuestions - quizData.CorrectlyAnsweredQuestionCount // Amount of questions remaining
|
|
||||||
|
|
||||||
// All questions
|
let quizData = await this.bot.browser.func.getQuizData(page)
|
||||||
for (let question = 0; question < questionsRemaining; question++) {
|
const questionsRemaining = quizData.maxQuestions - quizData.CorrectlyAnsweredQuestionCount // Amount of questions remaining
|
||||||
|
|
||||||
if (quizData.numberOfOptions === 8) {
|
// All questions
|
||||||
const answers: string[] = []
|
for (let question = 0; question < questionsRemaining; question++) {
|
||||||
|
|
||||||
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
if (quizData.numberOfOptions === 8) {
|
||||||
const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { visible: true, timeout: 5000 })
|
const answers: string[] = []
|
||||||
const answerAttribute = await answerSelector?.evaluate(el => el.getAttribute('iscorrectoption'))
|
|
||||||
|
|
||||||
if (answerAttribute && answerAttribute.toLowerCase() === 'true') {
|
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
||||||
answers.push(`#rqAnswerOption${i}`)
|
const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { visible: true, timeout: 5000 })
|
||||||
|
const answerAttribute = await answerSelector?.evaluate(el => el.getAttribute('iscorrectoption'))
|
||||||
|
|
||||||
|
if (answerAttribute && answerAttribute.toLowerCase() === 'true') {
|
||||||
|
answers.push(`#rqAnswerOption${i}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Click the answers
|
// Click the answers
|
||||||
for (const answer of answers) {
|
for (const answer of answers) {
|
||||||
await page.waitForSelector(answer, { visible: true, timeout: 2000 })
|
await page.waitForSelector(answer, { visible: true, timeout: 2000 })
|
||||||
|
|
||||||
// Click the answer on page
|
|
||||||
await page.click(answer)
|
|
||||||
|
|
||||||
const refreshSuccess = await waitForQuizRefresh(page)
|
|
||||||
if (!refreshSuccess) {
|
|
||||||
await page.close()
|
|
||||||
log('QUIZ', 'An error occurred, refresh was unsuccessful', 'error')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other type quiz
|
|
||||||
} else if ([2, 3, 4].includes(quizData.numberOfOptions)) {
|
|
||||||
quizData = await getQuizData(page) // Refresh Quiz Data
|
|
||||||
const correctOption = quizData.correctAnswer
|
|
||||||
|
|
||||||
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
|
||||||
|
|
||||||
const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { visible: true, timeout: 5000 })
|
|
||||||
const dataOption = await answerSelector?.evaluate(el => el.getAttribute('data-option'))
|
|
||||||
|
|
||||||
if (dataOption === correctOption) {
|
|
||||||
// Click the answer on page
|
// Click the answer on page
|
||||||
await page.click(`#rqAnswerOption${i}`)
|
await page.click(answer)
|
||||||
|
|
||||||
const refreshSuccess = await waitForQuizRefresh(page)
|
const refreshSuccess = await this.bot.browser.func.waitForQuizRefresh(page)
|
||||||
if (!refreshSuccess) {
|
if (!refreshSuccess) {
|
||||||
await page.close()
|
await page.close()
|
||||||
log('QUIZ', 'An error occurred, refresh was unsuccessful', 'error')
|
this.bot.log('QUIZ', 'An error occurred, refresh was unsuccessful', 'error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
await wait(2000)
|
|
||||||
|
|
||||||
|
// Other type quiz
|
||||||
|
} else if ([2, 3, 4].includes(quizData.numberOfOptions)) {
|
||||||
|
quizData = await this.bot.browser.func.getQuizData(page) // Refresh Quiz Data
|
||||||
|
const correctOption = quizData.correctAnswer
|
||||||
|
|
||||||
|
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
||||||
|
|
||||||
|
const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { visible: true, timeout: 5000 })
|
||||||
|
const dataOption = await answerSelector?.evaluate(el => el.getAttribute('data-option'))
|
||||||
|
|
||||||
|
if (dataOption === correctOption) {
|
||||||
|
// Click the answer on page
|
||||||
|
await page.click(`#rqAnswerOption${i}`)
|
||||||
|
|
||||||
|
const refreshSuccess = await this.bot.browser.func.waitForQuizRefresh(page)
|
||||||
|
if (!refreshSuccess) {
|
||||||
|
await page.close()
|
||||||
|
this.bot.log('QUIZ', 'An error occurred, refresh was unsuccessful', 'error')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.bot.utils.wait(2000)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Done with
|
||||||
|
await this.bot.utils.wait(2000)
|
||||||
|
await page.close()
|
||||||
|
this.bot.log('QUIZ', 'Completed the quiz successfully')
|
||||||
|
} catch (error) {
|
||||||
|
await page.close()
|
||||||
|
this.bot.log('QUIZ', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Done with
|
|
||||||
await wait(2000)
|
|
||||||
await page.close()
|
|
||||||
log('QUIZ', 'Completed the quiz successfully')
|
|
||||||
} catch (error) {
|
|
||||||
await page.close()
|
|
||||||
log('QUIZ', 'An error occurred:' + error, 'error')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,203 +1,230 @@
|
|||||||
import { Page } from 'puppeteer'
|
import { Page } from 'puppeteer'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
import { getLatestTab } from '../../browser/BrowserUtil'
|
import { Workers } from '../Workers'
|
||||||
import { getSearchPoints } from '../../browser/BrowserFunc'
|
|
||||||
import { log } from '../../util/Logger'
|
|
||||||
import { randomNumber, shuffleArray, wait } from '../../util/Utils'
|
|
||||||
|
|
||||||
import { searchSettings } 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'
|
||||||
|
|
||||||
export async function doSearch(page: Page, data: DashboardData, mobile: boolean) {
|
|
||||||
log('SEARCH-BING', 'Starting bing searches')
|
|
||||||
|
|
||||||
const mobileData = data.userStatus.counters?.mobileSearch ? data.userStatus.counters.mobileSearch[0] : null // Mobile searches
|
export class Search extends Workers {
|
||||||
const edgeData = data.userStatus.counters.pcSearch[1] as DashboardImpression // Edge searches
|
|
||||||
const genericData = data.userStatus.counters.pcSearch[0] as DashboardImpression // Normal searches
|
|
||||||
|
|
||||||
let missingPoints = (mobile && mobileData) ?
|
public async doSearch(page: Page, data: DashboardData, mobile: boolean) {
|
||||||
(mobileData.pointProgressMax - mobileData.pointProgress) :
|
this.bot.log('SEARCH-BING', 'Starting bing searches')
|
||||||
(edgeData.pointProgressMax - edgeData.pointProgress) + (genericData.pointProgressMax - genericData.pointProgress)
|
|
||||||
|
|
||||||
if (missingPoints == 0) {
|
const mobileData = data.userStatus.counters?.mobileSearch ? data.userStatus.counters.mobileSearch[0] : null // Mobile searches
|
||||||
log('SEARCH-BING', `Bing searches for ${mobile ? 'MOBILE' : 'DESKTOP'} have already been completed`)
|
const edgeData = data.userStatus.counters.pcSearch[1] as DashboardImpression // Edge searches
|
||||||
return
|
const genericData = data.userStatus.counters.pcSearch[0] as DashboardImpression // Normal searches
|
||||||
}
|
|
||||||
|
|
||||||
// Generate search queries
|
let missingPoints = (mobile && mobileData) ?
|
||||||
let googleSearchQueries = await getGoogleTrends(data.userProfile.attributes.country, missingPoints)
|
(mobileData.pointProgressMax - mobileData.pointProgress) :
|
||||||
googleSearchQueries = shuffleArray(googleSearchQueries)
|
(edgeData.pointProgressMax - edgeData.pointProgress) + (genericData.pointProgressMax - genericData.pointProgress)
|
||||||
|
|
||||||
// Deduplicate the search terms
|
|
||||||
googleSearchQueries = [...new Set(googleSearchQueries)]
|
|
||||||
|
|
||||||
// Open a new tab
|
|
||||||
const browser = page.browser()
|
|
||||||
const searchPage = await browser.newPage()
|
|
||||||
|
|
||||||
// Go to bing
|
|
||||||
await searchPage.goto('https://bing.com')
|
|
||||||
|
|
||||||
let maxLoop = 0 // If the loop hits 10 this when not gaining any points, we're assuming it's stuck. If it ddoesn't continue after 5 more searches with alternative queries, abort search
|
|
||||||
|
|
||||||
const queries: string[] = []
|
|
||||||
googleSearchQueries.forEach(x => queries.push(x.topic, ...x.related))
|
|
||||||
|
|
||||||
// Loop over Google search queries
|
|
||||||
for (let i = 0; i < queries.length; i++) {
|
|
||||||
const query = queries[i] as string
|
|
||||||
|
|
||||||
log('SEARCH-BING', `${missingPoints} Points Remaining | Query: ${query} | Mobile: ${mobile}`)
|
|
||||||
|
|
||||||
const newData = await bingSearch(page, searchPage, query)
|
|
||||||
|
|
||||||
const newMobileData = newData.mobileSearch ? newData.mobileSearch[0] : null // Mobile searches
|
|
||||||
const newEdgeData = newData.pcSearch[1] as DashboardImpression // Edge searches
|
|
||||||
const newGenericData = newData.pcSearch[0] as DashboardImpression // Normal searches
|
|
||||||
|
|
||||||
const newMissingPoints = (mobile && newMobileData) ?
|
|
||||||
(newMobileData.pointProgressMax - newMobileData.pointProgress) :
|
|
||||||
(newEdgeData.pointProgressMax - newEdgeData.pointProgress) + (newGenericData.pointProgressMax - newGenericData.pointProgress)
|
|
||||||
|
|
||||||
// If the new point amount is the same as before
|
|
||||||
if (newMissingPoints == missingPoints) {
|
|
||||||
maxLoop++ // Add to max loop
|
|
||||||
} else { // There has been a change in points
|
|
||||||
maxLoop = 0 // Reset the loop
|
|
||||||
}
|
|
||||||
|
|
||||||
missingPoints = newMissingPoints
|
|
||||||
|
|
||||||
if (missingPoints == 0) {
|
if (missingPoints == 0) {
|
||||||
break
|
this.bot.log('SEARCH-BING', `Bing searches for ${mobile ? 'MOBILE' : 'DESKTOP'} have already been completed`)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we didn't gain points for 10 iterations, assume it's stuck
|
// Generate search queries
|
||||||
if (maxLoop > 10) {
|
let googleSearchQueries = await this.getGoogleTrends(data.userProfile.attributes.country, missingPoints)
|
||||||
log('SEARCH-BING', 'Search didn\'t gain point for 10 iterations aborting searches', 'warn')
|
googleSearchQueries = this.bot.utils.shuffleArray(googleSearchQueries)
|
||||||
maxLoop = 0 // Reset to 0 so we can retry with related searches below
|
|
||||||
break
|
// Deduplicate the search terms
|
||||||
|
googleSearchQueries = [...new Set(googleSearchQueries)]
|
||||||
|
|
||||||
|
// Open a new tab
|
||||||
|
const browser = page.browser()
|
||||||
|
const searchPage = await browser.newPage()
|
||||||
|
|
||||||
|
// Go to bing
|
||||||
|
await searchPage.goto('https://bing.com')
|
||||||
|
|
||||||
|
let maxLoop = 0 // If the loop hits 10 this when not gaining any points, we're assuming it's stuck. If it ddoesn't continue after 5 more searches with alternative queries, abort search
|
||||||
|
|
||||||
|
const queries: string[] = []
|
||||||
|
googleSearchQueries.forEach(x => queries.push(x.topic, ...x.related))
|
||||||
|
|
||||||
|
// Loop over Google search queries
|
||||||
|
for (let i = 0; i < queries.length; i++) {
|
||||||
|
const query = queries[i] as string
|
||||||
|
|
||||||
|
this.bot.log('SEARCH-BING', `${missingPoints} Points Remaining | Query: ${query} | Mobile: ${mobile}`)
|
||||||
|
|
||||||
|
const newData = await this.bingSearch(page, searchPage, query)
|
||||||
|
|
||||||
|
const newMobileData = newData.mobileSearch ? newData.mobileSearch[0] : null // Mobile searches
|
||||||
|
const newEdgeData = newData.pcSearch[1] as DashboardImpression // Edge searches
|
||||||
|
const newGenericData = newData.pcSearch[0] as DashboardImpression // Normal searches
|
||||||
|
|
||||||
|
const newMissingPoints = (mobile && newMobileData) ?
|
||||||
|
(newMobileData.pointProgressMax - newMobileData.pointProgress) :
|
||||||
|
(newEdgeData.pointProgressMax - newEdgeData.pointProgress) + (newGenericData.pointProgressMax - newGenericData.pointProgress)
|
||||||
|
|
||||||
|
// If the new point amount is the same as before
|
||||||
|
if (newMissingPoints == missingPoints) {
|
||||||
|
maxLoop++ // Add to max loop
|
||||||
|
} else { // There has been a change in points
|
||||||
|
maxLoop = 0 // Reset the loop
|
||||||
|
}
|
||||||
|
|
||||||
|
missingPoints = newMissingPoints
|
||||||
|
|
||||||
|
if (missingPoints == 0) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't gain points for 10 iterations, assume it's stuck
|
||||||
|
if (maxLoop > 10) {
|
||||||
|
this.bot.log('SEARCH-BING', 'Search didn\'t gain point for 10 iterations aborting searches', 'warn')
|
||||||
|
maxLoop = 0 // Reset to 0 so we can retry with related searches below
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// If we still got remaining search queries, generate extra ones
|
// If we still got remaining search queries, generate extra ones
|
||||||
if (missingPoints > 0) {
|
if (missingPoints > 0) {
|
||||||
log('SEARCH-BING', `Search completed but we're missing ${missingPoints} points, generating extra searches`)
|
this.bot.log('SEARCH-BING', `Search completed but we're missing ${missingPoints} points, generating extra searches`)
|
||||||
|
|
||||||
let i = 0
|
let i = 0
|
||||||
while (missingPoints > 0) {
|
while (missingPoints > 0) {
|
||||||
const query = googleSearchQueries[i++] as GoogleSearch
|
const query = googleSearchQueries[i++] as GoogleSearch
|
||||||
|
|
||||||
// Get related search terms to the Google search queries
|
// Get related search terms to the Google search queries
|
||||||
const relatedTerms = await getRelatedTerms(query?.topic)
|
const relatedTerms = await this.getRelatedTerms(query?.topic)
|
||||||
if (relatedTerms.length > 3) {
|
if (relatedTerms.length > 3) {
|
||||||
// Search for the first 2 related terms
|
// Search for the first 2 related terms
|
||||||
for (const term of relatedTerms.slice(1, 3)) {
|
for (const term of relatedTerms.slice(1, 3)) {
|
||||||
log('SEARCH-BING-EXTRA', `${missingPoints} Points Remaining | Query: ${term} | Mobile: ${mobile}`)
|
this.bot.log('SEARCH-BING-EXTRA', `${missingPoints} Points Remaining | Query: ${term} | Mobile: ${mobile}`)
|
||||||
const newData = await bingSearch(page, searchPage, query.topic)
|
const newData = await this.bingSearch(page, searchPage, query.topic)
|
||||||
|
|
||||||
const newMobileData = newData.mobileSearch ? newData.mobileSearch[0] : null // 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 && newMobileData) ?
|
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)
|
||||||
|
|
||||||
// If the new point amount is the same as before
|
// If the new point amount is the same as before
|
||||||
if (newMissingPoints == missingPoints) {
|
if (newMissingPoints == missingPoints) {
|
||||||
maxLoop++ // Add to max loop
|
maxLoop++ // Add to max loop
|
||||||
} else { // There has been a change in points
|
} else { // There has been a change in points
|
||||||
maxLoop = 0 // Reset the loop
|
maxLoop = 0 // Reset the loop
|
||||||
}
|
}
|
||||||
|
|
||||||
missingPoints = newMissingPoints
|
missingPoints = newMissingPoints
|
||||||
|
|
||||||
// If we satisfied the searches
|
// If we satisfied the searches
|
||||||
if (missingPoints == 0) {
|
if (missingPoints == 0) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try 5 more times, then we tried a total of 15 times, fair to say it's stuck
|
// Try 5 more times, then we tried a total of 15 times, fair to say it's stuck
|
||||||
if (maxLoop > 5) {
|
if (maxLoop > 5) {
|
||||||
log('SEARCH-BING-EXTRA', 'Search didn\'t gain point for 5 iterations aborting searches', 'warn')
|
this.bot.log('SEARCH-BING-EXTRA', 'Search didn\'t gain point for 5 iterations aborting searches', 'warn')
|
||||||
return
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.bot.log('SEARCH-BING', 'Completed searches')
|
||||||
}
|
}
|
||||||
|
|
||||||
log('SEARCH-BING', 'Completed searches')
|
private async bingSearch(page: Page, searchPage: Page, query: string) {
|
||||||
}
|
// Try a max of 5 times
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
try {
|
||||||
|
const searchBar = '#sb_form_q'
|
||||||
|
await searchPage.waitForSelector(searchBar, { visible: true, timeout: 10_000 })
|
||||||
|
await searchPage.click(searchBar) // Focus on the textarea
|
||||||
|
await this.bot.utils.wait(500)
|
||||||
|
await searchPage.keyboard.down('Control')
|
||||||
|
await searchPage.keyboard.press('A')
|
||||||
|
await searchPage.keyboard.press('Backspace')
|
||||||
|
await searchPage.keyboard.up('Control')
|
||||||
|
await searchPage.keyboard.type(query)
|
||||||
|
await searchPage.keyboard.press('Enter')
|
||||||
|
|
||||||
async function bingSearch(page: Page, searchPage: Page, query: string) {
|
if (this.bot.config.searchSettings.scrollRandomResults) {
|
||||||
// Try a max of 5 times
|
await this.bot.utils.wait(2000)
|
||||||
for (let i = 0; i < 5; i++) {
|
await this.randomScroll(searchPage)
|
||||||
try {
|
}
|
||||||
const searchBar = '#sb_form_q'
|
|
||||||
await searchPage.waitForSelector(searchBar, { visible: true, timeout: 10_000 })
|
|
||||||
await searchPage.click(searchBar) // Focus on the textarea
|
|
||||||
await wait(500)
|
|
||||||
await searchPage.keyboard.down('Control')
|
|
||||||
await searchPage.keyboard.press('A')
|
|
||||||
await searchPage.keyboard.press('Backspace')
|
|
||||||
await searchPage.keyboard.up('Control')
|
|
||||||
await searchPage.keyboard.type(query)
|
|
||||||
await searchPage.keyboard.press('Enter')
|
|
||||||
|
|
||||||
if (searchSettings.scrollRandomResults) {
|
if (this.bot.config.searchSettings.clickRandomResults) {
|
||||||
await wait(2000)
|
await this.bot.utils.wait(2000)
|
||||||
await randomScroll(searchPage)
|
await this.clickRandomLink(searchPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.bot.utils.wait(Math.floor(this.bot.utils.randomNumber(this.bot.config.searchSettings.searchDelay.min, this.bot.config.searchSettings.searchDelay.max)))
|
||||||
|
|
||||||
|
return await this.bot.browser.func.getSearchPoints(page)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
if (i === 5) {
|
||||||
|
this.bot.log('SEARCH-BING', 'Failed after 5 retries... An error occurred:' + error, 'error')
|
||||||
|
break
|
||||||
|
|
||||||
|
}
|
||||||
|
this.bot.log('SEARCH-BING', 'Search failed, An error occurred:' + error, 'error')
|
||||||
|
this.bot.log('SEARCH-BING', `Retrying search, attempt ${i}/5`, 'warn')
|
||||||
|
|
||||||
|
await this.bot.utils.wait(4000)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searchSettings.clickRandomResults) {
|
|
||||||
await wait(2000)
|
|
||||||
await clickRandomLink(searchPage)
|
|
||||||
}
|
|
||||||
|
|
||||||
await wait(Math.floor(randomNumber(10_000, 20_000)))
|
|
||||||
|
|
||||||
return await getSearchPoints(page)
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
if (i === 5) {
|
|
||||||
log('SEARCH-BING', 'Failed after 5 retries... An error occurred:' + error, 'error')
|
|
||||||
break
|
|
||||||
|
|
||||||
}
|
|
||||||
log('SEARCH-BING', 'Search failed, An error occurred:' + error, 'error')
|
|
||||||
log('SEARCH-BING', `Retrying search, attempt ${i}/5`, 'warn')
|
|
||||||
|
|
||||||
await wait(4000)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.bot.log('SEARCH-BING', 'Search failed after 5 retries, ending', 'error')
|
||||||
|
return await this.bot.browser.func.getSearchPoints(page)
|
||||||
}
|
}
|
||||||
|
|
||||||
log('SEARCH-BING', 'Search failed after 5 retries, ending', 'error')
|
private async getGoogleTrends(geoLocale: string, queryCount: number): Promise<GoogleSearch[]> {
|
||||||
return await getSearchPoints(page)
|
const queryTerms: GoogleSearch[] = []
|
||||||
}
|
let i = 0
|
||||||
|
|
||||||
async function getGoogleTrends(geoLocale: string, queryCount: number): Promise<GoogleSearch[]> {
|
geoLocale = (this.bot.config.searchSettings.useGeoLocaleQueries && geoLocale.length === 2) ? geoLocale.toUpperCase() : 'US'
|
||||||
const queryTerms: GoogleSearch[] = []
|
|
||||||
let i = 0
|
|
||||||
|
|
||||||
geoLocale = (searchSettings.useGeoLocaleQueries && geoLocale.length === 2) ? geoLocale.toUpperCase() : 'US'
|
this.bot.log('SEARCH-GOOGLE-TRENDS', `Generating search queries, can take a while! | GeoLocale: ${geoLocale}`)
|
||||||
|
|
||||||
log('SEARCH-GOOGLE-TRENDS', `Generating search queries, can take a while! | GeoLocale: ${geoLocale}`)
|
while (queryCount > queryTerms.length) {
|
||||||
|
i += 1
|
||||||
|
const date = new Date()
|
||||||
|
date.setDate(date.getDate() - i)
|
||||||
|
const formattedDate = this.formatDate(date)
|
||||||
|
|
||||||
while (queryCount > queryTerms.length) {
|
try {
|
||||||
i += 1
|
const request = {
|
||||||
const date = new Date()
|
url: `https://trends.google.com/trends/api/dailytrends?geo=${geoLocale}&hl=en&ed=${formattedDate}&ns=15`,
|
||||||
date.setDate(date.getDate() - i)
|
method: 'GET',
|
||||||
const formattedDate = formatDate(date)
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios(request)
|
||||||
|
|
||||||
|
const data: GoogleTrends = JSON.parse((await response.data).slice(5))
|
||||||
|
|
||||||
|
for (const topic of data.default.trendingSearchesDays[0]?.trendingSearches ?? []) {
|
||||||
|
queryTerms.push({
|
||||||
|
topic: topic.title.query.toLowerCase(),
|
||||||
|
related: topic.relatedQueries.map(x => x.query.toLocaleLowerCase())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.bot.log('SEARCH-GOOGLE-TRENDS', 'An error occurred:' + error, 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryTerms
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getRelatedTerms(term: string): Promise<string[]> {
|
||||||
try {
|
try {
|
||||||
const request = {
|
const request = {
|
||||||
url: `https://trends.google.com/trends/api/dailytrends?geo=${geoLocale}&hl=en&ed=${formattedDate}&ns=15`,
|
url: `https://api.bing.com/osjson.aspx?query=${term}`,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
@@ -206,126 +233,99 @@ async function getGoogleTrends(geoLocale: string, queryCount: number): Promise<G
|
|||||||
|
|
||||||
const response = await axios(request)
|
const response = await axios(request)
|
||||||
|
|
||||||
const data: GoogleTrends = JSON.parse((await response.data).slice(5))
|
return response.data[1] as string[]
|
||||||
|
|
||||||
for (const topic of data.default.trendingSearchesDays[0]?.trendingSearches ?? []) {
|
|
||||||
queryTerms.push({
|
|
||||||
topic: topic.title.query.toLowerCase(),
|
|
||||||
related: topic.relatedQueries.map(x => x.query.toLocaleLowerCase())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log('SEARCH-GOOGLE-TRENDS', 'An error occurred:' + error, 'error')
|
this.bot.log('SEARCH-BING-RELTATED', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return queryTerms
|
private formatDate(date: Date): string {
|
||||||
}
|
const year = date.getFullYear()
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
|
|
||||||
async function getRelatedTerms(term: string): Promise<string[]> {
|
return `${year}${month}${day}`
|
||||||
try {
|
}
|
||||||
const request = {
|
|
||||||
url: `https://api.bing.com/osjson.aspx?query=${term}`,
|
private async randomScroll(page: Page) {
|
||||||
method: 'GET',
|
try {
|
||||||
headers: {
|
// Press the arrow down key to scroll
|
||||||
'Content-Type': 'application/json'
|
for (let i = 0; i < this.bot.utils.randomNumber(5, 100); i++) {
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.bot.log('SEARCH-RANDOM-SCROLL', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await axios(request)
|
|
||||||
|
|
||||||
return response.data[1] as string[]
|
|
||||||
} catch (error) {
|
|
||||||
log('SEARCH-BING-RELTATED', 'An error occurred:' + error, 'error')
|
|
||||||
}
|
}
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDate(date: Date): string {
|
private async clickRandomLink(page: Page) {
|
||||||
const year = date.getFullYear()
|
try {
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
const searchListingURL = new URL(page.url()) // Get searchPage info before clicking
|
||||||
const day = String(date.getDate()).padStart(2, '0')
|
|
||||||
|
|
||||||
return `${year}${month}${day}`
|
await page.click('#b_results .b_algo h2').catch(() => { }) // Since we don't really care if it did it or not
|
||||||
}
|
|
||||||
|
|
||||||
async function randomScroll(page: Page) {
|
// Wait for website to load
|
||||||
try {
|
await this.bot.utils.wait(3000)
|
||||||
// Press the arrow down key to scroll
|
|
||||||
for (let i = 0; i < randomNumber(5, 100); i++) {
|
|
||||||
await page.keyboard.press('ArrowDown')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
log('SEARCH-RANDOM-SCROLL', 'An error occurred:' + error, 'error')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function clickRandomLink(page: Page) {
|
// Will get current tab if no new one is created
|
||||||
try {
|
let lastTab = await this.bot.browser.utils.getLatestTab(page)
|
||||||
const searchListingURL = new URL(page.url()) // Get searchPage info before clicking
|
|
||||||
|
|
||||||
await page.click('#b_results .b_algo h2').catch(() => { }) // Since we don't really care if it did it or not
|
// Wait for the body of the new page to be loaded
|
||||||
|
await lastTab.waitForSelector('body', { timeout: 10_000 }).catch(() => { })
|
||||||
|
|
||||||
// Wait for website to load
|
// Check if the tab is closed or not
|
||||||
await wait(3000)
|
if (!lastTab.isClosed()) {
|
||||||
|
let lastTabURL = new URL(lastTab.url()) // Get new tab info
|
||||||
// Will get current tab if no new one is created
|
|
||||||
let lastTab = await getLatestTab(page)
|
|
||||||
|
|
||||||
// Wait for the body of the new page to be loaded
|
|
||||||
await lastTab.waitForSelector('body', { timeout: 10_000 }).catch(() => { })
|
|
||||||
|
|
||||||
// Check if the tab is closed or not
|
|
||||||
if (!lastTab.isClosed()) {
|
|
||||||
let lastTabURL = new URL(lastTab.url()) // Get new tab info
|
|
||||||
|
|
||||||
// Check if the URL is different from the original one, don't loop more than 5 times.
|
|
||||||
let i = 0
|
|
||||||
while (lastTabURL.href !== searchListingURL.href && i < 5) {
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// If "goBack" didn't return to search listing (due to redirects)
|
|
||||||
if (lastTabURL.hostname !== searchListingURL.hostname) {
|
|
||||||
await lastTab.goto(searchListingURL.href)
|
|
||||||
}
|
|
||||||
|
|
||||||
} 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 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) {
|
|
||||||
// Make sure the page is still open!
|
|
||||||
if (!lastTab.isClosed()) {
|
|
||||||
await lastTab.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (lastTabURL.href !== searchListingURL.href) {
|
|
||||||
|
|
||||||
|
// Check if the URL is different from the original one, don't loop more than 5 times.
|
||||||
|
let i = 0
|
||||||
|
while (lastTabURL.href !== searchListingURL.href && i < 5) {
|
||||||
|
// If hostname is still bing, (Bing images/news etc)
|
||||||
|
if (lastTabURL.hostname == searchListingURL.hostname) {
|
||||||
await lastTab.goBack()
|
await lastTab.goBack()
|
||||||
|
|
||||||
lastTab = await getLatestTab(page) // Get last opened tab
|
lastTab = await this.bot.browser.utils.getLatestTab(page) // Get last opened tab
|
||||||
lastTabURL = new URL(lastTab.url())
|
|
||||||
|
|
||||||
// If "goBack" didn't return to search listing (due to redirects)
|
// If "goBack" didn't return to search listing (due to redirects)
|
||||||
if (lastTabURL.hostname !== searchListingURL.hostname) {
|
if (lastTabURL.hostname !== searchListingURL.hostname) {
|
||||||
await lastTab.goto(searchListingURL.href)
|
await lastTab.goto(searchListingURL.href)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else { // No longer on bing, likely opened a new tab, close this tab
|
||||||
|
lastTab = await this.bot.browser.utils.getLatestTab(page) // Get last opened tab
|
||||||
|
lastTabURL = new URL(lastTab.url())
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// Make sure the page is still open!
|
||||||
|
if (!lastTab.isClosed()) {
|
||||||
|
await lastTab.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (lastTabURL.href !== searchListingURL.href) {
|
||||||
|
|
||||||
|
await lastTab.goBack()
|
||||||
|
|
||||||
|
lastTab = await this.bot.browser.utils.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
lastTab = await getLatestTab(page) // Finally update the lastTab var again
|
lastTab = await this.bot.browser.utils.getLatestTab(page) // Finally update the lastTab var again
|
||||||
i++
|
i++
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.bot.log('SEARCH-RANDOM-CLICK', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
log('SEARCH-RANDOM-CLICK', 'An error occurred:' + error, 'error')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,34 +1,33 @@
|
|||||||
import { Page } from 'puppeteer'
|
import { Page } from 'puppeteer'
|
||||||
|
|
||||||
import { wait } from '../../util/Utils'
|
import { Workers } from '../Workers'
|
||||||
import { log } from '../../util/Logger'
|
|
||||||
import { getQuizData } from '../../browser/BrowserFunc'
|
|
||||||
|
|
||||||
export async function doThisOrThat(page: Page) {
|
|
||||||
log('THIS-OR-THAT', 'Trying to complete ThisOrThat')
|
|
||||||
|
|
||||||
try {
|
export class ThisOrThat extends Workers {
|
||||||
await page.waitForNetworkIdle({ timeout: 5000 })
|
|
||||||
await wait(2000)
|
|
||||||
|
|
||||||
// Check if the quiz has been started or not
|
async doThisOrThat(page: Page) {
|
||||||
const quizNotStarted = await page.waitForSelector('#rqStartQuiz', { visible: true, timeout: 3000 }).then(() => true).catch(() => false)
|
this.bot.log('THIS-OR-THAT', 'Trying to complete ThisOrThat')
|
||||||
if (quizNotStarted) {
|
|
||||||
await page.click('#rqStartQuiz')
|
try {
|
||||||
} else {
|
// Check if the quiz has been started or not
|
||||||
log('THIS-OR-THAT', 'ThisOrThat has already been started, trying to finish it')
|
const quizNotStarted = await page.waitForSelector('#rqStartQuiz', { visible: true, timeout: 3000 }).then(() => true).catch(() => false)
|
||||||
|
if (quizNotStarted) {
|
||||||
|
await page.click('#rqStartQuiz')
|
||||||
|
} else {
|
||||||
|
this.bot.log('THIS-OR-THAT', 'ThisOrThat has already been started, trying to finish it')
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.bot.utils.wait(2000)
|
||||||
|
|
||||||
|
// Solving
|
||||||
|
const quizData = await this.bot.browser.func.getQuizData(page)
|
||||||
|
quizData // correctAnswer property is always null?
|
||||||
|
|
||||||
|
this.bot.log('THIS-OR-THAT', 'Completed the ThisOrthat successfully')
|
||||||
|
} catch (error) {
|
||||||
|
await page.close()
|
||||||
|
this.bot.log('THIS-OR-THAT', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
await wait(2000)
|
|
||||||
|
|
||||||
// Solving
|
|
||||||
const quizData = await getQuizData(page)
|
|
||||||
quizData // correctAnswer property is always null?
|
|
||||||
|
|
||||||
log('THIS-OR-THAT', 'Completed the ThisOrthat successfully')
|
|
||||||
} catch (error) {
|
|
||||||
await page.close()
|
|
||||||
log('THIS-OR-THAT', 'An error occurred:' + error, 'error')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,22 @@
|
|||||||
import { Page } from 'puppeteer'
|
import { Page } from 'puppeteer'
|
||||||
|
|
||||||
import { wait } from '../../util/Utils'
|
import { Workers } from '../Workers'
|
||||||
import { log } from '../../util/Logger'
|
|
||||||
|
|
||||||
export async function doUrlReward(page: Page) {
|
|
||||||
log('URL-REWARD', 'Trying to complete UrlReward')
|
|
||||||
|
|
||||||
try {
|
export class UrlReward extends Workers {
|
||||||
// After waiting, close the page
|
|
||||||
await page.waitForNetworkIdle({ timeout: 10_000 })
|
|
||||||
await wait(2000)
|
|
||||||
await page.close()
|
|
||||||
|
|
||||||
log('URL-REWARD', 'Completed the UrlReward successfully')
|
async doUrlReward(page: Page) {
|
||||||
} catch (error) {
|
this.bot.log('URL-REWARD', 'Trying to complete UrlReward')
|
||||||
await page.close()
|
|
||||||
log('URL-REWARD', 'An error occurred:' + error, 'error')
|
try {
|
||||||
|
// After waiting, close the page
|
||||||
|
await page.close()
|
||||||
|
|
||||||
|
this.bot.log('URL-REWARD', 'Completed the UrlReward successfully')
|
||||||
|
} catch (error) {
|
||||||
|
await page.close()
|
||||||
|
this.bot.log('URL-REWARD', 'An error occurred:' + error, 'error')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
91
src/index.ts
91
src/index.ts
@@ -1,40 +1,60 @@
|
|||||||
import cluster from 'cluster'
|
import cluster from 'cluster'
|
||||||
|
|
||||||
import Browser from './browser/Browser'
|
import Browser from './browser/Browser'
|
||||||
import { getDashboardData, getEarnablePoints, goHome } from './browser/BrowserFunc'
|
import BrowserFunc from './browser/BrowserFunc'
|
||||||
|
import BrowserUtil from './browser/BrowserUtil'
|
||||||
|
|
||||||
import { log } from './util/Logger'
|
import { log } from './util/Logger'
|
||||||
import { loadAccounts } from './util/Account'
|
import Util from './util/Utils'
|
||||||
import { chunkArray } from './util/Utils'
|
import { loadAccounts, loadConfig } from './util/Load'
|
||||||
|
|
||||||
import { login } from './functions/Login'
|
import { Login } from './functions/Login'
|
||||||
import { doDailySet, doMorePromotions, doPunchCard } from './functions/Workers'
|
import { Workers } from './functions/Workers'
|
||||||
import { doSearch } from './functions/activities/Search'
|
import Activities from './functions/Activities'
|
||||||
|
|
||||||
import { Account } from './interface/Account'
|
import { Account } from './interface/Account'
|
||||||
|
|
||||||
import { runOnZeroPoints, workers, clusters } from './config.json'
|
|
||||||
|
|
||||||
// Main bot class
|
// Main bot class
|
||||||
class MicrosoftRewardsBot {
|
export class MicrosoftRewardsBot {
|
||||||
private activeWorkers: number = clusters
|
public log: typeof log
|
||||||
|
public config
|
||||||
|
public utils: Util
|
||||||
|
public activities: Activities = new Activities(this)
|
||||||
|
public browser: {
|
||||||
|
func: BrowserFunc,
|
||||||
|
utils: BrowserUtil
|
||||||
|
}
|
||||||
|
|
||||||
private collectedPoints: number = 0
|
private collectedPoints: number = 0
|
||||||
private browserFactory: Browser = new Browser()
|
private activeWorkers: number
|
||||||
|
private browserFactory: Browser = new Browser(this)
|
||||||
private accounts: Account[]
|
private accounts: Account[]
|
||||||
|
private workers: Workers
|
||||||
|
private login = new Login(this)
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
this.log = log
|
||||||
|
|
||||||
this.accounts = []
|
this.accounts = []
|
||||||
|
this.utils = new Util()
|
||||||
|
this.workers = new Workers(this)
|
||||||
|
this.browser = {
|
||||||
|
func: new BrowserFunc(this),
|
||||||
|
utils: new BrowserUtil(this)
|
||||||
|
}
|
||||||
|
this.config = loadConfig()
|
||||||
|
this.activeWorkers = this.config.clusters
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
this.accounts = await loadAccounts()
|
this.accounts = loadAccounts()
|
||||||
}
|
}
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
log('MAIN', `Bot started with ${clusters} clusters`)
|
log('MAIN', `Bot started with ${this.config.clusters} clusters`)
|
||||||
|
|
||||||
// Only cluster when there's more than 1 cluster demanded
|
// Only cluster when there's more than 1 cluster demanded
|
||||||
if (clusters > 1) {
|
if (this.config.clusters > 1) {
|
||||||
if (cluster.isPrimary) {
|
if (cluster.isPrimary) {
|
||||||
this.runMaster()
|
this.runMaster()
|
||||||
} else {
|
} else {
|
||||||
@@ -48,7 +68,7 @@ class MicrosoftRewardsBot {
|
|||||||
private runMaster() {
|
private runMaster() {
|
||||||
log('MAIN-PRIMARY', 'Primary process started')
|
log('MAIN-PRIMARY', 'Primary process started')
|
||||||
|
|
||||||
const accountChunks = chunkArray(this.accounts, clusters)
|
const accountChunks = this.utils.chunkArray(this.accounts, this.config.clusters)
|
||||||
|
|
||||||
for (let i = 0; i < accountChunks.length; i++) {
|
for (let i = 0; i < accountChunks.length; i++) {
|
||||||
const worker = cluster.fork()
|
const worker = cluster.fork()
|
||||||
@@ -71,7 +91,6 @@ class MicrosoftRewardsBot {
|
|||||||
|
|
||||||
private runWorker() {
|
private runWorker() {
|
||||||
log('MAIN-WORKER', `Worker ${process.pid} spawned`)
|
log('MAIN-WORKER', `Worker ${process.pid} spawned`)
|
||||||
|
|
||||||
// Receive the chunk of accounts from the master
|
// Receive the chunk of accounts from the master
|
||||||
process.on('message', async ({ chunk }) => {
|
process.on('message', async ({ chunk }) => {
|
||||||
await this.runTasks(chunk)
|
await this.runTasks(chunk)
|
||||||
@@ -86,7 +105,7 @@ class MicrosoftRewardsBot {
|
|||||||
await this.Desktop(account)
|
await this.Desktop(account)
|
||||||
|
|
||||||
// If runOnZeroPoints is false and 0 points to earn, stop and try the next account
|
// If runOnZeroPoints is false and 0 points to earn, stop and try the next account
|
||||||
if (!runOnZeroPoints && this.collectedPoints === 0) {
|
if (!this.config.runOnZeroPoints && this.collectedPoints === 0) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,22 +138,22 @@ class MicrosoftRewardsBot {
|
|||||||
log('MAIN', 'Starting DESKTOP browser')
|
log('MAIN', 'Starting DESKTOP browser')
|
||||||
|
|
||||||
// Login into MS Rewards
|
// Login into MS Rewards
|
||||||
await login(page, account.email, account.password)
|
await this.login.login(page, account.email, account.password)
|
||||||
|
|
||||||
const wentHome = await goHome(page)
|
const wentHome = await this.browser.func.goHome(page)
|
||||||
if (!wentHome) {
|
if (!wentHome) {
|
||||||
throw log('MAIN', 'Unable to get dashboard page', 'error')
|
throw log('MAIN', 'Unable to get dashboard page', 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await getDashboardData(page)
|
const data = await this.browser.func.getDashboardData(page)
|
||||||
log('MAIN-POINTS', `Current point count: ${data.userStatus.availablePoints}`)
|
log('MAIN-POINTS', `Current point count: ${data.userStatus.availablePoints}`)
|
||||||
|
|
||||||
const earnablePoints = await getEarnablePoints(data)
|
const earnablePoints = await this.browser.func.getEarnablePoints(data)
|
||||||
this.collectedPoints = earnablePoints
|
this.collectedPoints = earnablePoints
|
||||||
log('MAIN-POINTS', `You can earn ${earnablePoints} points today`)
|
log('MAIN-POINTS', `You can earn ${earnablePoints} points today`)
|
||||||
|
|
||||||
// If runOnZeroPoints is false and 0 points to earn, don't continue
|
// If runOnZeroPoints is false and 0 points to earn, don't continue
|
||||||
if (!runOnZeroPoints && this.collectedPoints === 0) {
|
if (!this.config.runOnZeroPoints && this.collectedPoints === 0) {
|
||||||
log('MAIN', 'No points to earn and "runOnZeroPoints" is set to "false", stopping')
|
log('MAIN', 'No points to earn and "runOnZeroPoints" is set to "false", stopping')
|
||||||
|
|
||||||
// Close desktop browser
|
// Close desktop browser
|
||||||
@@ -142,23 +161,23 @@ class MicrosoftRewardsBot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Complete daily set
|
// Complete daily set
|
||||||
if (workers.doDailySet) {
|
if (this.config.workers.doDailySet) {
|
||||||
await doDailySet(page, data)
|
await this.workers.doDailySet(page, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete more promotions
|
// Complete more promotions
|
||||||
if (workers.doMorePromotions) {
|
if (this.config.workers.doMorePromotions) {
|
||||||
await doMorePromotions(page, data)
|
await this.workers.doMorePromotions(page, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete punch cards
|
// Complete punch cards
|
||||||
if (workers.doPunchCards) {
|
if (this.config.workers.doPunchCards) {
|
||||||
await doPunchCard(page, data)
|
await this.workers.doPunchCard(page, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do desktop searches
|
// Do desktop searches
|
||||||
if (workers.doDesktopSearch) {
|
if (this.config.workers.doDesktopSearch) {
|
||||||
await doSearch(page, data, false)
|
await this.activities.doSearch(page, data, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close desktop browser
|
// Close desktop browser
|
||||||
@@ -182,11 +201,11 @@ class MicrosoftRewardsBot {
|
|||||||
log('MAIN', 'Starting MOBILE browser')
|
log('MAIN', 'Starting MOBILE browser')
|
||||||
|
|
||||||
// Login into MS Rewards
|
// Login into MS Rewards
|
||||||
await login(page, account.email, account.password)
|
await this.login.login(page, account.email, account.password)
|
||||||
|
|
||||||
await goHome(page)
|
await this.browser.func.goHome(page)
|
||||||
|
|
||||||
const data = await getDashboardData(page)
|
const data = await this.browser.func.getDashboardData(page)
|
||||||
|
|
||||||
// If no mobile searches data found, stop (Does not exist on new accounts)
|
// If no mobile searches data found, stop (Does not exist on new accounts)
|
||||||
if (!data.userStatus.counters.mobileSearch) {
|
if (!data.userStatus.counters.mobileSearch) {
|
||||||
@@ -197,12 +216,12 @@ class MicrosoftRewardsBot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Do mobile searches
|
// Do mobile searches
|
||||||
if (workers.doMobileSearch) {
|
if (this.config.workers.doMobileSearch) {
|
||||||
await doSearch(page, data, true)
|
await this.activities.doSearch(page, data, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch new points
|
// Fetch new points
|
||||||
const earnablePoints = await getEarnablePoints(data, page)
|
const earnablePoints = await this.browser.func.getEarnablePoints(data, page)
|
||||||
|
|
||||||
// If the new earnable is 0, means we got all the points, else retract
|
// If the new earnable is 0, means we got all the points, else retract
|
||||||
this.collectedPoints = earnablePoints === 0 ? this.collectedPoints : (this.collectedPoints - earnablePoints)
|
this.collectedPoints = earnablePoints === 0 ? this.collectedPoints : (this.collectedPoints - earnablePoints)
|
||||||
|
|||||||
35
src/interface/Config.ts
Normal file
35
src/interface/Config.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
export interface Config {
|
||||||
|
baseURL: string;
|
||||||
|
sessionPath: string;
|
||||||
|
headless: boolean;
|
||||||
|
runOnZeroPoints: boolean;
|
||||||
|
clusters: number;
|
||||||
|
workers: Workers;
|
||||||
|
searchSettings: SearchSettings;
|
||||||
|
webhook: Webhook;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchSettings {
|
||||||
|
useGeoLocaleQueries: boolean;
|
||||||
|
scrollRandomResults: boolean;
|
||||||
|
clickRandomResults: boolean;
|
||||||
|
searchDelay: SearchDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchDelay {
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Webhook {
|
||||||
|
enabled: boolean;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Workers {
|
||||||
|
doDailySet: boolean;
|
||||||
|
doMorePromotions: boolean;
|
||||||
|
doPunchCards: boolean;
|
||||||
|
doDesktopSearch: boolean;
|
||||||
|
doMobileSearch: boolean;
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import * as fs from 'fs'
|
import fs from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
import { Account } from '../interface/Account'
|
import { Account } from '../interface/Account'
|
||||||
|
import { Config } from '../interface/Config'
|
||||||
|
|
||||||
export async function loadAccounts(): Promise<Account[]> {
|
|
||||||
|
export function loadAccounts(): Account[] {
|
||||||
try {
|
try {
|
||||||
let file = 'accounts.json'
|
let file = 'accounts.json'
|
||||||
|
|
||||||
@@ -19,4 +21,15 @@ export async function loadAccounts(): Promise<Account[]> {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error as string)
|
throw new Error(error as string)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadConfig(): Config {
|
||||||
|
try {
|
||||||
|
const configDir = path.join(__dirname, '../', 'config.json')
|
||||||
|
const config = fs.readFileSync(configDir, 'utf-8')
|
||||||
|
|
||||||
|
return JSON.parse(config)
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(error as string)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,38 +1,42 @@
|
|||||||
export async function wait(ms: number): Promise<void> {
|
export default class Util {
|
||||||
return new Promise<void>((resolve) => {
|
|
||||||
setTimeout(resolve, ms)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getFormattedDate(ms = Date.now()): string {
|
async wait(ms: number): Promise<void> {
|
||||||
const today = new Date(ms)
|
return new Promise<void>((resolve) => {
|
||||||
const month = String(today.getMonth() + 1).padStart(2, '0') // January is 0
|
setTimeout(resolve, ms)
|
||||||
const day = String(today.getDate()).padStart(2, '0')
|
})
|
||||||
const year = today.getFullYear()
|
}
|
||||||
|
|
||||||
return `${month}/${day}/${year}`
|
getFormattedDate(ms = Date.now()): string {
|
||||||
}
|
const today = new Date(ms)
|
||||||
|
const month = String(today.getMonth() + 1).padStart(2, '0') // January is 0
|
||||||
export function shuffleArray<T>(array: T[]): T[] {
|
const day = String(today.getDate()).padStart(2, '0')
|
||||||
const shuffledArray = array.slice()
|
const year = today.getFullYear()
|
||||||
|
|
||||||
shuffledArray.sort(() => Math.random() - 0.5)
|
return `${month}/${day}/${year}`
|
||||||
|
}
|
||||||
return shuffledArray
|
|
||||||
}
|
shuffleArray<T>(array: T[]): T[] {
|
||||||
|
const shuffledArray = array.slice()
|
||||||
export function randomNumber(min: number, max: number): number {
|
|
||||||
return Math.floor(Math.random() * (max - min + 1)) + min
|
shuffledArray.sort(() => Math.random() - 0.5)
|
||||||
}
|
|
||||||
|
return shuffledArray
|
||||||
export function chunkArray<T>(arr: T[], numChunks: number): T[][] {
|
}
|
||||||
const chunkSize = Math.ceil(arr.length / numChunks)
|
|
||||||
const chunks: T[][] = []
|
randomNumber(min: number, max: number): number {
|
||||||
|
return Math.floor(Math.random() * (max - min + 1)) + min
|
||||||
for (let i = 0; i < arr.length; i += chunkSize) {
|
}
|
||||||
const chunk = arr.slice(i, i + chunkSize)
|
|
||||||
chunks.push(chunk)
|
chunkArray<T>(arr: T[], numChunks: number): T[][] {
|
||||||
|
const chunkSize = Math.ceil(arr.length / numChunks)
|
||||||
|
const chunks: T[][] = []
|
||||||
|
|
||||||
|
for (let i = 0; i < arr.length; i += chunkSize) {
|
||||||
|
const chunk = arr.slice(i, i + chunkSize)
|
||||||
|
chunks.push(chunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunks
|
||||||
}
|
}
|
||||||
|
|
||||||
return chunks
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
import { webhook } from '../config.json'
|
import { loadConfig } from './Load'
|
||||||
|
|
||||||
|
|
||||||
export async function Webhook(content: string) {
|
export async function Webhook(content: string) {
|
||||||
|
const webhook = loadConfig().webhook
|
||||||
|
|
||||||
if (!webhook.enabled || webhook.url.length < 10) return
|
if (!webhook.enabled || webhook.url.length < 10) return
|
||||||
|
|
||||||
const request = {
|
const request = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: webhook.url,
|
url: webhook.url,
|
||||||
|
|||||||
@@ -64,7 +64,8 @@
|
|||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*.ts",
|
"src/**/*.ts",
|
||||||
"src/accounts.json"
|
"src/accounts.json",
|
||||||
|
"src/config.json"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules"
|
"node_modules"
|
||||||
|
|||||||
Reference in New Issue
Block a user