mirror of
https://github.com/TheNetsky/Microsoft-Rewards-Script.git
synced 2026-01-18 14:03:58 +00:00
Small update to be deployed quickly. (#358)
* chore: Update TypeScript configuration and add @types/node as a dev dependency * feat: Add unified cross-platform setup script for easier configuration and installation * docs: Revise README for improved setup instructions and clarity * feat: Enhance setup scripts with improved prerequisite checks and user prompts * feat: Refactor setup scripts and enhance browser handling with automatic Playwright installation
This commit is contained in:
@@ -25,19 +25,40 @@ class Browser {
|
||||
}
|
||||
|
||||
async createBrowser(proxy: AccountProxy, email: string): Promise<BrowserContext> {
|
||||
const browser = await playwright.chromium.launch({
|
||||
//channel: 'msedge', // Uses Edge instead of chrome
|
||||
headless: this.bot.config.headless,
|
||||
...(proxy.url && { proxy: { username: proxy.username, password: proxy.password, server: `${proxy.url}:${proxy.port}` } }),
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--mute-audio',
|
||||
'--disable-setuid-sandbox',
|
||||
'--ignore-certificate-errors',
|
||||
'--ignore-certificate-errors-spki-list',
|
||||
'--ignore-ssl-errors'
|
||||
]
|
||||
})
|
||||
// Optional automatic browser installation (set AUTO_INSTALL_BROWSERS=1)
|
||||
if (process.env.AUTO_INSTALL_BROWSERS === '1') {
|
||||
try {
|
||||
// Dynamically import child_process to avoid overhead otherwise
|
||||
const { execSync } = await import('child_process') as any
|
||||
execSync('npx playwright install chromium', { stdio: 'ignore' })
|
||||
} catch { /* silent */ }
|
||||
}
|
||||
|
||||
let browser: any
|
||||
try {
|
||||
browser = await playwright.chromium.launch({
|
||||
//channel: 'msedge', // Uses Edge instead of chrome
|
||||
headless: this.bot.config.headless,
|
||||
...(proxy.url && { proxy: { username: proxy.username, password: proxy.password, server: `${proxy.url}:${proxy.port}` } }),
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--mute-audio',
|
||||
'--disable-setuid-sandbox',
|
||||
'--ignore-certificate-errors',
|
||||
'--ignore-certificate-errors-spki-list',
|
||||
'--ignore-ssl-errors'
|
||||
]
|
||||
})
|
||||
} catch (e: any) {
|
||||
const msg = (e instanceof Error ? e.message : String(e))
|
||||
// Common missing browser executable guidance
|
||||
if (/Executable doesn't exist/i.test(msg)) {
|
||||
this.bot.log(this.bot.isMobile, 'BROWSER', 'Chromium not installed for Playwright. Run: "npx playwright install chromium" (or set AUTO_INSTALL_BROWSERS=1 to auto attempt).', 'error')
|
||||
} else {
|
||||
this.bot.log(this.bot.isMobile, 'BROWSER', 'Failed to launch browser: ' + msg, 'error')
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
||||
const sessionData = await loadSessionData(this.bot.config.sessionPath, email, this.bot.isMobile, this.bot.config.saveFingerprint)
|
||||
|
||||
|
||||
42
src/index.ts
42
src/index.ts
@@ -170,14 +170,28 @@ export class MicrosoftRewardsBot {
|
||||
const errors: string[] = []
|
||||
|
||||
this.axios = new Axios(account.proxy)
|
||||
const verbose = process.env.DEBUG_REWARDS_VERBOSE === '1'
|
||||
const formatFullErr = (label: string, e: any) => {
|
||||
const base = shortErr(e)
|
||||
if (verbose && e instanceof Error) {
|
||||
return `${label}:${base} :: ${e.stack?.split('\n').slice(0,4).join(' | ')}`
|
||||
}
|
||||
return `${label}:${base}`
|
||||
}
|
||||
|
||||
if (this.config.parallel) {
|
||||
const mobileInstance = new MicrosoftRewardsBot(true)
|
||||
mobileInstance.axios = this.axios
|
||||
// Run both and capture results
|
||||
const [desktopResult, mobileResult] = await Promise.all([
|
||||
this.Desktop(account).catch(e => { errors.push(`desktop:${shortErr(e)}`); return null }),
|
||||
mobileInstance.Mobile(account).catch(e => { errors.push(`mobile:${shortErr(e)}`); return null })
|
||||
])
|
||||
// Run both and capture results with detailed logging
|
||||
const desktopPromise = this.Desktop(account).catch(e => {
|
||||
log(false, 'TASK', `Desktop flow failed early for ${account.email}: ${e instanceof Error ? e.message : e}`,'error')
|
||||
errors.push(formatFullErr('desktop', e)); return null
|
||||
})
|
||||
const mobilePromise = mobileInstance.Mobile(account).catch(e => {
|
||||
log(true, 'TASK', `Mobile flow failed early for ${account.email}: ${e instanceof Error ? e.message : e}`,'error')
|
||||
errors.push(formatFullErr('mobile', e)); return null
|
||||
})
|
||||
const [desktopResult, mobileResult] = await Promise.all([desktopPromise, mobilePromise])
|
||||
if (desktopResult) {
|
||||
desktopInitial = desktopResult.initialPoints
|
||||
desktopCollected = desktopResult.collectedPoints
|
||||
@@ -188,14 +202,20 @@ export class MicrosoftRewardsBot {
|
||||
}
|
||||
} else {
|
||||
this.isMobile = false
|
||||
const desktopResult = await this.Desktop(account).catch(e => { errors.push(`desktop:${shortErr(e)}`); return null })
|
||||
const desktopResult = await this.Desktop(account).catch(e => {
|
||||
log(false, 'TASK', `Desktop flow failed early for ${account.email}: ${e instanceof Error ? e.message : e}`,'error')
|
||||
errors.push(formatFullErr('desktop', e)); return null
|
||||
})
|
||||
if (desktopResult) {
|
||||
desktopInitial = desktopResult.initialPoints
|
||||
desktopCollected = desktopResult.collectedPoints
|
||||
}
|
||||
|
||||
this.isMobile = true
|
||||
const mobileResult = await this.Mobile(account).catch(e => { errors.push(`mobile:${shortErr(e)}`); return null })
|
||||
const mobileResult = await this.Mobile(account).catch(e => {
|
||||
log(true, 'TASK', `Mobile flow failed early for ${account.email}: ${e instanceof Error ? e.message : e}`,'error')
|
||||
errors.push(formatFullErr('mobile', e)); return null
|
||||
})
|
||||
if (mobileResult) {
|
||||
mobileInitial = mobileResult.initialPoints
|
||||
mobileCollected = mobileResult.collectedPoints
|
||||
@@ -221,6 +241,12 @@ export class MicrosoftRewardsBot {
|
||||
}
|
||||
|
||||
log(this.isMobile, 'MAIN-PRIMARY', 'Completed tasks for ALL accounts', 'log', 'green')
|
||||
// Extra diagnostic summary when verbose
|
||||
if (process.env.DEBUG_REWARDS_VERBOSE === '1') {
|
||||
for (const summary of this.accountSummaries) {
|
||||
log('main','SUMMARY-DEBUG',`Account ${summary.email} collected D:${summary.desktopCollected} M:${summary.mobileCollected} TOTAL:${summary.totalCollected} ERRORS:${summary.errors.length ? summary.errors.join(';') : 'none'}`)
|
||||
}
|
||||
}
|
||||
// If in worker mode (clusters>1) send summaries to primary
|
||||
if (this.config.clusters > 1 && !cluster.isPrimary) {
|
||||
if (process.send) {
|
||||
@@ -235,6 +261,7 @@ export class MicrosoftRewardsBot {
|
||||
|
||||
// Desktop
|
||||
async Desktop(account: Account) {
|
||||
log(false,'FLOW','Desktop() invoked')
|
||||
const browser = await this.browserFactory.createBrowser(account.proxy, account.email)
|
||||
this.homePage = await browser.newPage()
|
||||
|
||||
@@ -311,6 +338,7 @@ export class MicrosoftRewardsBot {
|
||||
|
||||
// Mobile
|
||||
async Mobile(account: Account) {
|
||||
log(true,'FLOW','Mobile() invoked')
|
||||
const browser = await this.browserFactory.createBrowser(account.proxy, account.email)
|
||||
this.homePage = await browser.newPage()
|
||||
|
||||
|
||||
78
src/types/node-shim.d.ts
vendored
78
src/types/node-shim.d.ts
vendored
@@ -1,78 +0,0 @@
|
||||
// Fallback in case @types/node not installed yet; ensures Process/stubs to reduce red squiggles.
|
||||
// Prefer installing @types/node for full types.
|
||||
|
||||
interface ProcessEnv { [key: string]: string | undefined }
|
||||
|
||||
interface Process {
|
||||
pid: number
|
||||
exit(code?: number): never
|
||||
send?(message: any): void
|
||||
on(event: string, listener: (...args: any[]) => void): any
|
||||
stdin: { on(event: string, listener: (...args: any[]) => void): any }
|
||||
stdout: { write(chunk: any): boolean }
|
||||
env: ProcessEnv
|
||||
}
|
||||
|
||||
declare var process: Process
|
||||
|
||||
// Minimal axios module declaration
|
||||
declare module 'axios' {
|
||||
export interface AxiosRequestConfig { [key: string]: any }
|
||||
export interface AxiosResponse<T = any> { data: T }
|
||||
export interface AxiosInstance {
|
||||
defaults: any
|
||||
request<T=any>(config: AxiosRequestConfig): Promise<AxiosResponse<T>>
|
||||
}
|
||||
export interface AxiosStatic {
|
||||
(config: AxiosRequestConfig): Promise<AxiosResponse>
|
||||
request<T=any>(config: AxiosRequestConfig): Promise<AxiosResponse<T>>
|
||||
create(config?: AxiosRequestConfig): AxiosInstance
|
||||
}
|
||||
const axios: AxiosStatic
|
||||
export default axios
|
||||
}
|
||||
|
||||
// Minimal readline
|
||||
declare module 'readline' {
|
||||
export interface Interface { question(query: string, cb: (answer: string)=>void): void; close(): void }
|
||||
export function createInterface(opts: any): Interface
|
||||
export default {} as any
|
||||
}
|
||||
|
||||
// Minimal crypto
|
||||
declare module 'crypto' {
|
||||
export function randomBytes(size: number): { toString(encoding: string): string }
|
||||
}
|
||||
|
||||
// Minimal os module
|
||||
declare module 'os' {
|
||||
export function platform(): string
|
||||
}
|
||||
|
||||
// Minimal cheerio subset
|
||||
declare module 'cheerio' {
|
||||
export interface CheerioAPI {
|
||||
(selector: any): any
|
||||
load(html: string): CheerioAPI
|
||||
text(): string
|
||||
}
|
||||
export function load(html: string): CheerioAPI
|
||||
}
|
||||
|
||||
declare module 'cluster' {
|
||||
import { EventEmitter } from 'events'
|
||||
interface WorkerLike extends EventEmitter {
|
||||
id: number
|
||||
process: { pid: number }
|
||||
send?(message: any): void
|
||||
on(event: 'message', listener: (msg: any) => void): any
|
||||
}
|
||||
interface Cluster extends EventEmitter {
|
||||
isPrimary: boolean
|
||||
fork(env?: NodeJS.ProcessEnv): WorkerLike
|
||||
workers?: Record<string, WorkerLike>
|
||||
on(event: 'exit', listener: (worker: WorkerLike, code: number) => void): any
|
||||
}
|
||||
const cluster: Cluster
|
||||
export default cluster
|
||||
}
|
||||
57
src/types/rebrowser-playwright.d.ts
vendored
57
src/types/rebrowser-playwright.d.ts
vendored
@@ -1,57 +0,0 @@
|
||||
// Minimal module declaration to silence TS complaints if upstream types not found.
|
||||
// You should replace with actual types if the package provides them.
|
||||
|
||||
// Basic playwright stubs (only what we currently need). Replace with real @types if available.
|
||||
declare module 'playwright' {
|
||||
export interface Cookie { name: string; value: string; domain?: string; path?: string; expires?: number; httpOnly?: boolean; secure?: boolean; sameSite?: 'Lax'|'Strict'|'None' }
|
||||
export interface BrowserContext {
|
||||
newPage(): Promise<Page>
|
||||
setDefaultTimeout(timeout: number): void
|
||||
addCookies(cookies: Cookie[]): Promise<void>
|
||||
cookies(): Promise<Cookie[]>
|
||||
pages(): Page[]
|
||||
close(): Promise<void>
|
||||
}
|
||||
export interface Browser {
|
||||
newPage(): Promise<Page>
|
||||
context(): BrowserContext
|
||||
close(): Promise<void>
|
||||
pages?(): Page[]
|
||||
}
|
||||
export interface Keyboard {
|
||||
type(text: string): Promise<any>
|
||||
press(key: string): Promise<any>
|
||||
down(key: string): Promise<any>
|
||||
up(key: string): Promise<any>
|
||||
}
|
||||
export interface Locator {
|
||||
first(): Locator
|
||||
click(opts?: any): Promise<any>
|
||||
isVisible(opts?: any): Promise<boolean>
|
||||
nth(index: number): Locator
|
||||
}
|
||||
export interface Page {
|
||||
goto(url: string, opts?: any): Promise<any>
|
||||
waitForLoadState(state?: string, opts?: any): Promise<any>
|
||||
waitForSelector(selector: string, opts?: any): Promise<any>
|
||||
fill(selector: string, value: string): Promise<any>
|
||||
keyboard: Keyboard
|
||||
click(selector: string, opts?: any): Promise<any>
|
||||
close(): Promise<any>
|
||||
url(): string
|
||||
route(match: string, handler: any): Promise<any>
|
||||
locator(selector: string): Locator
|
||||
$: (selector: string) => Promise<any>
|
||||
context(): BrowserContext
|
||||
reload(opts?: any): Promise<any>
|
||||
evaluate<R=any>(pageFunction: any, arg?: any): Promise<R>
|
||||
content(): Promise<string>
|
||||
waitForTimeout(timeout: number): Promise<void>
|
||||
}
|
||||
export interface ChromiumType { launch(opts?: any): Promise<Browser> }
|
||||
export const chromium: ChromiumType
|
||||
}
|
||||
|
||||
declare module 'rebrowser-playwright' {
|
||||
export * from 'playwright'
|
||||
}
|
||||
25
src/types/shims-node.d.ts
vendored
25
src/types/shims-node.d.ts
vendored
@@ -1,25 +0,0 @@
|
||||
// Minimal shims to silence TypeScript errors in environments without @types/node
|
||||
// If possible, install @types/node instead for full typing.
|
||||
|
||||
declare const __dirname: string
|
||||
|
||||
declare namespace NodeJS { interface Process { pid: number; send?: (msg: any) => void; exit(code?: number): void; } }
|
||||
|
||||
declare const process: NodeJS.Process
|
||||
|
||||
declare module 'cluster' {
|
||||
interface Worker { process: { pid: number }; on(event: 'message', cb: (msg: any) => void): void }
|
||||
const isPrimary: boolean
|
||||
function fork(): Worker
|
||||
function on(event: 'exit', cb: (worker: Worker, code: number) => void): void
|
||||
export { isPrimary, fork, on, Worker }
|
||||
export default { isPrimary, fork, on }
|
||||
}
|
||||
|
||||
declare module 'fs' { const x: any; export = x }
|
||||
|
||||
declare module 'path' { const x: any; export = x }
|
||||
|
||||
// Do NOT redeclare 'Page' to avoid erasing actual Playwright types if present.
|
||||
// If types are missing, install: npm i -D @types/node
|
||||
|
||||
Reference in New Issue
Block a user