mirror of
https://github.com/TheNetsky/Microsoft-Rewards-Script.git
synced 2026-01-18 05:53:57 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5fd06d229 | ||
|
|
0ddc964878 | ||
|
|
325bf65b30 | ||
|
|
6f19bd4b0e | ||
|
|
caf6a42a38 | ||
|
|
352d47229b | ||
|
|
9a12ee1ec8 | ||
|
|
b630c3ddda | ||
|
|
287e3897da | ||
|
|
fcf6aba446 | ||
|
|
1102f2ca94 | ||
|
|
82a896e83f | ||
|
|
b0bd1f52c4 | ||
|
|
7e4121e01b |
@@ -37,6 +37,7 @@ RUN touch /var/log/cron.log
|
|||||||
|
|
||||||
# Define the command to run your application with cron optionally
|
# Define the command to run your application with cron optionally
|
||||||
CMD ["sh", "-c", "echo \"$TZ\" > /etc/timezone && \
|
CMD ["sh", "-c", "echo \"$TZ\" > /etc/timezone && \
|
||||||
|
ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
|
||||||
dpkg-reconfigure -f noninteractive tzdata && \
|
dpkg-reconfigure -f noninteractive tzdata && \
|
||||||
envsubst < /etc/cron.d/microsoft-rewards-cron.template > /etc/cron.d/microsoft-rewards-cron && \
|
envsubst < /etc/cron.d/microsoft-rewards-cron.template > /etc/cron.d/microsoft-rewards-cron && \
|
||||||
chmod 0644 /etc/cron.d/microsoft-rewards-cron && \
|
chmod 0644 /etc/cron.d/microsoft-rewards-cron && \
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -57,9 +57,11 @@ A basic docker `compose.yaml` is provided. Follow these steps to configure and r
|
|||||||
| baseURL | MS Rewards page | `https://rewards.bing.com` |
|
| baseURL | MS Rewards page | `https://rewards.bing.com` |
|
||||||
| sessionPath | Path to where you want sessions/fingerprints to be stored | `sessions` (In ./browser/sessions) |
|
| sessionPath | Path to where you want sessions/fingerprints to be stored | `sessions` (In ./browser/sessions) |
|
||||||
| headless | If the browser window should be visible be ran in the background | `false` (Browser is visible) |
|
| headless | If the browser window should be visible be ran in the background | `false` (Browser is visible) |
|
||||||
|
| parallel | If you want mobile and desktop tasks to run parallel or sequential| `true` |
|
||||||
| runOnZeroPoints | Run the rest of the script if 0 points can be earned | `false` (Will not run on 0 points) |
|
| runOnZeroPoints | Run the rest of the script if 0 points can be earned | `false` (Will not run on 0 points) |
|
||||||
| clusters | Amount of instances ran on launch, 1 per account | `1` (Will run 1 account at the time) |
|
| clusters | Amount of instances ran on launch, 1 per account | `1` (Will run 1 account at the time) |
|
||||||
| saveFingerprint | Re-use the same fingerprint each time | `false` (Will generate a new fingerprint each time) |
|
| saveFingerprint.mobile | Re-use the same fingerprint each time | `false` (Will generate a new fingerprint each time) |
|
||||||
|
| saveFingerprint.desktop | Re-use the same fingerprint each time | `false` (Will generate a new fingerprint each time) |
|
||||||
| workers.doDailySet | Complete daily set items | `true` |
|
| workers.doDailySet | Complete daily set items | `true` |
|
||||||
| workers.doMorePromotions | Complete promotional items | `true` |
|
| workers.doMorePromotions | Complete promotional items | `true` |
|
||||||
| workers.doPunchCards | Complete punchcards | `true` |
|
| workers.doPunchCards | Complete punchcards | `true` |
|
||||||
@@ -67,17 +69,19 @@ A basic docker `compose.yaml` is provided. Follow these steps to configure and r
|
|||||||
| workers.doMobileSearch | Complete daily mobile searches | `true` |
|
| workers.doMobileSearch | Complete daily mobile searches | `true` |
|
||||||
| workers.doDailyCheckIn | Complete daily check-in activity | `true` |
|
| workers.doDailyCheckIn | Complete daily check-in activity | `true` |
|
||||||
| workers.doReadToEarn | Complete read to earn activity | `true` |
|
| workers.doReadToEarn | Complete read to earn activity | `true` |
|
||||||
|
| searchOnBingLocalQueries | Complete the activity "search on Bing" using the `queries.json` or fetched from this repo | `false` (Will fetch from this repo) |
|
||||||
| globalTimeout | The length before the action gets timeout | `30s` |
|
| globalTimeout | The length before the action gets timeout | `30s` |
|
||||||
| searchSettings.useGeoLocaleQueries | Generate search queries based on your geo-location | `true` (Uses EN-US generated queries) |
|
| searchSettings.useGeoLocaleQueries | Generate search queries based on your geo-location | `false` (Uses EN-US generated queries) |
|
||||||
| scrollRandomResults | Scroll randomly in search results | `true` |
|
| searchSettings.scrollRandomResults | Scroll randomly in search results | `true` |
|
||||||
| searchSettings.clickRandomResults | Visit random website from search result| `true` |
|
| searchSettings.clickRandomResults | Visit random website from search result| `true` |
|
||||||
| searchSettings.searchDelay | Minimum and maximum time in miliseconds between search queries | `min: 1min` `max: 2min` |
|
| searchSettings.searchDelay | Minimum and maximum time in milliseconds between search queries | `min: 3min` `max: 5min` |
|
||||||
| searchSettings.retryMobileSearchAmount | Keep retrying mobile searches for specified amount | `3` |
|
| searchSettings.retryMobileSearchAmount | Keep retrying mobile searches for specified amount | `2` |
|
||||||
| logExcludeFunc | Functions to exclude out of the logs and webhooks | `SEARCH-CLOSE-TABS` |
|
| logExcludeFunc | Functions to exclude out of the logs and webhooks | `SEARCH-CLOSE-TABS` |
|
||||||
|
| webhookLogExcludeFunc | Functions to exclude out of the webhooks log | `SEARCH-CLOSE-TABS` |
|
||||||
|
| proxy.proxyGoogleTrends | Enable or disable proxying the request via set proxy | `true` (will be proxied) |
|
||||||
|
| proxy.proxyBingTerms | Enable or disable proxying the request via set proxy | `true` (will be proxied) |
|
||||||
| webhook.enabled | Enable or disable your set webhook | `false` |
|
| webhook.enabled | Enable or disable your set webhook | `false` |
|
||||||
| webhook.url | Your Discord webhook URL | `null` |
|
| webhook.url | Your Discord webhook URL | `null` |
|
||||||
| cronStartTime | Scheduled script run-time, *only available for docker implementation* | `0 5,11 * * *` (5:00 am, 11:00 am daily) |
|
|
||||||
| | Run the script immediately when the Docker container starts | `true` |
|
|
||||||
|
|
||||||
## Features ##
|
## Features ##
|
||||||
- [x] Multi-Account Support
|
- [x] Multi-Account Support
|
||||||
@@ -112,4 +116,4 @@ A basic docker `compose.yaml` is provided. Follow these steps to configure and r
|
|||||||
## Disclaimer ##
|
## Disclaimer ##
|
||||||
Your account may be at risk of getting banned or suspended using this script, you've been warned!
|
Your account may be at risk of getting banned or suspended using this script, you've been warned!
|
||||||
<br />
|
<br />
|
||||||
Use this script at your own risk!
|
Use this script at your own risk!
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "microsoft-rewards-script",
|
"name": "microsoft-rewards-script",
|
||||||
"version": "1.5.0",
|
"version": "1.5.3",
|
||||||
"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": {
|
||||||
@@ -35,11 +35,11 @@
|
|||||||
"typescript": "^5.5.4"
|
"typescript": "^5.5.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.8.4",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"cheerio": "^1.0.0",
|
"cheerio": "^1.0.0",
|
||||||
"fingerprint-generator": "^2.1.62",
|
"fingerprint-generator": "^2.1.66",
|
||||||
"fingerprint-injector": "^2.1.62",
|
"fingerprint-injector": "^2.1.66",
|
||||||
"http-proxy-agent": "^7.0.2",
|
"http-proxy-agent": "^7.0.2",
|
||||||
"https-proxy-agent": "^7.0.6",
|
"https-proxy-agent": "^7.0.6",
|
||||||
"ms": "^2.1.3",
|
"ms": "^2.1.3",
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class Browser {
|
|||||||
const context = await newInjectedContext(browser as any, { fingerprint: fingerprint })
|
const context = await newInjectedContext(browser as any, { fingerprint: fingerprint })
|
||||||
|
|
||||||
// Set timeout to preferred amount
|
// Set timeout to preferred amount
|
||||||
context.setDefaultTimeout(this.bot.utils.stringToMs(this.bot.config?.globalTimeout ?? 30_000))
|
context.setDefaultTimeout(this.bot.utils.stringToMs(this.bot.config?.globalTimeout ?? 30000))
|
||||||
|
|
||||||
await context.addCookies(sessionData.cookies)
|
await context.addCookies(sessionData.cookies)
|
||||||
|
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ export default class BrowserFunc {
|
|||||||
|
|
||||||
async waitForQuizRefresh(page: Page): Promise<boolean> {
|
async waitForQuizRefresh(page: Page): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await page.waitForSelector('span.rqMCredits', { state: 'visible', timeout: 10_000 })
|
await page.waitForSelector('span.rqMCredits', { state: 'visible', timeout: 10000 })
|
||||||
await this.bot.utils.wait(2000)
|
await this.bot.utils.wait(2000)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ export default class BrowserUtil {
|
|||||||
this.bot = bot
|
this.bot = bot
|
||||||
}
|
}
|
||||||
|
|
||||||
async tryDismissAllMessages(page: Page): Promise<boolean> {
|
async tryDismissAllMessages(page: Page): Promise<void> {
|
||||||
const buttons = [
|
const buttons = [
|
||||||
|
{ selector: 'button[type="submit"]', label: 'Submit Button' },
|
||||||
{ selector: '#acceptButton', label: 'AcceptButton' },
|
{ selector: '#acceptButton', label: 'AcceptButton' },
|
||||||
{ selector: '.ext-secondary.ext-button', label: '"Skip for now" Button' },
|
{ selector: '.ext-secondary.ext-button', label: '"Skip for now" Button' },
|
||||||
{ selector: '#iLandingViewAction', label: 'iLandingViewAction' },
|
{ selector: '#iLandingViewAction', label: 'iLandingViewAction' },
|
||||||
@@ -23,27 +24,23 @@ export default class BrowserUtil {
|
|||||||
{ selector: '.ms-Button.ms-Button--primary', label: 'Primary Button' },
|
{ selector: '.ms-Button.ms-Button--primary', label: 'Primary Button' },
|
||||||
{ selector: '.c-glyph.glyph-cancel', label: 'Mobile Welcome Button' },
|
{ selector: '.c-glyph.glyph-cancel', label: 'Mobile Welcome Button' },
|
||||||
{ selector: '.maybe-later', label: 'Mobile Rewards App Banner' },
|
{ selector: '.maybe-later', label: 'Mobile Rewards App Banner' },
|
||||||
{ selector: '//div[@id="cookieConsentContainer"]//button[contains(text(), "Accept")]', label: 'Accept Cookie Consent Container' },
|
{ selector: '//div[@id="cookieConsentContainer"]//button[contains(text(), "Accept")]', label: 'Accept Cookie Consent Container', isXPath: true },
|
||||||
{ selector: '#bnp_btn_accept', label: 'Bing Cookie Banner' },
|
{ selector: '#bnp_btn_accept', label: 'Bing Cookie Banner' },
|
||||||
{ selector: '#reward_pivot_earn', label: 'Reward Coupon Accept' }
|
{ selector: '#reward_pivot_earn', label: 'Reward Coupon Accept' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const dismissTasks = buttons.map(async (button) => {
|
for (const button of buttons) {
|
||||||
try {
|
try {
|
||||||
const element = page.locator(button.selector)
|
const element = button.isXPath ? page.locator(`xpath=${button.selector}`) : page.locator(button.selector)
|
||||||
if (await element.first().isVisible({ timeout: 1000 })) {
|
await element.first().click({ timeout: 500 })
|
||||||
await element.first().click({ timeout: 1000 })
|
await page.waitForTimeout(500)
|
||||||
this.bot.log(this.bot.isMobile, 'DISMISS-ALL-MESSAGES', `Dismissed: ${button.label}`)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Ignore errors and continue
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
const results = await Promise.allSettled(dismissTasks)
|
this.bot.log(this.bot.isMobile, 'DISMISS-ALL-MESSAGES', `Dismissed: ${button.label}`)
|
||||||
return results.some(result => result.status === 'fulfilled' && result.value === true)
|
|
||||||
|
} catch (error) {
|
||||||
|
// Silent fail
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLatestTab(page: Page): Promise<Page> {
|
async getLatestTab(page: Page): Promise<Page> {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"baseURL": "https://rewards.bing.com",
|
"baseURL": "https://rewards.bing.com",
|
||||||
"sessionPath": "sessions",
|
"sessionPath": "sessions",
|
||||||
"headless": true,
|
"headless": false,
|
||||||
"parallel": true,
|
"parallel": true,
|
||||||
"runOnZeroPoints": false,
|
"runOnZeroPoints": false,
|
||||||
"clusters": 1,
|
"clusters": 1,
|
||||||
@@ -33,6 +33,13 @@
|
|||||||
"logExcludeFunc": [
|
"logExcludeFunc": [
|
||||||
"SEARCH-CLOSE-TABS"
|
"SEARCH-CLOSE-TABS"
|
||||||
],
|
],
|
||||||
|
"webhookLogExcludeFunc": [
|
||||||
|
"SEARCH-CLOSE-TABS"
|
||||||
|
],
|
||||||
|
"proxy": {
|
||||||
|
"proxyGoogleTrends": true,
|
||||||
|
"proxyBingTerms": true
|
||||||
|
},
|
||||||
"webhook": {
|
"webhook": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"url": ""
|
"url": ""
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export class Login {
|
|||||||
// Check if account is locked
|
// Check if account is locked
|
||||||
await this.checkAccountLocked(page)
|
await this.checkAccountLocked(page)
|
||||||
|
|
||||||
const isLoggedIn = await page.waitForSelector('html[data-role-name="RewardsPortal"]', { timeout: 10_000 }).then(() => true).catch(() => false)
|
const isLoggedIn = await page.waitForSelector('html[data-role-name="RewardsPortal"]', { timeout: 10000 }).then(() => true).catch(() => false)
|
||||||
|
|
||||||
if (!isLoggedIn) {
|
if (!isLoggedIn) {
|
||||||
await this.execLogin(page, email, password)
|
await this.execLogin(page, email, password)
|
||||||
@@ -86,30 +86,80 @@ export class Login {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async enterEmail(page: Page, email: string) {
|
private async enterEmail(page: Page, email: string) {
|
||||||
const emailPrefilled = await page.waitForSelector('#userDisplayName', { timeout: 2_000 }).catch(() => null)
|
const emailInputSelector = 'input[type="email"]'
|
||||||
if (emailPrefilled) {
|
|
||||||
this.bot.log(this.bot.isMobile, 'LOGIN', 'Email already prefilled by Microsoft')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await page.fill('#i0116', email)
|
try {
|
||||||
await page.click('#idSIButton9')
|
// Wait for email field
|
||||||
this.bot.log(this.bot.isMobile, 'LOGIN', 'Email entered successfully')
|
const emailField = await page.waitForSelector(emailInputSelector, { state: 'visible', timeout: 2000 }).catch(() => null)
|
||||||
|
if (!emailField) {
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Email field not found', 'warn')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.bot.utils.wait(1000)
|
||||||
|
|
||||||
|
// Check if email is prefilled
|
||||||
|
const emailPrefilled = await page.waitForSelector('#userDisplayName', { timeout: 5000 }).catch(() => null)
|
||||||
|
if (emailPrefilled) {
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Email already prefilled by Microsoft')
|
||||||
|
} else {
|
||||||
|
// Else clear and fill email
|
||||||
|
await page.fill(emailInputSelector, '')
|
||||||
|
await this.bot.utils.wait(500)
|
||||||
|
await page.fill(emailInputSelector, email)
|
||||||
|
await this.bot.utils.wait(1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextButton = await page.waitForSelector('button[type="submit"]', { timeout: 2000 }).catch(() => null)
|
||||||
|
if (nextButton) {
|
||||||
|
await nextButton.click()
|
||||||
|
await this.bot.utils.wait(2000)
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Email entered successfully')
|
||||||
|
} else {
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Next button not found after email entry', 'warn')
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', `Email entry failed: ${error}`, 'error')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async enterPassword(page: Page, password: string) {
|
private async enterPassword(page: Page, password: string) {
|
||||||
|
const passwordInputSelector = 'input[type="password"]'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await page.waitForSelector('#i0118', { state: 'visible', timeout: 2000 })
|
// Wait for password field
|
||||||
await this.bot.utils.wait(2000)
|
const passwordField = await page.waitForSelector(passwordInputSelector, { state: 'visible', timeout: 5000 }).catch(() => null)
|
||||||
await page.fill('#i0118', password)
|
if (!passwordField) {
|
||||||
await page.click('#idSIButton9')
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Password field not found, possibly 2FA required', 'warn')
|
||||||
this.bot.log(this.bot.isMobile, 'LOGIN', 'Password entered successfully')
|
await this.handle2FA(page)
|
||||||
} catch {
|
return
|
||||||
this.bot.log(this.bot.isMobile, 'LOGIN', 'Password entry failed or 2FA required')
|
}
|
||||||
|
|
||||||
|
await this.bot.utils.wait(1000)
|
||||||
|
|
||||||
|
// Clear and fill password
|
||||||
|
await page.fill(passwordInputSelector, '')
|
||||||
|
await this.bot.utils.wait(500)
|
||||||
|
await page.fill(passwordInputSelector, password)
|
||||||
|
await this.bot.utils.wait(1000)
|
||||||
|
|
||||||
|
const nextButton = await page.waitForSelector('button[type="submit"]', { timeout: 2000 }).catch(() => null)
|
||||||
|
if (nextButton) {
|
||||||
|
await nextButton.click()
|
||||||
|
await this.bot.utils.wait(2000)
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Password entered successfully')
|
||||||
|
} else {
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Next button not found after password entry', 'warn')
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.bot.log(this.bot.isMobile, 'LOGIN', `Password entry failed: ${error}`, 'error')
|
||||||
await this.handle2FA(page)
|
await this.handle2FA(page)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async handle2FA(page: Page) {
|
private async handle2FA(page: Page) {
|
||||||
try {
|
try {
|
||||||
const numberToPress = await this.get2FACode(page)
|
const numberToPress = await this.get2FACode(page)
|
||||||
@@ -138,7 +188,7 @@ export class Login {
|
|||||||
while (true) {
|
while (true) {
|
||||||
const button = await page.waitForSelector('button[aria-describedby="pushNotificationsTitle errorDescription"]', { state: 'visible', timeout: 2000 }).catch(() => null)
|
const button = await page.waitForSelector('button[aria-describedby="pushNotificationsTitle errorDescription"]', { state: 'visible', timeout: 2000 }).catch(() => null)
|
||||||
if (button) {
|
if (button) {
|
||||||
await this.bot.utils.wait(60_000)
|
await this.bot.utils.wait(60000)
|
||||||
await button.click()
|
await button.click()
|
||||||
|
|
||||||
continue
|
continue
|
||||||
@@ -148,7 +198,7 @@ export class Login {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.click('button[aria-describedby="confirmSendTitle"]').catch(() => {})
|
await page.click('button[aria-describedby="confirmSendTitle"]').catch(() => { })
|
||||||
await this.bot.utils.wait(2000)
|
await this.bot.utils.wait(2000)
|
||||||
const element = await page.waitForSelector('#displaySign', { state: 'visible', timeout: 2000 })
|
const element = await page.waitForSelector('#displaySign', { state: 'visible', timeout: 2000 })
|
||||||
return await element.textContent()
|
return await element.textContent()
|
||||||
@@ -162,7 +212,7 @@ export class Login {
|
|||||||
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', `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')
|
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: 60_000 })
|
await page.waitForSelector('#i0281', { state: 'detached', timeout: 60000 })
|
||||||
|
|
||||||
this.bot.log(this.bot.isMobile, 'LOGIN', 'Login successfully approved!')
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Login successfully approved!')
|
||||||
break
|
break
|
||||||
@@ -203,7 +253,7 @@ export class Login {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wait for login to complete
|
// Wait for login to complete
|
||||||
await page.waitForSelector('html[data-role-name="RewardsPortal"]', { timeout: 10_000 })
|
await page.waitForSelector('html[data-role-name="RewardsPortal"]', { timeout: 10000 })
|
||||||
this.bot.log(this.bot.isMobile, 'LOGIN', 'Successfully logged into the rewards portal')
|
this.bot.log(this.bot.isMobile, 'LOGIN', 'Successfully logged into the rewards portal')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,7 +322,7 @@ export class Login {
|
|||||||
currentUrl = new URL(page.url())
|
currentUrl = new URL(page.url())
|
||||||
await this.bot.utils.wait(5000)
|
await this.bot.utils.wait(5000)
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = new URLSearchParams()
|
const body = new URLSearchParams()
|
||||||
body.append('grant_type', 'authorization_code')
|
body.append('grant_type', 'authorization_code')
|
||||||
body.append('client_id', this.clientId)
|
body.append('client_id', this.clientId)
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export class Workers {
|
|||||||
Due to common false timeout on this function, we're ignoring the error regardless, if it worked then it's faster,
|
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.
|
if it didn't then it gave enough time for the page to load.
|
||||||
*/
|
*/
|
||||||
await activityPage.waitForLoadState('networkidle', { timeout: 10_000 }).catch(() => { })
|
await activityPage.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => { })
|
||||||
await this.bot.utils.wait(2000)
|
await this.bot.utils.wait(2000)
|
||||||
|
|
||||||
switch (activity.promotionType) {
|
switch (activity.promotionType) {
|
||||||
|
|||||||
@@ -15,18 +15,18 @@ export class ABC extends Workers {
|
|||||||
const maxIterations = 15
|
const maxIterations = 15
|
||||||
let i
|
let i
|
||||||
for (i = 0; i < maxIterations && !$('span.rw_icon').length; i++) {
|
for (i = 0; i < maxIterations && !$('span.rw_icon').length; i++) {
|
||||||
await page.waitForSelector('.wk_OptionClickClass', { state: 'visible', timeout: 10_000 })
|
await page.waitForSelector('.wk_OptionClickClass', { state: 'visible', timeout: 10000 })
|
||||||
|
|
||||||
const answers = $('.wk_OptionClickClass')
|
const answers = $('.wk_OptionClickClass')
|
||||||
const answer = answers[this.bot.utils.randomNumber(0, 2)]?.attribs['id']
|
const answer = answers[this.bot.utils.randomNumber(0, 2)]?.attribs['id']
|
||||||
|
|
||||||
await page.waitForSelector(`#${answer}`, { state: 'visible', timeout: 10_000 })
|
await page.waitForSelector(`#${answer}`, { state: 'visible', timeout: 10000 })
|
||||||
|
|
||||||
await this.bot.utils.wait(2000)
|
await this.bot.utils.wait(2000)
|
||||||
await page.click(`#${answer}`) // Click answer
|
await page.click(`#${answer}`) // Click answer
|
||||||
|
|
||||||
await this.bot.utils.wait(4000)
|
await this.bot.utils.wait(4000)
|
||||||
await page.waitForSelector('div.wk_button', { state: 'visible', timeout: 10_000 })
|
await page.waitForSelector('div.wk_button', { state: 'visible', timeout: 10000 })
|
||||||
await page.click('div.wk_button') // Click next question button
|
await page.click('div.wk_button') // Click next question button
|
||||||
|
|
||||||
page = await this.bot.browser.utils.getLatestTab(page)
|
page = await this.bot.browser.utils.getLatestTab(page)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class Poll extends Workers {
|
|||||||
try {
|
try {
|
||||||
const buttonId = `#btoption${Math.floor(this.bot.utils.randomNumber(0, 1))}`
|
const buttonId = `#btoption${Math.floor(this.bot.utils.randomNumber(0, 1))}`
|
||||||
|
|
||||||
await page.waitForSelector(buttonId, { state: 'visible', timeout: 10_000 }).catch(() => { }) // We're gonna click regardless or not
|
await page.waitForSelector(buttonId, { state: 'visible', timeout: 10000 }).catch(() => { }) // We're gonna click regardless or not
|
||||||
await this.bot.utils.wait(2000)
|
await this.bot.utils.wait(2000)
|
||||||
|
|
||||||
await page.click(buttonId)
|
await page.click(buttonId)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export class Quiz extends Workers {
|
|||||||
const answers: string[] = []
|
const answers: string[] = []
|
||||||
|
|
||||||
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
||||||
const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { state: 'visible', timeout: 10_000 })
|
const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { state: 'visible', timeout: 10000 })
|
||||||
const answerAttribute = await answerSelector?.evaluate(el => el.getAttribute('iscorrectoption'))
|
const answerAttribute = await answerSelector?.evaluate(el => el.getAttribute('iscorrectoption'))
|
||||||
|
|
||||||
if (answerAttribute && answerAttribute.toLowerCase() === 'true') {
|
if (answerAttribute && answerAttribute.toLowerCase() === 'true') {
|
||||||
@@ -59,7 +59,7 @@ export class Quiz extends Workers {
|
|||||||
|
|
||||||
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
for (let i = 0; i < quizData.numberOfOptions; i++) {
|
||||||
|
|
||||||
const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { state: 'visible', timeout: 10_000 })
|
const answerSelector = await page.waitForSelector(`#rqAnswerOption${i}`, { state: 'visible', timeout: 10000 })
|
||||||
const dataOption = await answerSelector?.evaluate(el => el.getAttribute('data-option'))
|
const dataOption = await answerSelector?.evaluate(el => el.getAttribute('data-option'))
|
||||||
|
|
||||||
if (dataOption === correctOption) {
|
if (dataOption === correctOption) {
|
||||||
|
|||||||
@@ -4,9 +4,17 @@ import { platform } from 'os'
|
|||||||
import { Workers } from '../Workers'
|
import { Workers } from '../Workers'
|
||||||
|
|
||||||
import { Counters, DashboardData } from '../../interface/DashboardData'
|
import { Counters, DashboardData } from '../../interface/DashboardData'
|
||||||
import { GoogleTrends } from '../../interface/GoogleDailyTrends'
|
|
||||||
import { GoogleSearch } from '../../interface/Search'
|
import { GoogleSearch } from '../../interface/Search'
|
||||||
|
import { AxiosRequestConfig } from 'axios'
|
||||||
|
|
||||||
|
type GoogleTrendsResponse = [
|
||||||
|
string,
|
||||||
|
[
|
||||||
|
string,
|
||||||
|
...null[],
|
||||||
|
[string, ...string[]]
|
||||||
|
][]
|
||||||
|
];
|
||||||
|
|
||||||
export class Search extends Workers {
|
export class Search extends Workers {
|
||||||
private bingHome = 'https://bing.com'
|
private bingHome = 'https://bing.com'
|
||||||
@@ -26,7 +34,7 @@ export class Search extends Workers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate search queries
|
// Generate search queries
|
||||||
let googleSearchQueries = await this.getGoogleTrends(data.userProfile.attributes.country, missingPoints)
|
let googleSearchQueries = await this.getGoogleTrends(this.bot.config.searchSettings.useGeoLocaleQueries ? data.userProfile.attributes.country : 'US')
|
||||||
googleSearchQueries = this.bot.utils.shuffleArray(googleSearchQueries)
|
googleSearchQueries = this.bot.utils.shuffleArray(googleSearchQueries)
|
||||||
|
|
||||||
// Deduplicate the search terms
|
// Deduplicate the search terms
|
||||||
@@ -39,7 +47,7 @@ export class Search extends Workers {
|
|||||||
|
|
||||||
await this.bot.browser.utils.tryDismissAllMessages(page)
|
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
|
let maxLoop = 0 // If the loop hits 10 this when not gaining any points, we're assuming it's stuck. If it doesn't continue after 5 more searches with alternative queries, abort search
|
||||||
|
|
||||||
const queries: string[] = []
|
const queries: string[] = []
|
||||||
// Mobile search doesn't seem to like related queries?
|
// Mobile search doesn't seem to like related queries?
|
||||||
@@ -148,7 +156,7 @@ export class Search extends Workers {
|
|||||||
await this.bot.utils.wait(500)
|
await this.bot.utils.wait(500)
|
||||||
|
|
||||||
const searchBar = '#sb_form_q'
|
const searchBar = '#sb_form_q'
|
||||||
await searchPage.waitForSelector(searchBar, { state: 'visible', timeout: 10_000 })
|
await searchPage.waitForSelector(searchBar, { state: 'visible', timeout: 10000 })
|
||||||
await searchPage.click(searchBar) // Focus on the textarea
|
await searchPage.click(searchBar) // Focus on the textarea
|
||||||
await this.bot.utils.wait(500)
|
await this.bot.utils.wait(500)
|
||||||
await searchPage.keyboard.down(platformControlKey)
|
await searchPage.keyboard.down(platformControlKey)
|
||||||
@@ -203,49 +211,64 @@ export class Search extends Workers {
|
|||||||
return await this.bot.browser.func.getSearchPoints()
|
return await this.bot.browser.func.getSearchPoints()
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getGoogleTrends(geoLocale: string, queryCount: number): Promise<GoogleSearch[]> {
|
private async getGoogleTrends(geoLocale: string = 'US'): Promise<GoogleSearch[]> {
|
||||||
const queryTerms: GoogleSearch[] = []
|
const queryTerms: GoogleSearch[] = []
|
||||||
let i = 0
|
|
||||||
|
|
||||||
geoLocale = (this.bot.config.searchSettings.useGeoLocaleQueries && geoLocale.length === 2) ? geoLocale.toUpperCase() : 'US'
|
|
||||||
|
|
||||||
this.bot.log(this.bot.isMobile, '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) {
|
try {
|
||||||
i += 1
|
const request: AxiosRequestConfig = {
|
||||||
const date = new Date()
|
url: 'https://trends.google.com/_/TrendsUi/data/batchexecute',
|
||||||
date.setDate(date.getDate() - i)
|
method: 'POST',
|
||||||
const formattedDate = this.formatDate(date)
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
|
||||||
try {
|
},
|
||||||
const request = {
|
data: `f.req=[[[i0OFE,"[null, null, \\"${geoLocale.toUpperCase()}\\", 0, null, 48]"]]]`
|
||||||
url: `https://trends.google.com/trends/api/dailytrends?geo=${geoLocale}&hl=en&ed=${formattedDate}&ns=15`,
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await this.bot.axios.request(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.toLowerCase())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
this.bot.log(this.bot.isMobile, 'SEARCH-GOOGLE-TRENDS', 'An error occurred:' + error, 'error')
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const response = await this.bot.axios.request(request, this.bot.config.proxy.proxyGoogleTrends)
|
||||||
|
const rawText = response.data
|
||||||
|
|
||||||
|
const trendsData = this.extractJsonFromResponse(rawText)
|
||||||
|
if (!trendsData) {
|
||||||
|
throw this.bot.log(this.bot.isMobile, 'SEARCH-GOOGLE-TRENDS', 'Failed to parse Google Trends response', 'error')
|
||||||
|
}
|
||||||
|
|
||||||
|
const mappedTrendsData = trendsData.map(query => [query[0], query[9]!.slice(1)])
|
||||||
|
if (mappedTrendsData.length < 90) {
|
||||||
|
this.bot.log(this.bot.isMobile, 'SEARCH-GOOGLE-TRENDS', 'Insufficient search queries, falling back to US', 'warn')
|
||||||
|
return this.getGoogleTrends()
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [topic, relatedQueries] of mappedTrendsData) {
|
||||||
|
queryTerms.push({
|
||||||
|
topic: topic as string,
|
||||||
|
related: relatedQueries as string[]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
this.bot.log(this.bot.isMobile, 'SEARCH-GOOGLE-TRENDS', 'An error occurred:' + error, 'error')
|
||||||
}
|
}
|
||||||
|
|
||||||
return queryTerms
|
return queryTerms
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extractJsonFromResponse(text: string): GoogleTrendsResponse[1] | null {
|
||||||
|
const lines = text.split('\n')
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmed = line.trim()
|
||||||
|
if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(JSON.parse(trimmed)[0][2])[1]
|
||||||
|
} catch {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
private async getRelatedTerms(term: string): Promise<string[]> {
|
private async getRelatedTerms(term: string): Promise<string[]> {
|
||||||
try {
|
try {
|
||||||
const request = {
|
const request = {
|
||||||
@@ -256,7 +279,7 @@ export class Search extends Workers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await this.bot.axios.request(request)
|
const response = await this.bot.axios.request(request, this.bot.config.proxy.proxyBingTerms)
|
||||||
|
|
||||||
return response.data[1] as string[]
|
return response.data[1] as string[]
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -266,14 +289,6 @@ export class Search extends Workers {
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
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')
|
|
||||||
|
|
||||||
return `${year}${month}${day}`
|
|
||||||
}
|
|
||||||
|
|
||||||
private async randomScroll(page: Page) {
|
private async randomScroll(page: Page) {
|
||||||
try {
|
try {
|
||||||
const viewportHeight = await page.evaluate(() => window.innerHeight)
|
const viewportHeight = await page.evaluate(() => window.innerHeight)
|
||||||
@@ -297,7 +312,7 @@ export class Search extends Workers {
|
|||||||
await this.closeContinuePopup(page)
|
await this.closeContinuePopup(page)
|
||||||
|
|
||||||
// Stay for 10 seconds for page to load and "visit"
|
// Stay for 10 seconds for page to load and "visit"
|
||||||
await this.bot.utils.wait(10_000)
|
await this.bot.utils.wait(10000)
|
||||||
|
|
||||||
// Will get current tab if no new one is created, this will always be the visited site or the result page if it failed to click
|
// Will get current tab if no new one is created, this will always be the visited site or the result page if it failed to click
|
||||||
let lastTab = await this.bot.browser.utils.getLatestTab(page)
|
let lastTab = await this.bot.browser.utils.getLatestTab(page)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export class SearchOnBing extends Workers {
|
|||||||
const query = await this.getSearchQuery(activity.title)
|
const query = await this.getSearchQuery(activity.title)
|
||||||
|
|
||||||
const searchBar = '#sb_form_q'
|
const searchBar = '#sb_form_q'
|
||||||
await page.waitForSelector(searchBar, { state: 'visible', timeout: 10_000 })
|
await page.waitForSelector(searchBar, { state: 'visible', timeout: 10000 })
|
||||||
await page.click(searchBar)
|
await page.click(searchBar)
|
||||||
await this.bot.utils.wait(500)
|
await this.bot.utils.wait(500)
|
||||||
await page.keyboard.type(query)
|
await page.keyboard.type(query)
|
||||||
|
|||||||
@@ -134,5 +134,156 @@
|
|||||||
"Japan time",
|
"Japan time",
|
||||||
"New York time"
|
"New York time"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"title": "Maisons près de chez vous",
|
||||||
|
"queries": [
|
||||||
|
"Maisons près de chez moi"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Vous ressentez des symptômes ?",
|
||||||
|
"queries": [
|
||||||
|
"Éruption cutanée sur l'avant-bras",
|
||||||
|
"Nez bouché",
|
||||||
|
"Toux chatouilleuse"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Faites vos achats plus vite",
|
||||||
|
"queries": [
|
||||||
|
"Acheter une PS5",
|
||||||
|
"Acheter une Xbox",
|
||||||
|
"Offres sur les chaises"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Traduisez tout !",
|
||||||
|
"queries": [
|
||||||
|
"Traduction bienvenue à la maison en coréen",
|
||||||
|
"Traduction bienvenue à la maison en japonais",
|
||||||
|
"Traduction au revoir en japonais"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Rechercher paroles de chanson",
|
||||||
|
"queries": [
|
||||||
|
"Paroles de Debarge rhythm of the night"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Et si nous regardions ce film une nouvelle fois?",
|
||||||
|
"queries": [
|
||||||
|
"Alien film",
|
||||||
|
"Film Aliens",
|
||||||
|
"Film Alien 3",
|
||||||
|
"Film Predator"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Planifiez une petite escapade",
|
||||||
|
"queries": [
|
||||||
|
"Vols Amsterdam-Tokyo",
|
||||||
|
"Vols New York-Tokyo"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Consulter postes à pourvoir",
|
||||||
|
"queries": [
|
||||||
|
"emplois chez Microsoft",
|
||||||
|
"Offres d'emploi Microsoft",
|
||||||
|
"Emplois près de chez moi",
|
||||||
|
"emplois chez Boeing"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Vous pouvez suivre votre colis",
|
||||||
|
"queries": [
|
||||||
|
"Suivi Chronopost"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Trouver un endroit à découvrir",
|
||||||
|
"queries": [
|
||||||
|
"Itinéraire vers Berlin",
|
||||||
|
"Itinéraire vers Tokyo",
|
||||||
|
"Itinéraire vers New York"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Trop fatigué pour cuisiner ce soir ?",
|
||||||
|
"queries": [
|
||||||
|
"KFC près de chez moi",
|
||||||
|
"Burger King près de chez moi",
|
||||||
|
"McDonalds près de chez moi"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Convertissez rapidement votre argent",
|
||||||
|
"queries": [
|
||||||
|
"convertir 250 EUR en yen",
|
||||||
|
"convertir 500 EUR en yen"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Apprenez à cuisiner une nouvelle recette",
|
||||||
|
"queries": [
|
||||||
|
"Comment faire cuire la ratatouille",
|
||||||
|
"Comment faire cuire les lasagnes"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Trouvez des emplacements pour rester!",
|
||||||
|
"queries": [
|
||||||
|
"Hôtels Berlin Allemagne",
|
||||||
|
"Hôtels Amsterdam Pays-Bas"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Comment se porte l'économie ?",
|
||||||
|
"queries": [
|
||||||
|
"CAC 40"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Qui a gagné ?",
|
||||||
|
"queries": [
|
||||||
|
"score du Paris Saint-Germain"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Temps de jeu",
|
||||||
|
"queries": [
|
||||||
|
"Jeu vidéo Overwatch",
|
||||||
|
"Jeu vidéo Call of Duty"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Enrichissez votre vocabulaire",
|
||||||
|
"queries": [
|
||||||
|
"definition definition"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Quelle heure est-il ?",
|
||||||
|
"queries": [
|
||||||
|
"Heure du Japon",
|
||||||
|
"Heure de New York"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Vérifier la météo",
|
||||||
|
"queries": [
|
||||||
|
"Météo de Paris",
|
||||||
|
"Météo de la France"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Tenez-vous informé des sujets d'actualité",
|
||||||
|
"queries": [
|
||||||
|
"Augmentation Impots",
|
||||||
|
"Mort célébrité"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -5,13 +5,15 @@ export interface Config {
|
|||||||
parallel: boolean;
|
parallel: boolean;
|
||||||
runOnZeroPoints: boolean;
|
runOnZeroPoints: boolean;
|
||||||
clusters: number;
|
clusters: number;
|
||||||
workers: Workers;
|
saveFingerprint: ConfigSaveFingerprint;
|
||||||
|
workers: ConfigWorkers;
|
||||||
searchOnBingLocalQueries: boolean;
|
searchOnBingLocalQueries: boolean;
|
||||||
globalTimeout: number | string;
|
globalTimeout: number | string;
|
||||||
searchSettings: SearchSettings;
|
searchSettings: ConfigSearchSettings;
|
||||||
webhook: Webhook;
|
|
||||||
logExcludeFunc: string[];
|
logExcludeFunc: string[];
|
||||||
saveFingerprint: ConfigSaveFingerprint;
|
webhookLogExcludeFunc: string[];
|
||||||
|
proxy: ConfigProxy;
|
||||||
|
webhook: ConfigWebhook;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConfigSaveFingerprint {
|
export interface ConfigSaveFingerprint {
|
||||||
@@ -19,25 +21,30 @@ export interface ConfigSaveFingerprint {
|
|||||||
desktop: boolean;
|
desktop: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchSettings {
|
export interface ConfigSearchSettings {
|
||||||
useGeoLocaleQueries: boolean;
|
useGeoLocaleQueries: boolean;
|
||||||
scrollRandomResults: boolean;
|
scrollRandomResults: boolean;
|
||||||
clickRandomResults: boolean;
|
clickRandomResults: boolean;
|
||||||
searchDelay: SearchDelay;
|
searchDelay: ConfigSearchDelay;
|
||||||
retryMobileSearchAmount: number;
|
retryMobileSearchAmount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchDelay {
|
export interface ConfigSearchDelay {
|
||||||
min: number | string;
|
min: number | string;
|
||||||
max: number | string;
|
max: number | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Webhook {
|
export interface ConfigWebhook {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Workers {
|
export interface ConfigProxy {
|
||||||
|
proxyGoogleTrends: boolean;
|
||||||
|
proxyBingTerms: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConfigWorkers {
|
||||||
doDailySet: boolean;
|
doDailySet: boolean;
|
||||||
doMorePromotions: boolean;
|
doMorePromotions: boolean;
|
||||||
doPunchCards: boolean;
|
doPunchCards: boolean;
|
||||||
|
|||||||
@@ -28,8 +28,5 @@ sleep $SLEEPTIME
|
|||||||
# Log the start of the script
|
# Log the start of the script
|
||||||
echo "Starting script..."
|
echo "Starting script..."
|
||||||
|
|
||||||
# Update config with environment variables before running the script
|
|
||||||
node src/updateConfig.js
|
|
||||||
|
|
||||||
# Execute the Node.js script directly
|
# Execute the Node.js script directly
|
||||||
npm run start
|
npm run start
|
||||||
|
|||||||
@@ -36,7 +36,12 @@ class AxiosClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generic method to make any Axios request
|
// Generic method to make any Axios request
|
||||||
public async request(config: AxiosRequestConfig): Promise<AxiosResponse> {
|
public async request(config: AxiosRequestConfig, bypassProxy = false): Promise<AxiosResponse> {
|
||||||
|
if (bypassProxy) {
|
||||||
|
const bypassInstance = axios.create()
|
||||||
|
return bypassInstance.request(config)
|
||||||
|
}
|
||||||
|
|
||||||
return this.instance.request(config)
|
return this.instance.request(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ export function log(isMobile: boolean | 'main', title: string, message: string,
|
|||||||
const cleanStr = `[${currentTime}] [PID: ${process.pid}] [${type.toUpperCase()}] ${platformText} [${title}] ${message}`
|
const cleanStr = `[${currentTime}] [PID: ${process.pid}] [${type.toUpperCase()}] ${platformText} [${title}] ${message}`
|
||||||
|
|
||||||
// Send the clean string to the Webhook
|
// Send the clean string to the Webhook
|
||||||
Webhook(configData, cleanStr)
|
if (!configData.webhookLogExcludeFunc.some(x => x.toLowerCase() === title.toLowerCase())) {
|
||||||
|
Webhook(configData, cleanStr)
|
||||||
|
}
|
||||||
|
|
||||||
// Formatted string with chalk for terminal logging
|
// Formatted string with chalk for terminal logging
|
||||||
const str = `[${currentTime}] [PID: ${process.pid}] [${type.toUpperCase()}] ${chalkedPlatform} [${title}] ${message}`
|
const str = `[${currentTime}] [PID: ${process.pid}] [${type.toUpperCase()}] ${chalkedPlatform} [${title}] ${message}`
|
||||||
@@ -40,4 +42,4 @@ export function log(isMobile: boolean | 'main', title: string, message: string,
|
|||||||
applyChalk ? console.log(applyChalk(str)) : console.log(str)
|
applyChalk ? console.log(applyChalk(str)) : console.log(str)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,9 @@ export default class Util {
|
|||||||
}
|
}
|
||||||
|
|
||||||
shuffleArray<T>(array: T[]): T[] {
|
shuffleArray<T>(array: T[]): T[] {
|
||||||
const shuffledArray = array.slice()
|
return array.map(value => ({ value, sort: Math.random() }))
|
||||||
|
.sort((a, b) => a.sort - b.sort)
|
||||||
shuffledArray.sort(() => Math.random() - 0.5)
|
.map(({ value }) => value)
|
||||||
|
|
||||||
return shuffledArray
|
|
||||||
}
|
}
|
||||||
|
|
||||||
randomNumber(min: number, max: number): number {
|
randomNumber(min: number, max: number): number {
|
||||||
|
|||||||
Reference in New Issue
Block a user