mirror of
https://github.com/LightZirconite/Microsoft-Rewards-Bot.git
synced 2026-01-09 00:56:16 +00:00
Added single-account execution functionality and historical data loading for charts
This commit is contained in:
112
public/app.js
112
public/app.js
@@ -220,6 +220,88 @@ function setChartPeriod(period, btn) {
|
||||
function loadInitialData() {
|
||||
fetch('/api/status').then((r) => { return r.json() }).then(updateBotStatus).catch(() => { })
|
||||
fetch('/api/accounts').then((r) => { return r.json() }).then(renderAccounts).catch(() => { })
|
||||
loadAccountHistoryData() // FIXED: Load real historical data for charts
|
||||
}
|
||||
|
||||
// FIXED: Load account history from API to populate charts with real data
|
||||
function loadAccountHistoryData() {
|
||||
fetch('/api/account-history')
|
||||
.then((r) => r.json())
|
||||
.then((histories) => {
|
||||
if (histories && Object.keys(histories).length > 0) {
|
||||
updateChartsWithRealData(histories)
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.warn('[History] Failed to load:', e)
|
||||
})
|
||||
}
|
||||
|
||||
// FIXED: Update charts with real historical data
|
||||
function updateChartsWithRealData(histories) {
|
||||
var last7Days = {}
|
||||
var activityCounts = {
|
||||
'Desktop Search': 0,
|
||||
'Mobile Search': 0,
|
||||
'Daily Set': 0,
|
||||
'Quizzes': 0,
|
||||
'Other': 0
|
||||
}
|
||||
|
||||
// Process each account's history
|
||||
Object.values(histories).forEach((accountHistory) => {
|
||||
if (!accountHistory.history) return
|
||||
|
||||
accountHistory.history.forEach((entry) => {
|
||||
var date = entry.date || entry.timestamp.split('T')[0]
|
||||
|
||||
// Aggregate points by day
|
||||
if (!last7Days[date]) {
|
||||
last7Days[date] = 0
|
||||
}
|
||||
last7Days[date] += entry.totalPoints || 0
|
||||
|
||||
// Count activity types
|
||||
if (entry.completedActivities) {
|
||||
entry.completedActivities.forEach((activity) => {
|
||||
if (activity.includes('Search')) {
|
||||
if (entry.desktopPoints > 0) activityCounts['Desktop Search']++
|
||||
if (entry.mobilePoints > 0) activityCounts['Mobile Search']++
|
||||
} else if (activity.includes('DailySet')) {
|
||||
activityCounts['Daily Set']++
|
||||
} else if (activity.includes('Quiz') || activity.includes('Poll')) {
|
||||
activityCounts['Quizzes']++
|
||||
} else {
|
||||
activityCounts['Other']++
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Update points chart with last 7 days
|
||||
if (pointsChart) {
|
||||
var sortedDates = Object.keys(last7Days).sort().slice(-7)
|
||||
var labels = sortedDates.map((d) => {
|
||||
var date = new Date(d)
|
||||
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
|
||||
})
|
||||
var data = sortedDates.map((d) => { return last7Days[d] })
|
||||
|
||||
pointsChart.data.labels = labels.length > 0 ? labels : generateDateLabels(7)
|
||||
pointsChart.data.datasets[0].data = data.length > 0 ? data : generatePlaceholderData(7)
|
||||
pointsChart.update()
|
||||
}
|
||||
|
||||
// Update activity chart
|
||||
if (activityChart) {
|
||||
var total = Object.values(activityCounts).reduce((sum, val) => { return sum + val }, 0)
|
||||
if (total > 0) {
|
||||
activityChart.data.labels = Object.keys(activityCounts)
|
||||
activityChart.data.datasets[0].data = Object.values(activityCounts)
|
||||
activityChart.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function refreshData() {
|
||||
@@ -531,8 +613,36 @@ function runSingleAccount() {
|
||||
function executeSingleAccount() {
|
||||
var select = document.getElementById('singleAccountSelect')
|
||||
if (!select) return
|
||||
|
||||
var email = select.value
|
||||
closeModal()
|
||||
showToast('Running account: ' + maskEmail(select.value), 'info')
|
||||
|
||||
if (!email) {
|
||||
showToast('No account selected', 'error')
|
||||
return
|
||||
}
|
||||
|
||||
showToast('Starting bot for: ' + maskEmail(email), 'info')
|
||||
|
||||
// Call API to run single account
|
||||
fetch('/api/run-single', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email: email })
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
if (data.success) {
|
||||
showToast('✓ Bot started for account: ' + maskEmail(email), 'success')
|
||||
loadStatus()
|
||||
} else {
|
||||
showToast('✗ Failed to start: ' + (data.error || 'Unknown error'), 'error')
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('[API] Run single failed:', err)
|
||||
showToast('✗ Request failed: ' + err.message, 'error')
|
||||
})
|
||||
}
|
||||
|
||||
function exportLogs() {
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
},
|
||||
// === BROWSER ===
|
||||
"browser": {
|
||||
"headless": true,
|
||||
"headless": false,
|
||||
"globalTimeout": "30s"
|
||||
},
|
||||
"fingerprinting": {
|
||||
|
||||
@@ -106,6 +106,81 @@ export class BotController {
|
||||
return await this.start()
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a single account (for dashboard "run single" feature)
|
||||
* FIXED: Actually implement single account execution
|
||||
*/
|
||||
public async runSingle(email: string): Promise<{ success: boolean; error?: string; pid?: number }> {
|
||||
if (this.botInstance) {
|
||||
return { success: false, error: 'Bot is already running. Stop it first.' }
|
||||
}
|
||||
|
||||
if (this.isStarting) {
|
||||
return { success: false, error: 'Bot is currently starting, please wait' }
|
||||
}
|
||||
|
||||
try {
|
||||
this.isStarting = true
|
||||
this.log(`🚀 Starting bot for single account: ${email}`, 'log')
|
||||
|
||||
const { MicrosoftRewardsBot } = await import('../index')
|
||||
const { loadAccounts } = await import('../util/state/Load')
|
||||
|
||||
// Load all accounts and filter to just this one
|
||||
const allAccounts = loadAccounts()
|
||||
const targetAccount = allAccounts.find(a => a.email === email)
|
||||
|
||||
if (!targetAccount) {
|
||||
return { success: false, error: `Account ${email} not found in accounts.jsonc` }
|
||||
}
|
||||
|
||||
this.botInstance = new MicrosoftRewardsBot(false)
|
||||
this.startTime = new Date()
|
||||
dashboardState.setRunning(true)
|
||||
dashboardState.setBotInstance(this.botInstance)
|
||||
|
||||
// Update account status
|
||||
dashboardState.updateAccount(email, { status: 'running', errors: [] })
|
||||
|
||||
// Run bot asynchronously with single account
|
||||
void (async () => {
|
||||
try {
|
||||
this.log(`✓ Bot initialized for ${email}, starting execution...`, 'log')
|
||||
|
||||
await this.botInstance!.initialize()
|
||||
|
||||
// Override accounts to run only this one
|
||||
; (this.botInstance as any).accounts = [targetAccount]
|
||||
|
||||
await this.botInstance!.run()
|
||||
|
||||
this.log(`✓ Bot completed successfully for ${email}`, 'log')
|
||||
dashboardState.updateAccount(email, { status: 'completed' })
|
||||
} catch (error) {
|
||||
const errMsg = error instanceof Error ? error.message : String(error)
|
||||
this.log(`Bot error for ${email}: ${errMsg}`, 'error')
|
||||
dashboardState.updateAccount(email, {
|
||||
status: 'error',
|
||||
errors: [errMsg]
|
||||
})
|
||||
} finally {
|
||||
this.cleanup()
|
||||
}
|
||||
})()
|
||||
|
||||
return { success: true, pid: process.pid }
|
||||
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : String(error)
|
||||
this.log(`Failed to start bot for ${email}: ${errorMsg}`, 'error')
|
||||
dashboardState.updateAccount(email, { status: 'error', errors: [errorMsg] })
|
||||
this.cleanup()
|
||||
return { success: false, error: errorMsg }
|
||||
} finally {
|
||||
this.isStarting = false
|
||||
}
|
||||
}
|
||||
|
||||
private async wait(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -199,6 +199,34 @@ apiRouter.post('/restart', async (_req: Request, res: Response): Promise<void> =
|
||||
}
|
||||
})
|
||||
|
||||
// POST /api/run-single - Run a single account (dashboard feature)
|
||||
apiRouter.post('/run-single', async (req: Request, res: Response): Promise<void> => {
|
||||
try {
|
||||
const { email } = req.body
|
||||
|
||||
if (!email) {
|
||||
sendError(res, 400, 'Email is required')
|
||||
return
|
||||
}
|
||||
|
||||
const status = botController.getStatus()
|
||||
if (status.running) {
|
||||
sendError(res, 400, `Bot already running (PID: ${status.pid}). Stop it first.`)
|
||||
return
|
||||
}
|
||||
|
||||
const result = await botController.runSingle(email)
|
||||
|
||||
if (result.success) {
|
||||
sendSuccess(res, { message: `Started bot for account ${email}`, pid: result.pid })
|
||||
} else {
|
||||
sendError(res, 500, result.error || 'Failed to start bot for account')
|
||||
}
|
||||
} catch (error) {
|
||||
sendError(res, 500, getErr(error))
|
||||
}
|
||||
})
|
||||
|
||||
// GET /api/metrics - Basic metrics
|
||||
apiRouter.get('/metrics', (_req: Request, res: Response) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user