mirror of
https://github.com/TheNetsky/Microsoft-Rewards-Script.git
synced 2026-01-11 02:46:17 +00:00
* feat: Implement edge version fetching with retry logic and caching * chore: Update version to 2.1.0 in package.json * fix: Update package version to 2.1.0 and enhance user agent metadata * feat: Enhance 2FA handling with improved TOTP input and submission logic * fix: Refactor getSystemComponents to improve mobile user agent string generation * feat: Add support for cron expressions for advanced scheduling * feat: Improve humanization feature with detailed logging for off-days configuration * feat: Add live log streaming via webhook and enhance logging configuration * fix: Remove unused @types/cron-parser dependency from devDependencies * feat: Add cron-parser dependency and enhance Axios error handling for proxy authentication * feat: Enhance dashboard data retrieval with retry logic and diagnostics capture * feat: Add ready-to-use sample configurations and update configuration settings for better customization * feat: Add buy mode detection and configuration methods for enhanced manual redemption * feat: Migrate configuration from JSON to JSONC format for improved readability and comments support feat: Implement centralized diagnostics capture for better error handling and reporting fix: Update documentation references from config.json to config.jsonc chore: Add .vscode to .gitignore for cleaner project structure refactor: Enhance humanization and diagnostics capture logic in BrowserUtil and Login classes * feat: Reintroduce ambiance declarations for the 'luxon' module to unlock TypeScript * feat: Update search delay settings for improved performance and reliability * feat: Update README and SECURITY documentation for clarity and improved data handling guidelines * Enhance README and SECURITY documentation for Microsoft Rewards Script V2 - Updated README.md to improve structure, add badges, and enhance clarity on features and setup instructions. - Expanded SECURITY.md to provide detailed data handling practices, security guidelines, and best practices for users. - Included sections on data flow, credential management, and responsible use of the automation tool. - Added a security checklist for users to ensure safe practices while using the script. * feat: Réorganiser et enrichir la documentation du README pour une meilleure clarté et accessibilité * feat: Updated and reorganized the README for better presentation and clarity * feat: Revised and simplified the README for better clarity and accessibility * Update README.md
169 lines
6.6 KiB
TypeScript
169 lines
6.6 KiB
TypeScript
import axios from 'axios'
|
|
import chalk from 'chalk'
|
|
|
|
import { Ntfy } from './Ntfy'
|
|
import { loadConfig } from './Load'
|
|
|
|
type WebhookBuffer = {
|
|
lines: string[]
|
|
sending: boolean
|
|
timer?: NodeJS.Timeout
|
|
}
|
|
|
|
const webhookBuffers = new Map<string, WebhookBuffer>()
|
|
|
|
function getBuffer(url: string): WebhookBuffer {
|
|
let buf = webhookBuffers.get(url)
|
|
if (!buf) {
|
|
buf = { lines: [], sending: false }
|
|
webhookBuffers.set(url, buf)
|
|
}
|
|
return buf
|
|
}
|
|
|
|
async function sendBatch(url: string, buf: WebhookBuffer) {
|
|
if (buf.sending) return
|
|
buf.sending = true
|
|
while (buf.lines.length > 0) {
|
|
const chunk: string[] = []
|
|
let currentLength = 0
|
|
while (buf.lines.length > 0) {
|
|
const next = buf.lines[0]!
|
|
const projected = currentLength + next.length + (chunk.length > 0 ? 1 : 0)
|
|
if (projected > 1900 && chunk.length > 0) break
|
|
buf.lines.shift()
|
|
chunk.push(next)
|
|
currentLength = projected
|
|
}
|
|
|
|
const content = chunk.join('\n').slice(0, 1900)
|
|
if (!content) {
|
|
continue
|
|
}
|
|
|
|
try {
|
|
await axios.post(url, { content }, { headers: { 'Content-Type': 'application/json' }, timeout: 10000 })
|
|
await new Promise(resolve => setTimeout(resolve, 500))
|
|
} catch (error) {
|
|
// Re-queue failed batch at front and exit loop
|
|
buf.lines = chunk.concat(buf.lines)
|
|
console.error('[Webhook] live log delivery failed:', error)
|
|
break
|
|
}
|
|
}
|
|
buf.sending = false
|
|
}
|
|
|
|
function enqueueWebhookLog(url: string, line: string) {
|
|
const buf = getBuffer(url)
|
|
buf.lines.push(line)
|
|
if (!buf.timer) {
|
|
buf.timer = setTimeout(() => {
|
|
buf.timer = undefined
|
|
void sendBatch(url, buf)
|
|
}, 750)
|
|
}
|
|
}
|
|
|
|
// Synchronous logger that returns an Error when type === 'error' so callers can `throw log(...)` safely.
|
|
export function log(isMobile: boolean | 'main', title: string, message: string, type: 'log' | 'warn' | 'error' = 'log', color?: keyof typeof chalk): Error | void {
|
|
const configData = loadConfig()
|
|
|
|
// Access logging config with fallback for backward compatibility
|
|
const configAny = configData as unknown as Record<string, unknown>
|
|
const loggingConfig = configAny.logging || configData
|
|
const loggingConfigAny = loggingConfig as unknown as Record<string, unknown>
|
|
|
|
const logExcludeFunc = Array.isArray(loggingConfigAny.excludeFunc) ? loggingConfigAny.excludeFunc :
|
|
Array.isArray(loggingConfigAny.logExcludeFunc) ? loggingConfigAny.logExcludeFunc : []
|
|
|
|
if (Array.isArray(logExcludeFunc) && logExcludeFunc.some((x: string) => x.toLowerCase() === title.toLowerCase())) {
|
|
return
|
|
}
|
|
|
|
const currentTime = new Date().toLocaleString()
|
|
const platformText = isMobile === 'main' ? 'MAIN' : isMobile ? 'MOBILE' : 'DESKTOP'
|
|
|
|
// Clean string for notifications (no chalk, structured)
|
|
type LoggingCfg = { excludeFunc?: string[]; webhookExcludeFunc?: string[]; redactEmails?: boolean }
|
|
const loggingCfg: LoggingCfg = (configAny.logging || {}) as LoggingCfg
|
|
const shouldRedact = !!loggingCfg.redactEmails
|
|
const redact = (s: string) => shouldRedact ? s.replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/ig, (m) => {
|
|
const [u, d] = m.split('@'); return `${(u||'').slice(0,2)}***@${d||''}`
|
|
}) : s
|
|
const cleanStr = redact(`[${currentTime}] [PID: ${process.pid}] [${type.toUpperCase()}] ${platformText} [${title}] ${message}`)
|
|
|
|
// Define conditions for sending to NTFY
|
|
const ntfyConditions = {
|
|
log: [
|
|
message.toLowerCase().includes('started tasks for account'),
|
|
message.toLowerCase().includes('press the number'),
|
|
message.toLowerCase().includes('no points to earn')
|
|
],
|
|
error: [],
|
|
warn: [
|
|
message.toLowerCase().includes('aborting'),
|
|
message.toLowerCase().includes('didn\'t gain')
|
|
]
|
|
}
|
|
|
|
// Check if the current log type and message meet the NTFY conditions
|
|
try {
|
|
if (type in ntfyConditions && ntfyConditions[type as keyof typeof ntfyConditions].some(condition => condition)) {
|
|
// Fire-and-forget
|
|
Promise.resolve(Ntfy(cleanStr, type)).catch(() => { /* ignore ntfy errors */ })
|
|
}
|
|
} catch { /* ignore */ }
|
|
|
|
// Console output with better formatting
|
|
const typeIndicator = type === 'error' ? '✗' : type === 'warn' ? '⚠' : '●'
|
|
const platformColor = isMobile === 'main' ? chalk.cyan : isMobile ? chalk.blue : chalk.magenta
|
|
const typeColor = type === 'error' ? chalk.red : type === 'warn' ? chalk.yellow : chalk.green
|
|
|
|
const formattedStr = [
|
|
chalk.gray(`[${currentTime}]`),
|
|
chalk.gray(`[${process.pid}]`),
|
|
typeColor(`${typeIndicator} ${type.toUpperCase()}`),
|
|
platformColor(`[${platformText}]`),
|
|
chalk.bold(`[${title}]`),
|
|
redact(message)
|
|
].join(' ')
|
|
|
|
const applyChalk = color && typeof chalk[color] === 'function' ? chalk[color] as (msg: string) => string : null
|
|
|
|
// Log based on the type
|
|
switch (type) {
|
|
case 'warn':
|
|
applyChalk ? console.warn(applyChalk(formattedStr)) : console.warn(formattedStr)
|
|
break
|
|
|
|
case 'error':
|
|
applyChalk ? console.error(applyChalk(formattedStr)) : console.error(formattedStr)
|
|
break
|
|
|
|
default:
|
|
applyChalk ? console.log(applyChalk(formattedStr)) : console.log(formattedStr)
|
|
break
|
|
}
|
|
|
|
// Webhook streaming (live logs)
|
|
try {
|
|
const loggingCfg: Record<string, unknown> = (configAny.logging || {}) as Record<string, unknown>
|
|
const webhookCfg = configData.webhook
|
|
const liveUrlRaw = typeof loggingCfg.liveWebhookUrl === 'string' ? loggingCfg.liveWebhookUrl.trim() : ''
|
|
const liveUrl = liveUrlRaw || (webhookCfg?.enabled && webhookCfg.url ? webhookCfg.url : '')
|
|
const webhookExclude = Array.isArray(loggingCfg.webhookExcludeFunc) ? loggingCfg.webhookExcludeFunc : configData.webhookLogExcludeFunc || []
|
|
const webhookExcluded = Array.isArray(webhookExclude) && webhookExclude.some((x: string) => x.toLowerCase() === title.toLowerCase())
|
|
if (liveUrl && !webhookExcluded) {
|
|
enqueueWebhookLog(liveUrl, cleanStr)
|
|
}
|
|
} catch (error) {
|
|
console.error('[Logger] Failed to enqueue webhook log:', error)
|
|
}
|
|
|
|
// Return an Error when logging an error so callers can `throw log(...)`
|
|
if (type === 'error') {
|
|
// CommunityReporter disabled per project policy
|
|
return new Error(cleanStr)
|
|
}
|
|
} |