1.5 inital (#234)

* 1.4.12

* Update README.md

* Update package.json

* Update package.json

* 1.5 initial

- Added parallel mode (experimental, likely no Docker supported)
- Added chalk for clearer logging
- Added support for "SearchOnBing" Activities
- Added more configurable options for certain things
- Redone some of the popup/banner clicking for searching (Redo the entire "popup" clicking, so they're more specifically targeted)
- Axios proxy is now optional in the config
- Fingerprint saving is now optional for desktop and mobile

There needs to be many changes for Docker support, including parallel, the new config settings and general testing!

This is still highly experimental, if you use Docker or want something more stable, use the version before this commit!

* Add queries.json to build

* fix(Login): update URL within authorization loop to reflect current page (#210)

* Many changes

- Updated Packages
- Fixed mobile searches erroring out for dashboard  data
- Reworked "bad page" detection
- Catching more errors
- Reworked the search and "close tabs"
- More fixes to the login
- Fixed to paralell and clustering, thanks to @AariaX

* Docker 1.5 preliminary support (#211)

* Basic docker functionality for 1.5

Preliminary docker support for 1.5. Requires headless=true, clusters=1

* Tidy up timezone, add TZ to compose file

Minor changes that should improve timezone handling, and (hopefully) improve scheduling function

* updated readme to simplify and clarify docker instructions

also removed env vars from table

* Fix syntax for cron

* Fix scheduling, add .gitattributes to normalize line endings

fixed line endings caused by Windows in crontab.template and run_daily.sh, which were breaking cron and script execution in the Docker container.

* Removed unnecessary scheduling key from config.json

This key isn't necessary for docker or the base script.

* Basic docker functionality for 1.5

Preliminary docker support for 1.5. Requires headless=true, clusters=1

Tidied up timezone, add TZ to compose file

Minor changes that should improve timezone handling, and (hopefully) improve scheduling function

updated readme to simplify and clarify docker instructions

also removed env vars from table

Fixed syntax for cron

Fixed scheduling, add .gitattributes to normalize line endings

Fixed line endings caused by Windows in crontab.template and run_daily.sh, which were breaking cron and script execution in the Docker container.

Removed unnecessary scheduling key from config.json

This key isn't necessary for docker or the base script.

* Improve scheduling handling, show logs in console

Fixes scheduling when RUN_ON_START=true, and fixes scheduled runs not appearing in docker logs.

* Update compose.yaml

revert service and container name, revert volumes for better generalization, add tips to environment to set scheduling, timezone and whether to run on container start

* Update README.md

proper container name

Co-authored-by: AariaX <196196201+AariaX@users.noreply.github.com>

---------

Co-authored-by: AariaX <196196201+AariaX@users.noreply.github.com>

* Fixes

- Reworked some of the point counting
- Reverted back to the "playwright" package
- Fixed error throw for emailPrefill

* Update config.json

* Add pre-build script

* Update package.json

* Handle 2FA in parallel mode (#219)

* catch error in reloadBadPage (#220)

* Use pre-build and simplify dockerfile (#218)

This uses the new pre-build script included in package.json to handle deps greatly simplifying the dockerfile.

* Small improvements

* Small fixes

- Fixed log spam for "Waiting for authorization"
- Increased wait from 2 to 5 seconds
- Increased search to "safer" values for default

* Experimenting with selectors

Seeing #223 I want to try if this is a good new addition, since for most user this SHOULD work just as good as clicking the entire box.

* More stuff

- Added ability to exclude logs by their function name
- Now caching config settings

* fix: don't retry on 0 (#228)

* Improvements

- Check if searches for mobile are enabled before creating the new page in the browser
- Return message if mobile search data cannot be found
- Added more selectors for coupons

* Improve Popup Dismissal

- Now executes in Parallel
- Respects a timeout of 1 second

---------

Co-authored-by: AariaX <196196201+AariaX@users.noreply.github.com>
Co-authored-by: mgrimace <55518507+mgrimace@users.noreply.github.com>
This commit is contained in:
Netsky
2025-02-15 16:14:47 +01:00
committed by GitHub
parent d1f4364e18
commit 849406c44f
37 changed files with 937 additions and 586 deletions

View File

@@ -1,4 +1,4 @@
import { Page } from 'playwright'
import { Page } from 'rebrowser-playwright'
import { MicrosoftRewardsBot } from '../index'
@@ -8,10 +8,11 @@ import { Poll } from './activities/Poll'
import { Quiz } from './activities/Quiz'
import { ThisOrThat } from './activities/ThisOrThat'
import { UrlReward } from './activities/UrlReward'
import { SearchOnBing } from './activities/SearchOnBing'
import { ReadToEarn } from './activities/ReadToEarn'
import { DailyCheckIn } from './activities/DailyCheckIn'
import { DashboardData } from '../interface/DashboardData'
import { DashboardData, MorePromotion, PromotionalItem } from '../interface/DashboardData'
export default class Activities {
@@ -51,6 +52,11 @@ export default class Activities {
await urlReward.doUrlReward(page)
}
doSearchOnBing = async (page: Page, activity: MorePromotion | PromotionalItem): Promise<void> => {
const searchOnBing = new SearchOnBing(this.bot)
await searchOnBing.doSearchOnBing(page, activity)
}
doReadToEarn = async (accessToken: string, data: DashboardData): Promise<void> => {
const readToEarn = new ReadToEarn(this.bot)
await readToEarn.doReadToEarn(accessToken, data)

View File

@@ -1,18 +1,19 @@
import { Page } from 'playwright'
import { Page } from 'rebrowser-playwright'
import readline from 'readline'
import * as crypto from 'crypto'
import { AxiosRequestConfig } from 'axios'
import { MicrosoftRewardsBot } from '../index'
import { saveSessionData } from '../util/Load'
import axios from 'axios'
import { OAuth } from '../interface/OAuth'
import * as crypto from 'crypto'
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
export class Login {
private bot: MicrosoftRewardsBot
private clientId: string = '0000000040170455'
@@ -31,20 +32,23 @@ export class Login {
// Navigate to the Bing login page
await page.goto('https://rewards.bing.com/signin')
await page.waitForLoadState('domcontentloaded').catch(() => { })
await this.bot.browser.utils.reloadBadPage(page)
// Check if account is locked
await this.checkAccountLocked(page)
const isLoggedIn = await page.waitForSelector('html[data-role-name="RewardsPortal"]', { timeout: 10_000 }).then(() => true).catch(() => false)
if (!isLoggedIn) {
// Check if account is locked
const isLocked = await page.waitForSelector('.serviceAbusePageContainer', { state: 'visible', timeout: 1000 }).then(() => true).catch(() => false)
if (isLocked) {
this.bot.log('LOGIN', 'This account has been locked!', 'error')
throw new Error('Account has been locked!')
}
await this.execLogin(page, email, password)
this.bot.log('LOGIN', 'Logged into Microsoft successfully')
this.bot.log(this.bot.isMobile, 'LOGIN', 'Logged into Microsoft successfully')
} else {
this.bot.log('LOGIN', 'Already logged in')
this.bot.log(this.bot.isMobile, 'LOGIN', 'Already logged in')
// Check if account is locked
await this.checkAccountLocked(page)
}
// Check if logged in to bing
@@ -54,28 +58,43 @@ export class Login {
await saveSessionData(this.bot.config.sessionPath, page.context(), email, this.bot.isMobile)
// We're done logging in
this.bot.log('LOGIN', 'Logged in successfully')
this.bot.log(this.bot.isMobile, 'LOGIN', 'Logged in successfully, saved login session!')
} catch (error) {
// Throw and don't continue
throw this.bot.log('LOGIN', 'An error occurred:' + error, 'error')
throw this.bot.log(this.bot.isMobile, 'LOGIN', 'An error occurred:' + error, 'error')
}
}
private async execLogin(page: Page, email: string, password: string) {
try {
await this.enterEmail(page, email)
await this.bot.utils.wait(2000)
await this.bot.browser.utils.reloadBadPage(page)
await this.bot.utils.wait(2000)
await this.enterPassword(page, password)
await this.bot.utils.wait(2000)
// Check if account is locked
await this.checkAccountLocked(page)
await this.bot.browser.utils.reloadBadPage(page)
await this.checkLoggedIn(page)
} catch (error: any) {
this.bot.log('LOGIN', 'An error occurred: ' + error.message, 'error')
} catch (error) {
this.bot.log(this.bot.isMobile, 'LOGIN', 'An error occurred: ' + error, 'error')
}
}
private async enterEmail(page: Page, email: string) {
const emailPrefilled = await page.waitForSelector('#userDisplayName', { timeout: 2_000 }).catch(() => null)
if (emailPrefilled) {
this.bot.log(this.bot.isMobile, 'LOGIN', 'Email already prefilled by Microsoft')
return
}
await page.fill('#i0116', email)
await page.click('#idSIButton9')
this.bot.log('LOGIN', 'Email entered successfully')
this.bot.log(this.bot.isMobile, 'LOGIN', 'Email entered successfully')
}
private async enterPassword(page: Page, password: string) {
@@ -84,9 +103,9 @@ export class Login {
await this.bot.utils.wait(2000)
await page.fill('#i0118', password)
await page.click('#idSIButton9')
this.bot.log('LOGIN', 'Password entered successfully')
this.bot.log(this.bot.isMobile, 'LOGIN', 'Password entered successfully')
} catch {
this.bot.log('LOGIN', 'Password entry failed or 2FA required')
this.bot.log(this.bot.isMobile, 'LOGIN', 'Password entry failed or 2FA required')
await this.handle2FA(page)
}
}
@@ -101,8 +120,8 @@ export class Login {
// SMS verification
await this.authSMSVerification(page)
}
} catch (error: any) {
this.bot.log('LOGIN', `2FA handling failed: ${error.message}`)
} catch (error) {
this.bot.log(this.bot.isMobile, 'LOGIN', `2FA handling failed: ${error}`)
}
}
@@ -111,7 +130,25 @@ export class Login {
const element = await page.waitForSelector('#displaySign', { state: 'visible', timeout: 2000 })
return await element.textContent()
} catch {
await page.click('button[aria-describedby="confirmSendTitle"]')
if (this.bot.config.parallel) {
this.bot.log(this.bot.isMobile, 'LOGIN', 'Script running in parallel, can only send 1 2FA request per account at a time!', 'log', 'yellow')
this.bot.log(this.bot.isMobile, 'LOGIN', 'Trying again in 60 seconds! Please wait...', 'log', 'yellow')
// eslint-disable-next-line no-constant-condition
while (true) {
const button = await page.waitForSelector('button[aria-describedby="pushNotificationsTitle errorDescription"]', { state: 'visible', timeout: 2000 }).catch(() => null)
if (button) {
await this.bot.utils.wait(60_000)
await button.click()
continue
} else {
break
}
}
}
await page.click('button[aria-describedby="confirmSendTitle"]').catch(() => {})
await this.bot.utils.wait(2000)
const element = await page.waitForSelector('#displaySign', { state: 'visible', timeout: 2000 })
return await element.textContent()
@@ -122,15 +159,15 @@ export class Login {
// eslint-disable-next-line no-constant-condition
while (true) {
try {
this.bot.log('LOGIN', `Press the number ${numberToPress} on your Authenticator app to approve the login`)
this.bot.log('LOGIN', 'If you press the wrong number or the "DENY" button, try again in 60 seconds')
this.bot.log(this.bot.isMobile, 'LOGIN', `Press the number ${numberToPress} on your Authenticator app to approve the login`)
this.bot.log(this.bot.isMobile, 'LOGIN', 'If you press the wrong number or the "DENY" button, try again in 60 seconds')
await page.waitForSelector('#i0281', { state: 'detached', timeout: 60000 })
await page.waitForSelector('#i0281', { state: 'detached', timeout: 60_000 })
this.bot.log('LOGIN', 'Login successfully approved!')
this.bot.log(this.bot.isMobile, 'LOGIN', 'Login successfully approved!')
break
} catch {
this.bot.log('LOGIN', 'The code is expired. Trying to get a new code...')
this.bot.log(this.bot.isMobile, 'LOGIN', 'The code is expired. Trying to get a new code...')
await page.click('button[aria-describedby="pushNotificationsTitle errorDescription"]')
numberToPress = await this.get2FACode(page)
}
@@ -138,7 +175,7 @@ export class Login {
}
private async authSMSVerification(page: Page) {
this.bot.log('LOGIN', 'SMS 2FA code required. Waiting for user input...')
this.bot.log(this.bot.isMobile, 'LOGIN', 'SMS 2FA code required. Waiting for user input...')
const code = await new Promise<string>((resolve) => {
rl.question('Enter 2FA code:\n', (input) => {
@@ -149,7 +186,7 @@ export class Login {
await page.fill('input[name="otc"]', code)
await page.keyboard.press('Enter')
this.bot.log('LOGIN', '2FA code entered successfully')
this.bot.log(this.bot.isMobile, 'LOGIN', '2FA code entered successfully')
}
private async checkLoggedIn(page: Page) {
@@ -167,12 +204,12 @@ export class Login {
// Wait for login to complete
await page.waitForSelector('html[data-role-name="RewardsPortal"]', { timeout: 10_000 })
this.bot.log('LOGIN', 'Successfully logged into the rewards portal')
this.bot.log(this.bot.isMobile, 'LOGIN', 'Successfully logged into the rewards portal')
}
private async checkBingLogin(page: Page): Promise<void> {
try {
this.bot.log('LOGIN-BING', 'Verifying Bing login')
this.bot.log(this.bot.isMobile, '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
@@ -181,12 +218,12 @@ export class Login {
const currentUrl = new URL(page.url())
if (currentUrl.hostname === 'www.bing.com' && currentUrl.pathname === '/') {
await this.bot.browser.utils.tryDismissBingCookieBanner(page)
await this.bot.browser.utils.tryDismissAllMessages(page)
const loggedIn = await this.checkBingLoginStatus(page)
// If mobile browser, skip this step
if (loggedIn || this.bot.isMobile) {
this.bot.log('LOGIN-BING', 'Bing login verification passed!')
this.bot.log(this.bot.isMobile, 'LOGIN-BING', 'Bing login verification passed!')
break
}
}
@@ -195,7 +232,7 @@ export class Login {
}
} catch (error) {
this.bot.log('LOGIN-BING', 'An error occurred:' + error, 'error')
this.bot.log(this.bot.isMobile, 'LOGIN-BING', 'An error occurred:' + error, 'error')
}
}
@@ -221,25 +258,28 @@ export class Login {
await page.goto(authorizeUrl.href)
const currentUrl = new URL(page.url())
let currentUrl = new URL(page.url())
let code: string
this.bot.log(this.bot.isMobile, 'LOGIN-APP', 'Waiting for authorization...')
// eslint-disable-next-line no-constant-condition
while (true) {
this.bot.log('LOGIN-APP', 'Waiting for authorization')
if (currentUrl.hostname === 'login.live.com' && currentUrl.pathname === '/oauth20_desktop.srf') {
code = currentUrl.searchParams.get('code')!
break
}
}
currentUrl = new URL(page.url())
await this.bot.utils.wait(5000)
}
const body = new URLSearchParams()
body.append('grant_type', 'authorization_code')
body.append('client_id', this.clientId)
body.append('code', code)
body.append('redirect_uri', this.redirectUrl)
const tokenRequest = {
const tokenRequest: AxiosRequestConfig = {
url: this.tokenUrl,
method: 'POST',
headers: {
@@ -248,10 +288,18 @@ export class Login {
data: body.toString()
}
const tokenResponse = await axios(tokenRequest)
const tokenResponse = await this.bot.axios.request(tokenRequest)
const tokenData: OAuth = await tokenResponse.data
this.bot.log('LOGIN-APP', 'Successfully authorized')
this.bot.log(this.bot.isMobile, 'LOGIN-APP', 'Successfully authorized')
return tokenData.access_token
}
private async checkAccountLocked(page: Page) {
await this.bot.utils.wait(2000)
const isLocked = await page.waitForSelector('#serviceAbuseLandingTitle', { state: 'visible', timeout: 1000 }).then(() => true).catch(() => false)
if (isLocked) {
throw this.bot.log(this.bot.isMobile, 'CHECK-LOCKED', 'This account has been locked! Remove the account from "accounts.json" and restart!', 'error')
}
}
}

View File

@@ -1,4 +1,4 @@
import { Page } from 'playwright'
import { Page } from 'rebrowser-playwright'
import { DashboardData, MorePromotion, PromotionalItem, PunchCard } from '../interface/DashboardData'
@@ -18,12 +18,12 @@ export class Workers {
const activitiesUncompleted = todayData?.filter(x => !x.complete && x.pointProgressMax > 0) ?? []
if (!activitiesUncompleted.length) {
this.bot.log('DAILY-SET', 'All Daily Set" items have already been completed')
this.bot.log(this.bot.isMobile, 'DAILY-SET', 'All Daily Set" items have already been completed')
return
}
// Solve Activities
this.bot.log('DAILY-SET', 'Started solving "Daily Set" items')
this.bot.log(this.bot.isMobile, 'DAILY-SET', 'Started solving "Daily Set" items')
await this.solveActivities(page, activitiesUncompleted)
@@ -32,27 +32,34 @@ export class Workers {
// Always return to the homepage if not already
await this.bot.browser.func.goHome(page)
this.bot.log('DAILY-SET', 'All "Daily Set" items have been completed')
this.bot.log(this.bot.isMobile, 'DAILY-SET', 'All "Daily Set" items have been completed')
}
// Punch Card
async doPunchCard(page: Page, data: DashboardData) {
const punchCardsUncompleted = data.punchCards?.filter(x => !x.parentPromotion?.complete) ?? [] // Only return uncompleted punch cards
const punchCardsUncompleted = data.punchCards?.filter(x => x.parentPromotion && !x.parentPromotion.complete) ?? [] // Only return uncompleted punch cards
if (!punchCardsUncompleted.length) {
this.bot.log('PUNCH-CARD', 'All "Punch Cards" have already been completed')
this.bot.log(this.bot.isMobile, 'PUNCH-CARD', 'All "Punch Cards" have already been completed')
return
}
for (const punchCard of punchCardsUncompleted) {
// Ensure parentPromotion exists before proceeding
if (!punchCard.parentPromotion?.title) {
this.bot.log(this.bot.isMobile, 'PUNCH-CARD', `Skipped punchcard "${punchCard.name}" | Reason: Parent promotion is missing!`, 'warn')
continue
}
// Get latest page for each card
page = await this.bot.browser.utils.getLatestTab(page)
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}"`)
this.bot.log(this.bot.isMobile, 'PUNCH-CARD', `Started solving "Punch Card" items for punchcard: "${punchCard.parentPromotion.title}"`)
// Got to punch card index page in a new tab
await page.goto(punchCard.parentPromotion.destinationUrl, { referer: this.bot.config.baseURL })
@@ -72,10 +79,10 @@ export class Workers {
await this.bot.browser.func.goHome(page)
}
this.bot.log('PUNCH-CARD', `All items for punchcard: "${punchCard.parentPromotion.title}" have been completed`)
this.bot.log(this.bot.isMobile, 'PUNCH-CARD', `All items for punchcard: "${punchCard.parentPromotion.title}" have been completed`)
}
this.bot.log('PUNCH-CARD', 'All "Punch Card" items have been completed')
this.bot.log(this.bot.isMobile, 'PUNCH-CARD', 'All "Punch Card" items have been completed')
}
// More Promotions
@@ -90,12 +97,12 @@ export class Workers {
const activitiesUncompleted = morePromotions?.filter(x => !x.complete && x.pointProgressMax > 0 && x.exclusiveLockedFeatureStatus !== 'locked') ?? []
if (!activitiesUncompleted.length) {
this.bot.log('MORE-PROMOTIONS', 'All "More Promotion" items have already been completed')
this.bot.log(this.bot.isMobile, 'MORE-PROMOTIONS', 'All "More Promotion" items have already been completed')
return
}
// Solve Activities
this.bot.log('MORE-PROMOTIONS', 'Started solving "More Promotions" items')
this.bot.log(this.bot.isMobile, 'MORE-PROMOTIONS', 'Started solving "More Promotions" items')
page = await this.bot.browser.utils.getLatestTab(page)
@@ -106,7 +113,7 @@ export class Workers {
// Always return to the homepage if not already
await this.bot.browser.func.goHome(page)
this.bot.log('MORE-PROMOTIONS', 'All "More Promotion" items have been completed')
this.bot.log(this.bot.isMobile, 'MORE-PROMOTIONS', 'All "More Promotion" items have been completed')
}
// Solve all the different types of activities
@@ -132,28 +139,22 @@ export class Workers {
}
let selector = `[data-bi-id^="${activity.offerId}"]`
let selector = `[data-bi-id^="${activity.offerId}"] .pointLink:not(.contentContainer .pointLink)`
if (punchCard) {
selector = await this.bot.browser.func.getPunchCardActivity(activityPage, activity)
} else if (activity.name.toLowerCase().includes('membercenter')) {
selector = `[data-bi-id^="${activity.name}"]`
} else if (activity.name.toLowerCase().includes('membercenter') || activity.name.toLowerCase().includes('exploreonbing')) {
selector = `[data-bi-id^="${activity.name}"] .pointLink:not(.contentContainer .pointLink)`
}
// Click element, it will be opened in a new tab
await activityPage.click(selector)
// Select the new activity page
activityPage = await this.bot.browser.utils.getLatestTab(activityPage)
// Wait for the new tab to fully load, ignore error.
/*
Due to common false timeout on this function, we're ignoring the error regardless, if it worked then it's faster,
if it didn't then it gave enough time for the page to load.
*/
await activityPage.waitForLoadState('networkidle', { timeout: 10_000 }).catch(() => { })
await this.bot.utils.wait(5000)
await this.bot.utils.wait(2000)
switch (activity.promotionType) {
// Quiz (Poll, Quiz or ABC)
@@ -163,23 +164,31 @@ export class Workers {
case 10:
// Normal poll
if (activity.destinationUrl.toLowerCase().includes('pollscenarioid')) {
this.bot.log('ACTIVITY', `Found activity type: "Poll" title: "${activity.title}"`)
this.bot.log(this.bot.isMobile, 'ACTIVITY', `Found activity type: "Poll" title: "${activity.title}"`)
await activityPage.click(selector)
activityPage = await this.bot.browser.utils.getLatestTab(activityPage)
await this.bot.activities.doPoll(activityPage)
} else { // ABC
this.bot.log('ACTIVITY', `Found activity type: "ABC" title: "${activity.title}"`)
this.bot.log(this.bot.isMobile, 'ACTIVITY', `Found activity type: "ABC" title: "${activity.title}"`)
await activityPage.click(selector)
activityPage = await this.bot.browser.utils.getLatestTab(activityPage)
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}"`)
this.bot.log(this.bot.isMobile, 'ACTIVITY', `Found activity type: "ThisOrThat" title: "${activity.title}"`)
await activityPage.click(selector)
activityPage = await this.bot.browser.utils.getLatestTab(activityPage)
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}"`)
this.bot.log(this.bot.isMobile, 'ACTIVITY', `Found activity type: "Quiz" title: "${activity.title}"`)
await activityPage.click(selector)
activityPage = await this.bot.browser.utils.getLatestTab(activityPage)
await this.bot.activities.doQuiz(activityPage)
break
}
@@ -187,14 +196,24 @@ export class Workers {
// UrlReward (Visit)
case 'urlreward':
this.bot.log('ACTIVITY', `Found activity type: "UrlReward" title: "${activity.title}"`)
await this.bot.activities.doUrlReward(activityPage)
// Search on Bing are subtypes of "urlreward"
if (activity.name.toLowerCase().includes('exploreonbing')) {
this.bot.log(this.bot.isMobile, 'ACTIVITY', `Found activity type: "SearchOnBing" title: "${activity.title}"`)
await activityPage.click(selector)
activityPage = await this.bot.browser.utils.getLatestTab(activityPage)
await this.bot.activities.doSearchOnBing(activityPage, activity)
} else {
this.bot.log(this.bot.isMobile, 'ACTIVITY', `Found activity type: "UrlReward" title: "${activity.title}"`)
await activityPage.click(selector)
activityPage = await this.bot.browser.utils.getLatestTab(activityPage)
await this.bot.activities.doUrlReward(activityPage)
}
break
// Misc, Usually UrlReward Type
// Unsupported types
default:
this.bot.log('ACTIVITY', `Found activity type: "Misc" title: "${activity.title}"`)
await this.bot.activities.doUrlReward(activityPage)
this.bot.log(this.bot.isMobile, 'ACTIVITY', `Skipped activity "${activity.title}" | Reason: Unsupported type: "${activity.promotionType}"!`, 'warn')
break
}
@@ -202,7 +221,7 @@ export class Workers {
await this.bot.utils.wait(2000)
} catch (error) {
this.bot.log('ACTIVITY', 'An error occurred:' + error, 'error')
this.bot.log(this.bot.isMobile, 'ACTIVITY', 'An error occurred:' + error, 'error')
}
}

View File

@@ -1,4 +1,4 @@
import { Page } from 'playwright'
import { Page } from 'rebrowser-playwright'
import { Workers } from '../Workers'
@@ -6,10 +6,10 @@ import { Workers } from '../Workers'
export class ABC extends Workers {
async doABC(page: Page) {
this.bot.log('ABC', 'Trying to complete poll')
this.bot.log(this.bot.isMobile, 'ABC', 'Trying to complete poll')
try {
let $ = await this.bot.browser.func.refreshCheerio(page)
let $ = await this.bot.browser.func.loadInCheerio(page)
// Don't loop more than 15 in case unable to solve, would lock otherwise
const maxIterations = 15
@@ -30,7 +30,7 @@ export class ABC extends Workers {
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.browser.func.loadInCheerio(page)
await this.bot.utils.wait(1000)
}
@@ -38,14 +38,14 @@ export class ABC extends Workers {
await page.close()
if (i === maxIterations) {
this.bot.log('ABC', 'Failed to solve quiz, exceeded max iterations of 15', 'warn')
this.bot.log(this.bot.isMobile, 'ABC', 'Failed to solve quiz, exceeded max iterations of 15', 'warn')
} else {
this.bot.log('ABC', 'Completed the ABC successfully')
this.bot.log(this.bot.isMobile, 'ABC', 'Completed the ABC successfully')
}
} catch (error) {
await page.close()
this.bot.log('ABC', 'An error occurred:' + error, 'error')
this.bot.log(this.bot.isMobile, 'ABC', 'An error occurred:' + error, 'error')
}
}

View File

@@ -1,5 +1,5 @@
import axios from 'axios'
import { randomBytes } from 'crypto'
import { AxiosRequestConfig } from 'axios'
import { Workers } from '../Workers'
@@ -8,7 +8,7 @@ import { DashboardData } from '../../interface/DashboardData'
export class DailyCheckIn extends Workers {
public async doDailyCheckIn(accessToken: string, data: DashboardData) {
this.bot.log('DAILY-CHECK-IN', 'Starting Daily Check In')
this.bot.log(this.bot.isMobile, 'DAILY-CHECK-IN', 'Starting Daily Check In')
try {
let geoLocale = data.userProfile.attributes.country
@@ -24,7 +24,7 @@ export class DailyCheckIn extends Workers {
}
}
const claimRequest = {
const claimRequest: AxiosRequestConfig = {
url: 'https://prod.rewardsplatform.microsoft.com/dapi/me/activities',
method: 'POST',
headers: {
@@ -36,12 +36,12 @@ export class DailyCheckIn extends Workers {
data: JSON.stringify(jsonData)
}
const claimResponse = await axios(claimRequest)
const claimedPoint = parseInt((await claimResponse.data).response.activity.p)
const claimResponse = await this.bot.axios.request(claimRequest)
const claimedPoint = parseInt((await claimResponse.data).response?.activity?.p) ?? 0
this.bot.log('DAILY-CHECK-IN', claimedPoint > 0 ? `Claimed ${claimedPoint} points` : 'Already claimed today')
this.bot.log(this.bot.isMobile, 'DAILY-CHECK-IN', claimedPoint > 0 ? `Claimed ${claimedPoint} points` : 'Already claimed today')
} catch (error) {
this.bot.log('DAILY-CHECK-IN', 'An error occurred:' + error, 'error')
this.bot.log(this.bot.isMobile, 'DAILY-CHECK-IN', 'An error occurred:' + error, 'error')
}
}

View File

@@ -1,4 +1,4 @@
import { Page } from 'playwright'
import { Page } from 'rebrowser-playwright'
import { Workers } from '../Workers'
@@ -6,7 +6,7 @@ import { Workers } from '../Workers'
export class Poll extends Workers {
async doPoll(page: Page) {
this.bot.log('POLL', 'Trying to complete poll')
this.bot.log(this.bot.isMobile, 'POLL', 'Trying to complete poll')
try {
const buttonId = `#btoption${Math.floor(this.bot.utils.randomNumber(0, 1))}`
@@ -19,10 +19,10 @@ export class Poll extends Workers {
await this.bot.utils.wait(4000)
await page.close()
this.bot.log('POLL', 'Completed the poll successfully')
this.bot.log(this.bot.isMobile, 'POLL', 'Completed the poll successfully')
} catch (error) {
await page.close()
this.bot.log('POLL', 'An error occurred:' + error, 'error')
this.bot.log(this.bot.isMobile, 'POLL', 'An error occurred:' + error, 'error')
}
}

View File

@@ -1,4 +1,4 @@
import { Page } from 'playwright'
import { Page } from 'rebrowser-playwright'
import { Workers } from '../Workers'
@@ -6,7 +6,7 @@ import { Workers } from '../Workers'
export class Quiz extends Workers {
async doQuiz(page: Page) {
this.bot.log('QUIZ', 'Trying to complete quiz')
this.bot.log(this.bot.isMobile, 'QUIZ', 'Trying to complete quiz')
try {
// Check if the quiz has been started or not
@@ -14,7 +14,7 @@ export class Quiz extends Workers {
if (quizNotStarted) {
await page.click('#rqStartQuiz')
} else {
this.bot.log('QUIZ', 'Quiz has already been started, trying to finish it')
this.bot.log(this.bot.isMobile, 'QUIZ', 'Quiz has already been started, trying to finish it')
}
await this.bot.utils.wait(2000)
@@ -47,7 +47,7 @@ export class Quiz extends Workers {
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')
this.bot.log(this.bot.isMobile, 'QUIZ', 'An error occurred, refresh was unsuccessful', 'error')
return
}
}
@@ -69,7 +69,7 @@ export class Quiz extends Workers {
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')
this.bot.log(this.bot.isMobile, 'QUIZ', 'An error occurred, refresh was unsuccessful', 'error')
return
}
}
@@ -81,10 +81,11 @@ export class Quiz extends Workers {
// Done with
await this.bot.utils.wait(2000)
await page.close()
this.bot.log('QUIZ', 'Completed the quiz successfully')
this.bot.log(this.bot.isMobile, 'QUIZ', 'Completed the quiz successfully')
} catch (error) {
await page.close()
this.bot.log('QUIZ', 'An error occurred:' + error, 'error')
this.bot.log(this.bot.isMobile, 'QUIZ', 'An error occurred:' + error, 'error')
}
}

View File

@@ -1,5 +1,5 @@
import axios from 'axios'
import { randomBytes } from 'crypto'
import { AxiosRequestConfig } from 'axios'
import { Workers } from '../Workers'
@@ -8,13 +8,13 @@ import { DashboardData } from '../../interface/DashboardData'
export class ReadToEarn extends Workers {
public async doReadToEarn(accessToken: string, data: DashboardData) {
this.bot.log('READ-TO-EARN', 'Starting Read to Earn')
this.bot.log(this.bot.isMobile, 'READ-TO-EARN', 'Starting Read to Earn')
try {
let geoLocale = data.userProfile.attributes.country
geoLocale = (this.bot.config.searchSettings.useGeoLocaleQueries && geoLocale.length === 2) ? geoLocale.toLowerCase() : 'us'
const userDataRequest = {
const userDataRequest: AxiosRequestConfig = {
url: 'https://prod.rewardsplatform.microsoft.com/dapi/me',
method: 'GET',
headers: {
@@ -23,7 +23,7 @@ export class ReadToEarn extends Workers {
'X-Rewards-Language': 'en'
}
}
const userDataResponse = await axios(userDataRequest)
const userDataResponse = await this.bot.axios.request(userDataRequest)
const userData = (await userDataResponse.data).response
let userBalance = userData.balance
@@ -52,22 +52,22 @@ export class ReadToEarn extends Workers {
data: JSON.stringify(jsonData)
}
const claimResponse = await axios(claimRequest)
const claimResponse = await this.bot.axios.request(claimRequest)
const newBalance = (await claimResponse.data).response.balance
if (newBalance == userBalance) {
this.bot.log('READ-TO-EARN', 'Read all available articles')
this.bot.log(this.bot.isMobile, 'READ-TO-EARN', 'Read all available articles')
break
} else {
this.bot.log('READ-TO-EARN', `Read article ${i + 1} of ${articleCount} max | Gained ${newBalance - userBalance} Points`)
this.bot.log(this.bot.isMobile, 'READ-TO-EARN', `Read article ${i + 1} of ${articleCount} max | Gained ${newBalance - userBalance} Points`)
userBalance = newBalance
await this.bot.utils.wait(Math.floor(this.bot.utils.randomNumber(this.bot.config.searchSettings.searchDelay.min, this.bot.config.searchSettings.searchDelay.max)))
await this.bot.utils.wait(Math.floor(this.bot.utils.randomNumber(this.bot.utils.stringToMs(this.bot.config.searchSettings.searchDelay.min), this.bot.utils.stringToMs(this.bot.config.searchSettings.searchDelay.max))))
}
}
this.bot.log('READ-TO-EARN', 'Completed Read to Earn')
this.bot.log(this.bot.isMobile, 'READ-TO-EARN', 'Completed Read to Earn')
} catch (error) {
this.bot.log('READ-TO-EARN', 'An error occurred:' + error, 'error')
this.bot.log(this.bot.isMobile, 'READ-TO-EARN', 'An error occurred:' + error, 'error')
}
}
}

View File

@@ -1,5 +1,4 @@
import { Page } from 'playwright'
import axios from 'axios'
import { Page } from 'rebrowser-playwright'
import { platform } from 'os'
import { Workers } from '../Workers'
@@ -14,7 +13,7 @@ export class Search extends Workers {
private searchPageURL = ''
public async doSearch(page: Page, data: DashboardData) {
this.bot.log('SEARCH-BING', 'Starting Bing searches')
this.bot.log(this.bot.isMobile, 'SEARCH-BING', 'Starting Bing searches')
page = await this.bot.browser.utils.getLatestTab(page)
@@ -22,7 +21,7 @@ export class Search extends Workers {
let missingPoints = this.calculatePoints(searchCounters)
if (missingPoints === 0) {
this.bot.log('SEARCH-BING', `Bing searches for ${this.bot.isMobile ? 'MOBILE' : 'DESKTOP'} have already been completed`)
this.bot.log(this.bot.isMobile, 'SEARCH-BING', 'Bing searches have already been completed')
return
}
@@ -36,19 +35,21 @@ export class Search extends Workers {
// Go to bing
await page.goto(this.searchPageURL ? this.searchPageURL : this.bingHome)
await this.bot.utils.wait(2000)
await this.bot.browser.utils.tryDismissAllMessages(page)
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[] = []
// Mobile search doesn't seem to like related queries?
googleSearchQueries.forEach(x => { this.bot.isMobile ? queries.push(x.topic) : queries.push(x.topic, ...x.related) })
await this.bot.browser.utils.tryDismissBingCookieBanner(page)
// 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: ${this.bot.isMobile}`)
this.bot.log(this.bot.isMobile, 'SEARCH-BING', `${missingPoints} Points Remaining | Query: ${query}`)
searchCounters = await this.bingSearch(page, query)
const newMissingPoints = this.calculatePoints(searchCounters)
@@ -67,14 +68,14 @@ export class Search extends Workers {
}
// Only for mobile searches
if (maxLoop > 3 && this.bot.isMobile) {
this.bot.log('SEARCH-BING-MOBILE', 'Search didn\'t gain point for 3 iterations, likely bad User-Agent', 'warn')
if (maxLoop > 5 && this.bot.isMobile) {
this.bot.log(this.bot.isMobile, 'SEARCH-BING', 'Search didn\'t gain point for 5 iterations, likely bad User-Agent', 'warn')
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')
this.bot.log(this.bot.isMobile, '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
}
@@ -87,7 +88,7 @@ export class Search extends Workers {
// If we still got remaining search queries, generate extra ones
if (missingPoints > 0) {
this.bot.log('SEARCH-BING', `Search completed but we're missing ${missingPoints} points, generating extra searches`)
this.bot.log(this.bot.isMobile, 'SEARCH-BING', `Search completed but we're missing ${missingPoints} points, generating extra searches`)
let i = 0
while (missingPoints > 0) {
@@ -98,7 +99,7 @@ export class Search extends Workers {
if (relatedTerms.length > 3) {
// Search for the first 2 related terms
for (const term of relatedTerms.slice(1, 3)) {
this.bot.log('SEARCH-BING-EXTRA', `${missingPoints} Points Remaining | Query: ${term} | Mobile: ${this.bot.isMobile}`)
this.bot.log(this.bot.isMobile, 'SEARCH-BING-EXTRA', `${missingPoints} Points Remaining | Query: ${term}`)
searchCounters = await this.bingSearch(page, term)
const newMissingPoints = this.calculatePoints(searchCounters)
@@ -119,7 +120,7 @@ export class Search extends Workers {
// Try 5 more times, then we tried a total of 15 times, fair to say it's stuck
if (maxLoop > 5) {
this.bot.log('SEARCH-BING-EXTRA', 'Search didn\'t gain point for 5 iterations aborting searches', 'warn')
this.bot.log(this.bot.isMobile, 'SEARCH-BING-EXTRA', 'Search didn\'t gain point for 5 iterations aborting searches', 'warn')
return
}
}
@@ -127,7 +128,7 @@ export class Search extends Workers {
}
}
this.bot.log('SEARCH-BING', 'Completed searches')
this.bot.log(this.bot.isMobile, 'SEARCH-BING', 'Completed searches')
}
private async bingSearch(searchPage: Page, query: string) {
@@ -157,12 +158,14 @@ export class Search extends Workers {
await searchPage.keyboard.type(query)
await searchPage.keyboard.press('Enter')
await this.bot.utils.wait(1000)
await this.bot.utils.wait(3000)
// Bing.com in Chrome opens a new tab when searching
const resultPage = await this.bot.browser.utils.getLatestTab(searchPage)
this.searchPageURL = new URL(resultPage.url()).href // Set the results page
await this.bot.browser.utils.reloadBadPage(resultPage)
if (this.bot.config.searchSettings.scrollRandomResults) {
await this.bot.utils.wait(2000)
await this.randomScroll(resultPage)
@@ -174,19 +177,19 @@ export class Search extends Workers {
}
// Delay between searches
await this.bot.utils.wait(Math.floor(this.bot.utils.randomNumber(this.bot.config.searchSettings.searchDelay.min, this.bot.config.searchSettings.searchDelay.max)))
await this.bot.utils.wait(Math.floor(this.bot.utils.randomNumber(this.bot.utils.stringToMs(this.bot.config.searchSettings.searchDelay.min), this.bot.utils.stringToMs(this.bot.config.searchSettings.searchDelay.max))))
return await this.bot.browser.func.getSearchPoints()
} catch (error) {
if (i === 5) {
this.bot.log('SEARCH-BING', 'Failed after 5 retries... An error occurred:' + error, 'error')
this.bot.log(this.bot.isMobile, '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')
this.bot.log(this.bot.isMobile, 'SEARCH-BING', 'Search failed, An error occurred:' + error, 'error')
this.bot.log(this.bot.isMobile, 'SEARCH-BING', `Retrying search, attempt ${i}/5`, 'warn')
// Reset the tabs
const lastTab = await this.bot.browser.utils.getLatestTab(searchPage)
@@ -196,7 +199,7 @@ export class Search extends Workers {
}
}
this.bot.log('SEARCH-BING', 'Search failed after 5 retries, ending', 'error')
this.bot.log(this.bot.isMobile, 'SEARCH-BING', 'Search failed after 5 retries, ending', 'error')
return await this.bot.browser.func.getSearchPoints()
}
@@ -206,7 +209,7 @@ export class Search extends Workers {
geoLocale = (this.bot.config.searchSettings.useGeoLocaleQueries && geoLocale.length === 2) ? geoLocale.toUpperCase() : 'US'
this.bot.log('SEARCH-GOOGLE-TRENDS', `Generating search queries, can take a while! | GeoLocale: ${geoLocale}`)
this.bot.log(this.bot.isMobile, 'SEARCH-GOOGLE-TRENDS', `Generating search queries, can take a while! | GeoLocale: ${geoLocale}`)
while (queryCount > queryTerms.length) {
i += 1
@@ -223,7 +226,7 @@ export class Search extends Workers {
}
}
const response = await axios(request)
const response = await this.bot.axios.request(request)
const data: GoogleTrends = JSON.parse((await response.data).slice(5))
@@ -235,7 +238,8 @@ export class Search extends Workers {
}
} catch (error) {
this.bot.log('SEARCH-GOOGLE-TRENDS', 'An error occurred:' + error, 'error')
this.bot.log(this.bot.isMobile, 'SEARCH-GOOGLE-TRENDS', 'An error occurred:' + error, 'error')
break
}
}
@@ -252,12 +256,13 @@ export class Search extends Workers {
}
}
const response = await axios(request)
const response = await this.bot.axios.request(request)
return response.data[1] as string[]
} catch (error) {
this.bot.log('SEARCH-BING-RELATED', 'An error occurred:' + error, 'error')
this.bot.log(this.bot.isMobile, 'SEARCH-BING-RELATED', 'An error occurred:' + error, 'error')
}
return []
}
@@ -280,7 +285,7 @@ export class Search extends Workers {
}, randomScrollPosition)
} catch (error) {
this.bot.log('SEARCH-RANDOM-SCROLL', 'An error occurred:' + error, 'error')
this.bot.log(this.bot.isMobile, 'SEARCH-RANDOM-SCROLL', 'An error occurred:' + error, 'error')
}
}
@@ -288,6 +293,9 @@ export class Search extends Workers {
try {
await page.click('#b_results .b_algo h2', { timeout: 2000 }).catch(() => { }) // Since we don't really care if it did it or not
// Only used if the browser is not the edge browser (continue on Edge popup)
await this.closeContinuePopup(page)
// Stay for 10 seconds for page to load and "visit"
await this.bot.utils.wait(10_000)
@@ -309,7 +317,7 @@ export class Search extends Workers {
}
} catch (error) {
this.bot.log('SEARCH-RANDOM-CLICK', 'An error occurred:' + error, 'error')
this.bot.log(this.bot.isMobile, 'SEARCH-RANDOM-CLICK', 'An error occurred:' + error, 'error')
}
}
@@ -317,23 +325,35 @@ export class Search extends Workers {
const browser = lastTab.context()
const tabs = browser.pages()
// If more than 2 tabs are open, close the last tab
if (tabs.length > 2) {
await lastTab.close()
try {
if (tabs.length > 2) {
// If more than 2 tabs are open, close the last tab
// If only 1 tab is open, open a new one to search in
} else if (tabs.length === 1) {
const newPage = await browser.newPage()
await this.bot.utils.wait(1000)
await newPage.goto(this.bingHome)
await this.bot.utils.wait(3000)
this.searchPageURL = newPage.url()
await lastTab.close()
this.bot.log(this.bot.isMobile, 'SEARCH-CLOSE-TABS', `More than 2 were open, closed the last tab: "${new URL(lastTab.url()).host}"`)
// Else reset the last tab back to the search listing or Bing.com
} else {
lastTab = await this.bot.browser.utils.getLatestTab(lastTab)
await lastTab.goto(this.searchPageURL ? this.searchPageURL : this.bingHome)
} else if (tabs.length === 1) {
// If only 1 tab is open, open a new one to search in
const newPage = await browser.newPage()
await this.bot.utils.wait(1000)
await newPage.goto(this.bingHome)
await this.bot.utils.wait(3000)
this.searchPageURL = newPage.url()
this.bot.log(this.bot.isMobile, 'SEARCH-CLOSE-TABS', 'There was only 1 tab open, crated a new one')
} else {
// Else reset the last tab back to the search listing or Bing.com
lastTab = await this.bot.browser.utils.getLatestTab(lastTab)
await lastTab.goto(this.searchPageURL ? this.searchPageURL : this.bingHome)
}
} catch (error) {
this.bot.log(this.bot.isMobile, 'SEARCH-CLOSE-TABS', 'An error occurred:' + error, 'error')
}
}
private calculatePoints(counters: Counters) {
@@ -348,4 +368,18 @@ export class Search extends Workers {
return missingPoints
}
}
private async closeContinuePopup(page: Page) {
try {
await page.waitForSelector('#sacs_close', { timeout: 1000 })
const continueButton = await page.$('#sacs_close')
if (continueButton) {
await continueButton.click()
}
} catch (error) {
// Continue if element is not found or other error occurs
}
}
}

View File

@@ -0,0 +1,75 @@
import { Page } from 'rebrowser-playwright'
import * as fs from 'fs'
import path from 'path'
import { Workers } from '../Workers'
import { MorePromotion, PromotionalItem } from '../../interface/DashboardData'
export class SearchOnBing extends Workers {
async doSearchOnBing(page: Page, activity: MorePromotion | PromotionalItem) {
this.bot.log(this.bot.isMobile, 'SEARCH-ON-BING', 'Trying to complete SearchOnBing')
try {
await this.bot.utils.wait(5000)
await this.bot.browser.utils.tryDismissAllMessages(page)
const query = await this.getSearchQuery(activity.title)
const searchBar = '#sb_form_q'
await page.waitForSelector(searchBar, { state: 'visible', timeout: 10_000 })
await page.click(searchBar)
await this.bot.utils.wait(500)
await page.keyboard.type(query)
await page.keyboard.press('Enter')
await this.bot.utils.wait(3000)
await page.close()
this.bot.log(this.bot.isMobile, 'SEARCH-ON-BING', 'Completed the SearchOnBing successfully')
} catch (error) {
await page.close()
this.bot.log(this.bot.isMobile, 'SEARCH-ON-BING', 'An error occurred:' + error, 'error')
}
}
private async getSearchQuery(title: string): Promise<string> {
interface Queries {
title: string;
queries: string[]
}
let queries: Queries[] = []
try {
if (this.bot.config.searchOnBingLocalQueries) {
const data = fs.readFileSync(path.join(__dirname, '../queries.json'), 'utf8')
queries = JSON.parse(data)
} else {
// Fetch from the repo directly so the user doesn't need to redownload the script for the new activities
const response = await this.bot.axios.request({
method: 'GET',
url: 'https://raw.githubusercontent.com/TheNetsky/Microsoft-Rewards-Script/refs/heads/main/src/functions/queries.json'
})
queries = response.data
}
const answers = queries.find(x => this.normalizeString(x.title) === this.normalizeString(title))
const answer = answers ? this.bot.utils.shuffleArray(answers?.queries)[0] as string : title
this.bot.log(this.bot.isMobile, 'SEARCH-ON-BING-QUERY', `Fetched answer: ${answer} | question: ${title}`)
return answer
} catch (error) {
this.bot.log(this.bot.isMobile, 'SEARCH-ON-BING-QUERY', 'An error occurred:' + error, 'error')
return title
}
}
private normalizeString(string: string): string {
return string.normalize('NFD').trim().toLowerCase().replace(/[^\x20-\x7E]/g, '').replace(/[?!]/g, '')
}
}

View File

@@ -1,4 +1,4 @@
import { Page } from 'playwright'
import { Page } from 'rebrowser-playwright'
import { Workers } from '../Workers'
@@ -6,7 +6,7 @@ import { Workers } from '../Workers'
export class ThisOrThat extends Workers {
async doThisOrThat(page: Page) {
this.bot.log('THIS-OR-THAT', 'Trying to complete ThisOrThat')
this.bot.log(this.bot.isMobile, 'THIS-OR-THAT', 'Trying to complete ThisOrThat')
try {
@@ -15,7 +15,7 @@ export class ThisOrThat extends Workers {
if (quizNotStarted) {
await page.click('#rqStartQuiz')
} else {
this.bot.log('THIS-OR-THAT', 'ThisOrThat has already been started, trying to finish it')
this.bot.log(this.bot.isMobile, 'THIS-OR-THAT', 'ThisOrThat has already been started, trying to finish it')
}
await this.bot.utils.wait(2000)
@@ -32,15 +32,15 @@ export class ThisOrThat extends Workers {
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')
this.bot.log(this.bot.isMobile, 'QUIZ', 'An error occurred, refresh was unsuccessful', 'error')
return
}
}
this.bot.log('THIS-OR-THAT', 'Completed the ThisOrThat successfully')
this.bot.log(this.bot.isMobile, 'THIS-OR-THAT', 'Completed the ThisOrThat successfully')
} catch (error) {
await page.close()
this.bot.log('THIS-OR-THAT', 'An error occurred:' + error, 'error')
this.bot.log(this.bot.isMobile, 'THIS-OR-THAT', 'An error occurred:' + error, 'error')
}
}

View File

@@ -1,4 +1,4 @@
import { Page } from 'playwright'
import { Page } from 'rebrowser-playwright'
import { Workers } from '../Workers'
@@ -6,17 +6,17 @@ import { Workers } from '../Workers'
export class UrlReward extends Workers {
async doUrlReward(page: Page) {
this.bot.log('URL-REWARD', 'Trying to complete UrlReward')
this.bot.log(this.bot.isMobile, 'URL-REWARD', 'Trying to complete UrlReward')
try {
this.bot.utils.wait(2000)
await page.close()
this.bot.log('URL-REWARD', 'Completed the UrlReward successfully')
this.bot.log(this.bot.isMobile, 'URL-REWARD', 'Completed the UrlReward successfully')
} catch (error) {
await page.close()
this.bot.log('URL-REWARD', 'An error occurred:' + error, 'error')
this.bot.log(this.bot.isMobile, 'URL-REWARD', 'An error occurred:' + error, 'error')
}
}