mirror of
https://github.com/TheNetsky/Microsoft-Rewards-Script.git
synced 2026-01-11 19:06:18 +00:00
Support new quest to get points (#138)
* support passwordless auth (using Authenticator app) * update readme * feat: added mobile app tasks (daily check in + read to earn) * fix some stuff * make ReadToEarn use the delay config * fix daily reward search per week * reorder mobile tasks * fix message * Search fixes, reformatting and types --------- Co-authored-by: TheNetsky <56271887+TheNetsky@users.noreply.github.com>
This commit is contained in:
@@ -102,6 +102,8 @@ Under development, however mainly for personal use!
|
|||||||
- [x] Completing Punchcards
|
- [x] Completing Punchcards
|
||||||
- [x] Solving This Or That Quiz (Random)
|
- [x] Solving This Or That Quiz (Random)
|
||||||
- [x] Solving ABC Quiz
|
- [x] Solving ABC Quiz
|
||||||
|
- [x] Completing Daily Check In
|
||||||
|
- [x] Completing Read To Earn
|
||||||
- [ ] Completing Shopping Game
|
- [ ] Completing Shopping Game
|
||||||
- [ ] Completing Gaming Tab
|
- [ ] Completing Gaming Tab
|
||||||
- [x] Clustering Support
|
- [x] Clustering Support
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"typescript": "^5.5.4"
|
"typescript": "^5.5.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.4",
|
"axios": "^1.7.5",
|
||||||
"cheerio": "^1.0.0",
|
"cheerio": "^1.0.0",
|
||||||
"fingerprint-generator": "^2.1.54",
|
"fingerprint-generator": "^2.1.54",
|
||||||
"fingerprint-injector": "^2.1.54",
|
"fingerprint-injector": "^2.1.54",
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { Page } from 'playwright'
|
import { Page } from 'playwright'
|
||||||
import { CheerioAPI, load } from 'cheerio'
|
import { CheerioAPI, load } from 'cheerio'
|
||||||
|
import axios, { AxiosRequestConfig } from 'axios'
|
||||||
|
|
||||||
import { MicrosoftRewardsBot } from '../index'
|
import { MicrosoftRewardsBot } from '../index'
|
||||||
|
|
||||||
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 { AppUserData } from '../interface/AppUserData'
|
||||||
|
|
||||||
|
|
||||||
export default class BrowserFunc {
|
export default class BrowserFunc {
|
||||||
@@ -131,10 +133,10 @@ export default class BrowserFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get total earnable points
|
* Get total earnable points with web browser
|
||||||
* @returns {number} Total earnable points
|
* @returns {number} Total earnable points
|
||||||
*/
|
*/
|
||||||
async getEarnablePoints(): Promise<number> {
|
async getBrowserEarnablePoints(): Promise<number> {
|
||||||
try {
|
try {
|
||||||
const data = await this.getDashboardData()
|
const data = await this.getDashboardData()
|
||||||
|
|
||||||
@@ -166,7 +168,57 @@ export default class BrowserFunc {
|
|||||||
|
|
||||||
return totalEarnablePoints
|
return totalEarnablePoints
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw this.bot.log('GET-EARNABLE-POINTS', 'An error occurred:' + error, 'error')
|
throw this.bot.log('GET-BROWSER-EARNABLE-POINTS', 'An error occurred:' + error, 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get total earnable points with mobile app
|
||||||
|
* @returns {number} Total earnable points
|
||||||
|
*/
|
||||||
|
async getAppEarnablePoints(accessToken: string): Promise<number> {
|
||||||
|
try {
|
||||||
|
const eligibleOffers = [
|
||||||
|
'ENUS_readarticle3_30points',
|
||||||
|
'Gamification_Sapphire_DailyCheckIn'
|
||||||
|
]
|
||||||
|
let totalEarnablePoints = 0
|
||||||
|
|
||||||
|
const data = await this.getDashboardData()
|
||||||
|
let geoLocale = data.userProfile.attributes.country
|
||||||
|
geoLocale = (this.bot.config.searchSettings.useGeoLocaleQueries && geoLocale.length === 2) ? geoLocale.toLowerCase() : 'us'
|
||||||
|
|
||||||
|
const userDataRequest: AxiosRequestConfig = {
|
||||||
|
url: 'https://prod.rewardsplatform.microsoft.com/dapi/me?channel=SAAndroid&options=613',
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${accessToken}`,
|
||||||
|
'X-Rewards-Country': geoLocale,
|
||||||
|
'X-Rewards-Language': 'en'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const userDataResponse: AppUserData = (await axios(userDataRequest)).data
|
||||||
|
const userData = userDataResponse.response
|
||||||
|
const eligibleActivities = userData.promotions.filter((x) => eligibleOffers.includes(x.attributes.offerid ?? ''))
|
||||||
|
|
||||||
|
for (const item of eligibleActivities) {
|
||||||
|
if (item.attributes.type === 'msnreadearn') {
|
||||||
|
totalEarnablePoints += parseInt(item.attributes.pointmax ?? '') - parseInt(item.attributes.pointprogress ?? '')
|
||||||
|
break
|
||||||
|
} else if (item.attributes.type === 'checkin') {
|
||||||
|
const checkInDay = parseInt(item.attributes.progress ?? '') % 7
|
||||||
|
|
||||||
|
if (checkInDay < 6 && (new Date()).getDate() != (new Date(item.attributes.last_updated ?? '')).getDate()) {
|
||||||
|
totalEarnablePoints += parseInt(item.attributes['day_' + (checkInDay + 1) + '_points'] ?? '')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalEarnablePoints
|
||||||
|
} catch (error) {
|
||||||
|
throw this.bot.log('GET-APP-EARNABLE-POINTS', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,9 @@
|
|||||||
"doMorePromotions": true,
|
"doMorePromotions": true,
|
||||||
"doPunchCards": true,
|
"doPunchCards": true,
|
||||||
"doDesktopSearch": true,
|
"doDesktopSearch": true,
|
||||||
"doMobileSearch": true
|
"doMobileSearch": true,
|
||||||
|
"doDailyCheckIn": true,
|
||||||
|
"doReadToEarn": true
|
||||||
},
|
},
|
||||||
"globalTimeout": 30000,
|
"globalTimeout": 30000,
|
||||||
"searchSettings": {
|
"searchSettings": {
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import { Poll } from './activities/Poll'
|
|||||||
import { Quiz } from './activities/Quiz'
|
import { Quiz } from './activities/Quiz'
|
||||||
import { ThisOrThat } from './activities/ThisOrThat'
|
import { ThisOrThat } from './activities/ThisOrThat'
|
||||||
import { UrlReward } from './activities/UrlReward'
|
import { UrlReward } from './activities/UrlReward'
|
||||||
|
import { ReadToEarn } from './activities/ReadToEarn'
|
||||||
|
import { DailyCheckIn } from './activities/DailyCheckIn'
|
||||||
|
|
||||||
import { DashboardData } from '../interface/DashboardData'
|
import { DashboardData } from '../interface/DashboardData'
|
||||||
|
|
||||||
@@ -49,4 +51,14 @@ export default class Activities {
|
|||||||
await urlReward.doUrlReward(page)
|
await urlReward.doUrlReward(page)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
doReadToEarn = async (accessToken: string, data: DashboardData): Promise<void> => {
|
||||||
|
const readToEarn = new ReadToEarn(this.bot)
|
||||||
|
await readToEarn.doReadToEarn(accessToken, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
doDailyCheckIn = async (accessToken: string, data: DashboardData): Promise<void> => {
|
||||||
|
const dailyCheckIn = new DailyCheckIn(this.bot)
|
||||||
|
await dailyCheckIn.doDailyCheckIn(accessToken, data)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,9 @@ import readline from 'readline'
|
|||||||
|
|
||||||
import { MicrosoftRewardsBot } from '../index'
|
import { MicrosoftRewardsBot } from '../index'
|
||||||
import { saveSessionData } from '../util/Load'
|
import { saveSessionData } from '../util/Load'
|
||||||
|
import axios from 'axios'
|
||||||
|
import { OAuth } from '../interface/OAuth'
|
||||||
|
import * as crypto from 'crypto'
|
||||||
|
|
||||||
const rl = readline.createInterface({
|
const rl = readline.createInterface({
|
||||||
input: process.stdin,
|
input: process.stdin,
|
||||||
@@ -12,6 +15,11 @@ const rl = readline.createInterface({
|
|||||||
|
|
||||||
export class Login {
|
export class Login {
|
||||||
private bot: MicrosoftRewardsBot
|
private bot: MicrosoftRewardsBot
|
||||||
|
private clientId: string = '0000000040170455'
|
||||||
|
private authBaseUrl: string = 'https://login.live.com/oauth20_authorize.srf'
|
||||||
|
private redirectUrl: string = 'https://login.live.com/oauth20_desktop.srf'
|
||||||
|
private tokenUrl: string = 'https://login.microsoftonline.com/consumers/oauth2/v2.0/token'
|
||||||
|
private scope: string = 'service::prod.rewardsplatform.microsoft.com::MBI_SSL'
|
||||||
|
|
||||||
constructor(bot: MicrosoftRewardsBot) {
|
constructor(bot: MicrosoftRewardsBot) {
|
||||||
this.bot = bot
|
this.bot = bot
|
||||||
@@ -200,4 +208,50 @@ export class Login {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getMobileAccessToken(page: Page, email: string) {
|
||||||
|
const authorizeUrl = new URL(this.authBaseUrl)
|
||||||
|
|
||||||
|
authorizeUrl.searchParams.append('response_type', 'code')
|
||||||
|
authorizeUrl.searchParams.append('client_id', this.clientId)
|
||||||
|
authorizeUrl.searchParams.append('redirect_uri', this.redirectUrl)
|
||||||
|
authorizeUrl.searchParams.append('scope', this.scope)
|
||||||
|
authorizeUrl.searchParams.append('state', crypto.randomBytes(16).toString('hex'))
|
||||||
|
authorizeUrl.searchParams.append('access_type', 'offline_access')
|
||||||
|
authorizeUrl.searchParams.append('login_hint', email)
|
||||||
|
|
||||||
|
await page.goto(authorizeUrl.href)
|
||||||
|
|
||||||
|
const currentUrl = new URL(page.url())
|
||||||
|
let code: string
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = {
|
||||||
|
url: this.tokenUrl,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
data: body.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenResponse = await axios(tokenRequest)
|
||||||
|
const tokenData: OAuth = await tokenResponse.data
|
||||||
|
|
||||||
|
this.bot.log('LOGIN-APP', 'Successfully authorized')
|
||||||
|
return tokenData.access_token
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,7 @@ export class Workers {
|
|||||||
await page.goto(punchCard.parentPromotion.destinationUrl, { referer: this.bot.config.baseURL })
|
await page.goto(punchCard.parentPromotion.destinationUrl, { referer: this.bot.config.baseURL })
|
||||||
|
|
||||||
// Wait for new page to load, max 10 seconds, however try regardless in case of error
|
// Wait for new page to load, max 10 seconds, however try regardless in case of error
|
||||||
await page.waitForLoadState('networkidle', { timeout: 5_000 }).catch(() => { })
|
await page.waitForLoadState('networkidle', { timeout: 5000 }).catch(() => { })
|
||||||
|
|
||||||
await this.solveActivities(page, activitiesUncompleted, punchCard)
|
await this.solveActivities(page, activitiesUncompleted, punchCard)
|
||||||
|
|
||||||
|
|||||||
48
src/functions/activities/DailyCheckIn.ts
Normal file
48
src/functions/activities/DailyCheckIn.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import { randomBytes } from 'crypto'
|
||||||
|
|
||||||
|
import { Workers } from '../Workers'
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
try {
|
||||||
|
let geoLocale = data.userProfile.attributes.country
|
||||||
|
geoLocale = (this.bot.config.searchSettings.useGeoLocaleQueries && geoLocale.length === 2) ? geoLocale.toLowerCase() : 'us'
|
||||||
|
|
||||||
|
const jsonData = {
|
||||||
|
amount: 1,
|
||||||
|
country: geoLocale,
|
||||||
|
id: randomBytes(64).toString('hex'),
|
||||||
|
type: 101,
|
||||||
|
attributes: {
|
||||||
|
offerid: 'Gamification_Sapphire_DailyCheckIn'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const claimRequest = {
|
||||||
|
url: 'https://prod.rewardsplatform.microsoft.com/dapi/me/activities',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${accessToken}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Rewards-Country': geoLocale,
|
||||||
|
'X-Rewards-Language': 'en'
|
||||||
|
},
|
||||||
|
data: JSON.stringify(jsonData)
|
||||||
|
}
|
||||||
|
|
||||||
|
const claimResponse = await axios(claimRequest)
|
||||||
|
const claimedPoint = parseInt((await claimResponse.data).response.activity.p)
|
||||||
|
|
||||||
|
this.bot.log('DAILY-CHECK-IN', claimedPoint > 0 ? `Claimed ${claimedPoint} points` : 'Already claimed today')
|
||||||
|
} catch (error) {
|
||||||
|
this.bot.log('DAILY-CHECK-IN', 'An error occurred:' + error, 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
73
src/functions/activities/ReadToEarn.ts
Normal file
73
src/functions/activities/ReadToEarn.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import { randomBytes } from 'crypto'
|
||||||
|
|
||||||
|
import { Workers } from '../Workers'
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
let geoLocale = data.userProfile.attributes.country
|
||||||
|
geoLocale = (this.bot.config.searchSettings.useGeoLocaleQueries && geoLocale.length === 2) ? geoLocale.toLowerCase() : 'us'
|
||||||
|
|
||||||
|
const userDataRequest = {
|
||||||
|
url: 'https://prod.rewardsplatform.microsoft.com/dapi/me',
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${accessToken}`,
|
||||||
|
'X-Rewards-Country': geoLocale,
|
||||||
|
'X-Rewards-Language': 'en'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const userDataResponse = await axios(userDataRequest)
|
||||||
|
const userData = (await userDataResponse.data).response
|
||||||
|
let balance: number = userData.balance
|
||||||
|
|
||||||
|
const jsonData = {
|
||||||
|
amount: 1,
|
||||||
|
country: geoLocale,
|
||||||
|
id: '1',
|
||||||
|
type: 101,
|
||||||
|
attributes: {
|
||||||
|
offerid: 'ENUS_readarticle3_30points'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 10; ++i) {
|
||||||
|
jsonData.id = randomBytes(64).toString('hex')
|
||||||
|
const claimRequest = {
|
||||||
|
url: 'https://prod.rewardsplatform.microsoft.com/dapi/me/activities',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${accessToken}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Rewards-Country': geoLocale,
|
||||||
|
'X-Rewards-Language': 'en'
|
||||||
|
},
|
||||||
|
data: JSON.stringify(jsonData)
|
||||||
|
}
|
||||||
|
|
||||||
|
const claimResponse = await axios(claimRequest)
|
||||||
|
const newBalance = (await claimResponse.data).response.balance
|
||||||
|
|
||||||
|
if (newBalance == balance) {
|
||||||
|
this.bot.log('READ-TO-EARN', 'Read all available articles')
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
balance = newBalance
|
||||||
|
this.bot.log('READ-TO-EARN', `Read article ${i + 1}`)
|
||||||
|
await this.bot.utils.wait(Math.floor(this.bot.utils.randomNumber(this.bot.config.searchSettings.searchDelay.min, this.bot.config.searchSettings.searchDelay.max)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bot.log('READ-TO-EARN', 'Completed Read to Earn')
|
||||||
|
} catch (error) {
|
||||||
|
this.bot.log('READ-TO-EARN', 'An error occurred:' + error, 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -133,15 +133,19 @@ export class Search extends Workers {
|
|||||||
private async bingSearch(searchPage: Page, query: string) {
|
private async bingSearch(searchPage: Page, query: string) {
|
||||||
const platformControlKey = platform() === 'darwin' ? 'Meta' : 'Control'
|
const platformControlKey = platform() === 'darwin' ? 'Meta' : 'Control'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Try a max of 5 times
|
// Try a max of 5 times
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// Go to top of the page
|
// Go to top of the page
|
||||||
await searchPage.evaluate(() => {
|
await searchPage.evaluate(() => {
|
||||||
window.scrollTo(0, 0)
|
window.scrollTo(0, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Set it since params get added after visiting
|
||||||
|
this.searchPageURL = searchPage.url()
|
||||||
|
|
||||||
await this.bot.utils.wait(500)
|
await this.bot.utils.wait(500)
|
||||||
|
|
||||||
const searchBar = '#sb_form_q'
|
const searchBar = '#sb_form_q'
|
||||||
@@ -155,14 +159,19 @@ export class Search extends Workers {
|
|||||||
await searchPage.keyboard.type(query)
|
await searchPage.keyboard.type(query)
|
||||||
await searchPage.keyboard.press('Enter')
|
await searchPage.keyboard.press('Enter')
|
||||||
|
|
||||||
|
await this.bot.utils.wait(1000)
|
||||||
|
|
||||||
|
// Bing.com in Chrome opens a new tab when searching
|
||||||
|
const resultPage = await this.bot.browser.utils.getLatestTab(searchPage)
|
||||||
|
|
||||||
if (this.bot.config.searchSettings.scrollRandomResults) {
|
if (this.bot.config.searchSettings.scrollRandomResults) {
|
||||||
await this.bot.utils.wait(2000)
|
await this.bot.utils.wait(2000)
|
||||||
await this.randomScroll(searchPage)
|
await this.randomScroll(resultPage)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.bot.config.searchSettings.clickRandomResults) {
|
if (this.bot.config.searchSettings.clickRandomResults) {
|
||||||
await this.bot.utils.wait(2000)
|
await this.bot.utils.wait(2000)
|
||||||
await this.clickRandomLink(searchPage)
|
await this.clickRandomLink(resultPage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delay between searches
|
// Delay between searches
|
||||||
@@ -181,7 +190,7 @@ export class Search extends Workers {
|
|||||||
|
|
||||||
// Reset the tabs
|
// Reset the tabs
|
||||||
const lastTab = await this.bot.browser.utils.getLatestTab(searchPage)
|
const lastTab = await this.bot.browser.utils.getLatestTab(searchPage)
|
||||||
await this.closeTabs(lastTab, this.searchPageURL)
|
await this.closeTabs(lastTab)
|
||||||
|
|
||||||
await this.bot.utils.wait(4000)
|
await this.bot.utils.wait(4000)
|
||||||
}
|
}
|
||||||
@@ -262,11 +271,13 @@ export class Search extends Workers {
|
|||||||
|
|
||||||
private async randomScroll(page: Page) {
|
private async randomScroll(page: Page) {
|
||||||
try {
|
try {
|
||||||
const scrollAmount = this.bot.utils.randomNumber(5, 5000)
|
const viewportHeight = await page.evaluate(() => window.innerHeight)
|
||||||
|
const totalHeight = await page.evaluate(() => document.body.scrollHeight)
|
||||||
|
const randomScrollPosition = Math.floor(Math.random() * (totalHeight - viewportHeight))
|
||||||
|
|
||||||
await page.evaluate((scrollAmount) => {
|
await page.evaluate((scrollPos) => {
|
||||||
window.scrollBy(0, scrollAmount)
|
window.scrollTo(0, scrollPos)
|
||||||
}, scrollAmount)
|
}, randomScrollPosition)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.bot.log('SEARCH-RANDOM-SCROLL', 'An error occurred:' + error, 'error')
|
this.bot.log('SEARCH-RANDOM-SCROLL', 'An error occurred:' + error, 'error')
|
||||||
@@ -275,23 +286,22 @@ export class Search extends Workers {
|
|||||||
|
|
||||||
private async clickRandomLink(page: Page) {
|
private async clickRandomLink(page: Page) {
|
||||||
try {
|
try {
|
||||||
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
|
await page.click('#b_results .b_algo h2', { timeout: 2000 }).catch(() => { }) // Since we don't really care if it did it or not
|
||||||
|
|
||||||
// Will get current tab if no new one is created
|
// Will get current tab if no new one is created
|
||||||
let lastTab = await this.bot.browser.utils.getLatestTab(page)
|
let lastTab = await this.bot.browser.utils.getLatestTab(page)
|
||||||
|
|
||||||
// Let website load, if it doesn't load within 5 sec. exit regardless
|
// Stay for 10 seconds
|
||||||
await this.bot.utils.wait(5000)
|
await this.bot.utils.wait(10_000)
|
||||||
|
|
||||||
let lastTabURL = new URL(lastTab.url()) // Get new tab info
|
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.
|
// Check if the URL is different from the original one, don't loop more than 5 times.
|
||||||
let i = 0
|
let i = 0
|
||||||
while (lastTabURL.href !== searchListingURL.href && i < 5) {
|
while (lastTabURL.href !== this.searchPageURL && i < 5) {
|
||||||
|
|
||||||
await this.closeTabs(lastTab, searchListingURL.href)
|
await this.closeTabs(lastTab)
|
||||||
|
|
||||||
// End of loop, refresh lastPage
|
// End of loop, refresh lastPage
|
||||||
lastTab = await this.bot.browser.utils.getLatestTab(page) // Finally update the lastTab var again
|
lastTab = await this.bot.browser.utils.getLatestTab(page) // Finally update the lastTab var again
|
||||||
@@ -304,26 +314,25 @@ export class Search extends Workers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async closeTabs(lastTab: Page, url: string) {
|
private async closeTabs(lastTab: Page) {
|
||||||
const browser = lastTab.context()
|
const browser = lastTab.context()
|
||||||
const tabs = browser.pages()
|
const tabs = browser.pages()
|
||||||
|
|
||||||
// If more than 3 tabs are open, close the last tab
|
// If more than 2 tabs are open, close the last tab
|
||||||
if (tabs.length > 2) {
|
if (tabs.length > 2) {
|
||||||
await lastTab.close()
|
await lastTab.close()
|
||||||
|
|
||||||
// If only 1 tab is open, open a new one to search in
|
// If only 1 tab is open, open a new one to search in
|
||||||
} else if (tabs.length === 1) {
|
} else if (tabs.length === 1) {
|
||||||
const newPage = await browser.newPage()
|
const newPage = await browser.newPage()
|
||||||
await newPage.goto(url)
|
await newPage.goto(this.searchPageURL)
|
||||||
|
|
||||||
// Else go back one page
|
// Else go back one page, this means the correct amount is open
|
||||||
} else {
|
} else {
|
||||||
await lastTab.goBack()
|
await lastTab.goBack().catch(() => { })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private calculatePoints(counters: Counters) {
|
private calculatePoints(counters: Counters) {
|
||||||
const mobileData = counters.mobileSearch?.[0] // Mobile searches
|
const mobileData = counters.mobileSearch?.[0] // Mobile searches
|
||||||
const genericData = counters.pcSearch?.[0] // Normal searches
|
const genericData = counters.pcSearch?.[0] // Normal searches
|
||||||
|
|||||||
31
src/index.ts
31
src/index.ts
@@ -34,6 +34,7 @@ export class MicrosoftRewardsBot {
|
|||||||
private accounts: Account[]
|
private accounts: Account[]
|
||||||
private workers: Workers
|
private workers: Workers
|
||||||
private login = new Login(this)
|
private login = new Login(this)
|
||||||
|
private accessToken: string = ''
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.log = log
|
this.log = log
|
||||||
@@ -134,14 +135,19 @@ export class MicrosoftRewardsBot {
|
|||||||
|
|
||||||
// Login into MS Rewards, then go to rewards homepage
|
// Login into MS Rewards, then go to rewards homepage
|
||||||
await this.login.login(this.homePage, account.email, account.password)
|
await this.login.login(this.homePage, account.email, account.password)
|
||||||
|
this.accessToken = await this.login.getMobileAccessToken(this.homePage, account.email)
|
||||||
|
|
||||||
await this.browser.func.goHome(this.homePage)
|
await this.browser.func.goHome(this.homePage)
|
||||||
|
|
||||||
const data = await this.browser.func.getDashboardData()
|
const data = await this.browser.func.getDashboardData()
|
||||||
log('MAIN-POINTS', `Current point count: ${data.userStatus.availablePoints}`)
|
log('MAIN-POINTS', `Current point count: ${data.userStatus.availablePoints}`)
|
||||||
|
|
||||||
const earnablePoints = await this.browser.func.getEarnablePoints()
|
const browserEnarablePoints = await this.browser.func.getBrowserEarnablePoints()
|
||||||
|
const appEarnablePoints = await this.browser.func.getAppEarnablePoints(this.accessToken)
|
||||||
|
|
||||||
|
const earnablePoints = browserEnarablePoints + appEarnablePoints
|
||||||
this.collectedPoints = earnablePoints
|
this.collectedPoints = earnablePoints
|
||||||
log('MAIN-POINTS', `You can earn ${earnablePoints} points today`)
|
log('MAIN-POINTS', `You can earn ${earnablePoints} points today (Browser: ${browserEnarablePoints} points, App: ${appEarnablePoints} points)`)
|
||||||
|
|
||||||
// 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 (!this.config.runOnZeroPoints && this.collectedPoints === 0) {
|
if (!this.config.runOnZeroPoints && this.collectedPoints === 0) {
|
||||||
@@ -200,14 +206,18 @@ export class MicrosoftRewardsBot {
|
|||||||
|
|
||||||
const data = await this.browser.func.getDashboardData()
|
const data = await this.browser.func.getDashboardData()
|
||||||
|
|
||||||
// If no mobile searches data found, stop (Does not exist on new accounts)
|
// Do daily check in
|
||||||
if (!data.userStatus.counters.mobileSearch) {
|
if (this.config.workers.doDailyCheckIn) {
|
||||||
log('MAIN', 'No mobile searches found, stopping!')
|
await this.activities.doDailyCheckIn(this.accessToken, data)
|
||||||
|
|
||||||
// Close mobile browser
|
|
||||||
return await this.closeBrowser(browser, account.email)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do read to earn
|
||||||
|
if (this.config.workers.doReadToEarn) {
|
||||||
|
await this.activities.doReadToEarn(this.accessToken, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no mobile searches data found, stop (Does not exist on new accounts)
|
||||||
|
if (data.userStatus.counters.mobileSearch) {
|
||||||
// Open a new tab to where the tasks are going to be completed
|
// Open a new tab to where the tasks are going to be completed
|
||||||
const workerPage = await browser.newPage()
|
const workerPage = await browser.newPage()
|
||||||
|
|
||||||
@@ -233,9 +243,12 @@ export class MicrosoftRewardsBot {
|
|||||||
await this.Mobile(account)
|
await this.Mobile(account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log('MAIN', 'No mobile searches found!')
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch new points
|
// Fetch new points
|
||||||
const earnablePoints = await this.browser.func.getEarnablePoints()
|
const earnablePoints = await this.browser.func.getBrowserEarnablePoints() + await this.browser.func.getAppEarnablePoints(this.accessToken)
|
||||||
|
|
||||||
// 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)
|
||||||
|
|||||||
226
src/interface/AppUserData.ts
Normal file
226
src/interface/AppUserData.ts
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
export interface AppUserData {
|
||||||
|
response: Response;
|
||||||
|
correlationId: string;
|
||||||
|
code: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Response {
|
||||||
|
profile: Profile;
|
||||||
|
balance: number;
|
||||||
|
counters: null;
|
||||||
|
promotions: Promotion[];
|
||||||
|
catalog: null;
|
||||||
|
goal_item: GoalItem;
|
||||||
|
activities: null;
|
||||||
|
cashback: null;
|
||||||
|
orders: Order[];
|
||||||
|
rebateProfile: null;
|
||||||
|
rebatePayouts: null;
|
||||||
|
giveProfile: GiveProfile;
|
||||||
|
autoRedeemProfile: null;
|
||||||
|
autoRedeemItem: null;
|
||||||
|
thirdPartyProfile: null;
|
||||||
|
notifications: null;
|
||||||
|
waitlist: null;
|
||||||
|
autoOpenFlyout: null;
|
||||||
|
coupons: null;
|
||||||
|
recommendedAffordableCatalog: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GiveProfile {
|
||||||
|
give_user: string;
|
||||||
|
give_organization: { [key: string]: GiveOrganization | null };
|
||||||
|
first_give_optin: string;
|
||||||
|
last_give_optout: string;
|
||||||
|
give_lifetime_balance: string;
|
||||||
|
give_lifetime_donation_balance: string;
|
||||||
|
give_balance: string;
|
||||||
|
form: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GiveOrganization {
|
||||||
|
give_organization_donation_points: number;
|
||||||
|
give_organization_donation_point_to_currency_ratio: number;
|
||||||
|
give_organization_donation_currency: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GoalItem {
|
||||||
|
name: string;
|
||||||
|
provider: string;
|
||||||
|
price: number;
|
||||||
|
attributes: GoalItemAttributes;
|
||||||
|
config: GoalItemConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GoalItemAttributes {
|
||||||
|
category: string;
|
||||||
|
CategoryDescription: string;
|
||||||
|
'desc.group_text': string;
|
||||||
|
'desc.legal_text'?: string;
|
||||||
|
'desc.sc_description': string;
|
||||||
|
'desc.sc_title': string;
|
||||||
|
display_order: string;
|
||||||
|
ExtraLargeImage: string;
|
||||||
|
group: string;
|
||||||
|
group_image: string;
|
||||||
|
group_sc_image: string;
|
||||||
|
group_title: string;
|
||||||
|
hidden?: string;
|
||||||
|
large_image: string;
|
||||||
|
large_sc_image: string;
|
||||||
|
medium_image: string;
|
||||||
|
MobileImage: string;
|
||||||
|
original_price: string;
|
||||||
|
Remarks?: string;
|
||||||
|
ShortText?: string;
|
||||||
|
showcase?: string;
|
||||||
|
small_image: string;
|
||||||
|
title: string;
|
||||||
|
cimsid: string;
|
||||||
|
user_defined_goal?: string;
|
||||||
|
disable_bot_redemptions?: string;
|
||||||
|
'desc.large_text'?: string;
|
||||||
|
english_title?: string;
|
||||||
|
etid?: string;
|
||||||
|
sku?: string;
|
||||||
|
coupon_discount?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GoalItemConfig {
|
||||||
|
amount: string;
|
||||||
|
currencyCode: string;
|
||||||
|
isHidden: string;
|
||||||
|
PointToCurrencyConversionRatio: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Order {
|
||||||
|
id: string;
|
||||||
|
t: Date;
|
||||||
|
sku: string;
|
||||||
|
item_snapshot: ItemSnapshot;
|
||||||
|
p: number;
|
||||||
|
s: S;
|
||||||
|
a: A;
|
||||||
|
child_redemption: null;
|
||||||
|
third_party_partner: null;
|
||||||
|
log: Log[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface A {
|
||||||
|
form?: string;
|
||||||
|
OrderId: string;
|
||||||
|
CorrelationId: string;
|
||||||
|
Channel: string;
|
||||||
|
Language: string;
|
||||||
|
Country: string;
|
||||||
|
EvaluationId: string;
|
||||||
|
provider?: string;
|
||||||
|
referenceOrderID?: string;
|
||||||
|
externalRefID?: string;
|
||||||
|
denomination?: string;
|
||||||
|
rewardName?: string;
|
||||||
|
sendEmail?: string;
|
||||||
|
status?: string;
|
||||||
|
createdAt?: Date;
|
||||||
|
bal_before_deduct?: string;
|
||||||
|
bal_after_deduct?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ItemSnapshot {
|
||||||
|
name: string;
|
||||||
|
provider: string;
|
||||||
|
price: number;
|
||||||
|
attributes: GoalItemAttributes;
|
||||||
|
config: ItemSnapshotConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ItemSnapshotConfig {
|
||||||
|
amount: string;
|
||||||
|
countryCode: string;
|
||||||
|
currencyCode: string;
|
||||||
|
sku: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Log {
|
||||||
|
time: Date;
|
||||||
|
from: From;
|
||||||
|
to: S;
|
||||||
|
reason: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum From {
|
||||||
|
Created = 'Created',
|
||||||
|
RiskApproved = 'RiskApproved',
|
||||||
|
RiskReview = 'RiskReview'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum S {
|
||||||
|
Cancelled = 'Cancelled',
|
||||||
|
RiskApproved = 'RiskApproved',
|
||||||
|
RiskReview = 'RiskReview',
|
||||||
|
Shipped = 'Shipped'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Profile {
|
||||||
|
ruid: string;
|
||||||
|
attributes: ProfileAttributes;
|
||||||
|
offline_attributes: OfflineAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProfileAttributes {
|
||||||
|
publisher: string;
|
||||||
|
publisher_upd: Date;
|
||||||
|
creative: string;
|
||||||
|
creative_upd: Date;
|
||||||
|
program: string;
|
||||||
|
program_upd: Date;
|
||||||
|
country: string;
|
||||||
|
country_upd: Date;
|
||||||
|
referrerhash: string;
|
||||||
|
referrerhash_upd: Date;
|
||||||
|
optout_upd: Date;
|
||||||
|
language: string;
|
||||||
|
language_upd: Date;
|
||||||
|
target: string;
|
||||||
|
target_upd: Date;
|
||||||
|
created: Date;
|
||||||
|
created_upd: Date;
|
||||||
|
epuid: string;
|
||||||
|
epuid_upd: Date;
|
||||||
|
goal: string;
|
||||||
|
goal_upd: Date;
|
||||||
|
waitlistattributes: string;
|
||||||
|
waitlistattributes_upd: Date;
|
||||||
|
serpbotscore_upd: Date;
|
||||||
|
iscashbackeligible: string;
|
||||||
|
cbedc: string;
|
||||||
|
rlscpct_upd: Date;
|
||||||
|
give_user: string;
|
||||||
|
rebcpc_upd: Date;
|
||||||
|
SerpBotScore_upd: Date;
|
||||||
|
AdsBotScore_upd: Date;
|
||||||
|
dbs_upd: Date;
|
||||||
|
rbs: string;
|
||||||
|
rbs_upd: Date;
|
||||||
|
iris_segmentation: string;
|
||||||
|
iris_segmentation_upd: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OfflineAttributes {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Promotion {
|
||||||
|
name: string;
|
||||||
|
priority: number;
|
||||||
|
attributes: { [key: string]: string };
|
||||||
|
tags: Tag[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Tag {
|
||||||
|
AllowTrialUser = 'allow_trial_user',
|
||||||
|
ExcludeGivePcparent = 'exclude_give_pcparent',
|
||||||
|
ExcludeGlobalConfig = 'exclude_global_config',
|
||||||
|
ExcludeHidden = 'exclude_hidden',
|
||||||
|
LOCString = 'locString',
|
||||||
|
NonGlobalConfig = 'non_global_config'
|
||||||
|
}
|
||||||
@@ -35,4 +35,6 @@ export interface Workers {
|
|||||||
doPunchCards: boolean;
|
doPunchCards: boolean;
|
||||||
doDesktopSearch: boolean;
|
doDesktopSearch: boolean;
|
||||||
doMobileSearch: boolean;
|
doMobileSearch: boolean;
|
||||||
|
doDailyCheckIn: boolean;
|
||||||
|
doReadToEarn: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
9
src/interface/OAuth.ts
Normal file
9
src/interface/OAuth.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export interface OAuth {
|
||||||
|
access_token: string;
|
||||||
|
refresh_token: string;
|
||||||
|
scope: string;
|
||||||
|
expires_in: number;
|
||||||
|
ext_expires_in: number;
|
||||||
|
foci: string;
|
||||||
|
token_type: string;
|
||||||
|
}
|
||||||
@@ -1,41 +1,43 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs')
|
||||||
const path = require('path');
|
const path = require('path')
|
||||||
|
|
||||||
const configPath = path.join(__dirname, '../dist/config.json');
|
const configPath = path.join(__dirname, '../dist/config.json')
|
||||||
|
|
||||||
// Read the existing config file
|
// Read the existing config file
|
||||||
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
||||||
|
|
||||||
// Update the config with environment variables if they are set
|
// Update the config with environment variables if they are set
|
||||||
config.baseURL = process.env.BASE_URL || config.baseURL;
|
config.baseURL = process.env.BASE_URL || config.baseURL
|
||||||
config.sessionPath = process.env.SESSION_PATH || config.sessionPath;
|
config.sessionPath = process.env.SESSION_PATH || config.sessionPath
|
||||||
config.headless = process.env.HEADLESS ? process.env.HEADLESS === 'true' : config.headless;
|
config.headless = process.env.HEADLESS ? process.env.HEADLESS === 'true' : config.headless
|
||||||
config.runOnZeroPoints = process.env.RUN_ON_ZERO_POINTS ? process.env.RUN_ON_ZERO_POINTS === 'true' : config.runOnZeroPoints;
|
config.runOnZeroPoints = process.env.RUN_ON_ZERO_POINTS ? process.env.RUN_ON_ZERO_POINTS === 'true' : config.runOnZeroPoints
|
||||||
config.clusters = process.env.CLUSTERS ? parseInt(process.env.CLUSTERS, 10) : config.clusters;
|
config.clusters = process.env.CLUSTERS ? parseInt(process.env.CLUSTERS, 10) : config.clusters
|
||||||
config.saveFingerprint = process.env.SAVE_FINGERPRINT ? process.env.SAVE_FINGERPRINT === 'true' : config.saveFingerprint;
|
config.saveFingerprint = process.env.SAVE_FINGERPRINT ? process.env.SAVE_FINGERPRINT === 'true' : config.saveFingerprint
|
||||||
config.globalTimeout = process.env.GLOBAL_TIMEOUT ? parseInt(process.env.GLOBAL_TIMEOUT, 10) : config.globalTimeout;
|
config.globalTimeout = process.env.GLOBAL_TIMEOUT ? parseInt(process.env.GLOBAL_TIMEOUT, 10) : config.globalTimeout
|
||||||
|
|
||||||
config.workers.doDailySet = process.env.DO_DAILY_SET ? process.env.DO_DAILY_SET === 'true' : config.workers.doDailySet;
|
config.workers.doDailySet = process.env.DO_DAILY_SET ? process.env.DO_DAILY_SET === 'true' : config.workers.doDailySet
|
||||||
config.workers.doMorePromotions = process.env.DO_MORE_PROMOTIONS ? process.env.DO_MORE_PROMOTIONS === 'true' : config.workers.doMorePromotions;
|
config.workers.doMorePromotions = process.env.DO_MORE_PROMOTIONS ? process.env.DO_MORE_PROMOTIONS === 'true' : config.workers.doMorePromotions
|
||||||
config.workers.doPunchCards = process.env.DO_PUNCH_CARDS ? process.env.DO_PUNCH_CARDS === 'true' : config.workers.doPunchCards;
|
config.workers.doPunchCards = process.env.DO_PUNCH_CARDS ? process.env.DO_PUNCH_CARDS === 'true' : config.workers.doPunchCards
|
||||||
config.workers.doDesktopSearch = process.env.DO_DESKTOP_SEARCH ? process.env.DO_DESKTOP_SEARCH === 'true' : config.workers.doDesktopSearch;
|
config.workers.doDesktopSearch = process.env.DO_DESKTOP_SEARCH ? process.env.DO_DESKTOP_SEARCH === 'true' : config.workers.doDesktopSearch
|
||||||
config.workers.doMobileSearch = process.env.DO_MOBILE_SEARCH ? process.env.DO_MOBILE_SEARCH === 'true' : config.workers.doMobileSearch;
|
config.workers.doMobileSearch = process.env.DO_MOBILE_SEARCH ? process.env.DO_MOBILE_SEARCH === 'true' : config.workers.doMobileSearch
|
||||||
|
config.workers.doDailyCheckIn = process.env.DO_DAILY_CHECK_IN ? process.env.DO_DAILY_CHECK_IN === 'true' : config.workers.doDailyCheckIn
|
||||||
|
config.workers.doReadToEarn = process.env.DO_READ_TO_EARN ? process.env.DO_READ_TO_EARN === 'true' : config.workers.doReadToEarn
|
||||||
|
|
||||||
config.searchSettings.useGeoLocaleQueries = process.env.USE_GEO_LOCALE_QUERIES ? process.env.USE_GEO_LOCALE_QUERIES === 'true' : config.searchSettings.useGeoLocaleQueries;
|
config.searchSettings.useGeoLocaleQueries = process.env.USE_GEO_LOCALE_QUERIES ? process.env.USE_GEO_LOCALE_QUERIES === 'true' : config.searchSettings.useGeoLocaleQueries
|
||||||
config.searchSettings.scrollRandomResults = process.env.SCROLL_RANDOM_RESULTS ? process.env.SCROLL_RANDOM_RESULTS === 'true' : config.searchSettings.scrollRandomResults;
|
config.searchSettings.scrollRandomResults = process.env.SCROLL_RANDOM_RESULTS ? process.env.SCROLL_RANDOM_RESULTS === 'true' : config.searchSettings.scrollRandomResults
|
||||||
config.searchSettings.clickRandomResults = process.env.CLICK_RANDOM_RESULTS ? process.env.CLICK_RANDOM_RESULTS === 'true' : config.searchSettings.clickRandomResults;
|
config.searchSettings.clickRandomResults = process.env.CLICK_RANDOM_RESULTS ? process.env.CLICK_RANDOM_RESULTS === 'true' : config.searchSettings.clickRandomResults
|
||||||
config.searchSettings.searchDelay.min = process.env.SEARCH_DELAY_MIN ? parseInt(process.env.SEARCH_DELAY_MIN, 10) : config.searchSettings.searchDelay.min;
|
config.searchSettings.searchDelay.min = process.env.SEARCH_DELAY_MIN ? parseInt(process.env.SEARCH_DELAY_MIN, 10) : config.searchSettings.searchDelay.min
|
||||||
config.searchSettings.searchDelay.max = process.env.SEARCH_DELAY_MAX ? parseInt(process.env.SEARCH_DELAY_MAX, 10) : config.searchSettings.searchDelay.max;
|
config.searchSettings.searchDelay.max = process.env.SEARCH_DELAY_MAX ? parseInt(process.env.SEARCH_DELAY_MAX, 10) : config.searchSettings.searchDelay.max
|
||||||
config.searchSettings.retryMobileSearch = process.env.RETRY_MOBILE_SEARCH ? process.env.RETRY_MOBILE_SEARCH === 'true' : config.searchSettings.retryMobileSearch;
|
config.searchSettings.retryMobileSearch = process.env.RETRY_MOBILE_SEARCH ? process.env.RETRY_MOBILE_SEARCH === 'true' : config.searchSettings.retryMobileSearch
|
||||||
|
|
||||||
config.webhook.enabled = process.env.WEBHOOK_ENABLED ? process.env.WEBHOOK_ENABLED === 'true' : config.webhook.enabled;
|
config.webhook.enabled = process.env.WEBHOOK_ENABLED ? process.env.WEBHOOK_ENABLED === 'true' : config.webhook.enabled
|
||||||
config.webhook.url = process.env.WEBHOOK_URL || config.webhook.url;
|
config.webhook.url = process.env.WEBHOOK_URL || config.webhook.url
|
||||||
|
|
||||||
// Write the updated config back to the file
|
// Write the updated config back to the file
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2))
|
||||||
console.log('Config file updated with environment variables');
|
console.log('Config file updated with environment variables')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to write updated config file to ${configPath}:`, error);
|
console.error(`Failed to write updated config file to ${configPath}:`, error)
|
||||||
process.exit(1);
|
process.exit(1)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user