Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 24 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,9 @@ ACCOUNT_1_PASSWORD=your_password
>
> ```yaml
> services:
> microsoft-rewards-script:
> env_file:
> - path: .env
> microsoft-rewards-script:
> env_file:
> - path: .env
> ```
>
> 另外用**列表式** `environment:`(`- KEY=value`)时值**不要加引号**——引号会成为值的一部分(如 `- CRON_SCHEDULE="0 9 * * *"` 会让 cron 表达式带引号而失效);映射式(`KEY: value`)则按 YAML 规则正常加引号。
Expand Down Expand Up @@ -268,20 +268,20 @@ docker compose restart # 重启(不重建)
<details>
<summary><b>🔵 Core / 核心配置</b></summary>

| 设置 | 描述 | 默认值 | Docker 环境变量 |
| --------------------------- | ----------------------------------- | --------------- | ------------------------------------- |
| `sessionPath` | 存储浏览器会话的目录 | `sessions` | — |
| `headless` | 在后台运行浏览器 | `false`(可见) | Docker 强制 `true` |
| `browserChannel` | `chromium`(内置补丁版,推荐)或 `msedge`(系统真实 Edge;⚠️ 实验性:Edge 152 + Playwright 1.6x 访问 rewards.bing.com 会崩溃,见 [playwright#41438](https://github.com/microsoft/playwright/issues/41438),修复后可重试) | `chromium` | `CONFIG_BROWSER_CHANNEL` |
| `clusters` | 并发账户集群数 | `1` | `CONFIG_CLUSTERS` |
| `errorDiagnostics` | 出错时保存诊断信息到 `diagnostics/` | `false` | `CONFIG_ERROR_DIAGNOSTICS` |
| `ensureStreakProtection` | 确保连击保护已开启 | `true` | `CONFIG_ENSURE_STREAK_PROTECTION` |
| `autoClaimPunchcardRewards` | 自动领取已完成的打卡奖励 | `false` | `CONFIG_AUTO_CLAIM_PUNCHCARD_REWARDS` |
| `skipNonPointTasks` | 跳过无积分奖励的任务 | `true` | `CONFIG_SKIP_NON_POINT_TASKS` |
| `prewarmDesktopLogin` | 移动端登录后立即完成桌面端登录并保存会话(需要手机批准的账户可把两次批准集中在运行开头),之后桌面阶段直接复用会话 | `false` | `CONFIG_PREWARM_DESKTOP_LOGIN` |
| `accountDelay.min` / `.max` | 下一账户开始前的延迟 | `1min` - `3min` | `CONFIG_ACCOUNT_DELAY_MIN` / `_MAX` |
| `searchOnBingLocalQueries` | ExploreOnBing 活动使用本地词库 | `false` | `CONFIG_SEARCH_ON_BING_LOCAL` |
| `globalTimeout` | 所有操作的超时时间 | `30sec` | `CONFIG_GLOBAL_TIMEOUT` |
| 设置 | 描述 | 默认值 | Docker 环境变量 |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ------------------------------------- |
| `sessionPath` | 存储浏览器会话的目录 | `sessions` | — |
| `headless` | 在后台运行浏览器 | `false`(可见) | Docker 强制 `true` |
| `browserChannel` | `chromium`(内置补丁版,推荐)或 `msedge`(系统真实 Edge;⚠️ 实验性:Edge 152 + Playwright 1.6x 访问 rewards.bing.com 会崩溃,见 [playwright#41438](https://github.com/microsoft/playwright/issues/41438),修复后可重试) | `chromium` | `CONFIG_BROWSER_CHANNEL` |
| `clusters` | 并发账户集群数 | `1` | `CONFIG_CLUSTERS` |
| `errorDiagnostics` | 出错时保存诊断信息到 `diagnostics/` | `false` | `CONFIG_ERROR_DIAGNOSTICS` |
| `ensureStreakProtection` | 确保连击保护已开启 | `true` | `CONFIG_ENSURE_STREAK_PROTECTION` |
| `autoClaimPunchcardRewards` | 自动领取已完成的打卡奖励 | `false` | `CONFIG_AUTO_CLAIM_PUNCHCARD_REWARDS` |
| `skipNonPointTasks` | 跳过无积分奖励的任务 | `true` | `CONFIG_SKIP_NON_POINT_TASKS` |
| `prewarmDesktopLogin` | 移动端登录后立即完成桌面端登录并保存会话(需要手机批准的账户可把两次批准集中在运行开头),之后桌面阶段直接复用会话 | `false` | `CONFIG_PREWARM_DESKTOP_LOGIN` |
| `accountDelay.min` / `.max` | 下一账户开始前的延迟 | `1min` - `3min` | `CONFIG_ACCOUNT_DELAY_MIN` / `_MAX` |
| `searchOnBingLocalQueries` | ExploreOnBing 活动使用本地词库 | `false` | `CONFIG_SEARCH_ON_BING_LOCAL` |
| `globalTimeout` | 所有操作的超时时间 | `30sec` | `CONFIG_GLOBAL_TIMEOUT` |

</details>

Expand Down Expand Up @@ -419,13 +419,13 @@ docker compose restart # 重启(不重建)

降低风控特征的运行策略集合。`humanize.enabled=false`(默认)时以下全部不生效,行为与原版一致。

| 设置 | 描述 | 默认值 | Docker 环境变量 |
| -------------------------------------------- | -------------------------------------------------------------------- | ----------------- | ----------------------------------------------------- |
| `humanize.enabled` | 总开关 | `false` | `CONFIG_HUMANIZE_ENABLED` |
| `humanize.skipWhenCompletedToday` | 当天已成功运行过(`logs/last-success.txt`)则再次启动时直接退出,配合早晚两次定时实现"兜底轮自动跳过" | `false` | `CONFIG_HUMANIZE_SKIP_WHEN_COMPLETED` |
| `humanize.quietHours` | 静默时段数组;运行中进入时段自动挂起到结束,启动时落在时段内也等待后再跑。支持 `start > end` 跨午夜 | `[]` | 仅 config.json |
| `humanize.searchTargetRatio.min` / `.max` | 每轮搜索目标完成度随机区间(移动/桌面各自抽取),保留分向下取整到 3 的倍数 | `0.85` - `1.0` | `CONFIG_HUMANIZE_SEARCH_TARGET_RATIO_MIN` / `_MAX` |
| `humanize.readToEarnArticles.min` / `.max` | Read-to-Earn 每轮随机阅读篇数上限区间 | `7` - `10` | `CONFIG_HUMANIZE_READ_TO_EARN_MIN` / `_MAX` |
| 设置 | 描述 | 默认值 | Docker 环境变量 |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------- | -------------- | -------------------------------------------------- |
| `humanize.enabled` | 总开关 | `false` | `CONFIG_HUMANIZE_ENABLED` |
| `humanize.skipWhenCompletedToday` | 当天已成功运行过(`logs/last-success.txt`)则再次启动时直接退出,配合早晚两次定时实现"兜底轮自动跳过" | `false` | `CONFIG_HUMANIZE_SKIP_WHEN_COMPLETED` |
| `humanize.quietHours` | 静默时段数组;运行中进入时段自动挂起到结束,启动时落在时段内也等待后再跑。支持 `start > end` 跨午夜 | `[]` | 仅 config.json |
| `humanize.searchTargetRatio.min` / `.max` | 每轮搜索目标完成度随机区间(移动/桌面各自抽取),保留分向下取整到 3 的倍数 | `0.85` - `1.0` | `CONFIG_HUMANIZE_SEARCH_TARGET_RATIO_MIN` / `_MAX` |
| `humanize.readToEarnArticles.min` / `.max` | Read-to-Earn 每轮随机阅读篇数上限区间 | `7` - `10` | `CONFIG_HUMANIZE_READ_TO_EARN_MIN` / `_MAX` |

`quietHours` 规则示例(days 支持全名或缩写,未列出的天不静默):

Expand Down
5 changes: 1 addition & 4 deletions config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,7 @@
"maxBonusSearches": 110,
"parallelSearching": false,
"clusterSearch": true,
"queryEngines": [
"china",
"local"
],
"queryEngines": ["china", "local"],
"searchResultVisitTime": "20sec",
"searchDelay": {
"min": "6min",
Expand Down
50 changes: 23 additions & 27 deletions scripts/api/logParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,13 @@ function ensureAccount(state, email) {
// inside the log messages, but the surrounding text is Chinese.
const RE = {
runStart: /^启动微软奖励脚本 \| v(\S+) \| 账户数: (\d+) \| 集群数: (\d+)/,
accountStart:
/^开始处理账户: (\S+) \| geoLocale: ([^|]+?)(?: \| locale: (\S+))?(?: \| 缓存区域: (\S+))?\s*$/,
earnable: /^今日可赚取 \| 移动端: (\d+) \| 浏览器: (\d+) \| App: (\d+) \| (\S+) \| locale: (\S+)\s*$/,
accountStart: /^开始处理账户: (\S+) \| geoLocale: ([^|]+?)(?: \| locale: (\S+))?(?: \| 缓存区域: (\S+))?\s*$/,
earnable:
/^今日可赚取 \| 移动端: (\d+|未提供|未知) \| 浏览器: (\d+|未提供|未知) \| App: (\d+|未提供|未知) \| (\S+) \| locale: (\S+)\s*$/,
searchSummary: /^搜索汇总 \| 移动端=(-?\d+) \| 桌面端=(-?\d+) \| 额外=(-?\d+) \| 总计=(-?\d+)/,
streakProtection:
/^快照完成 \| offers=(\d+) \| 可上报=(\d+) \| streaks=(\d+) \| 连续保护已启用=(true|false|null) \| 连续保护剩余天数=(\d+|null) \| 连续计数=(\d+|null) \| 等级=([^|]+) \| 账户=(\S+@\S+)$/,
accountEnd:
/^账户完成: (\S+) \| 获得积分=(-?\d+) \| 原余额=(\d+) \| 现余额=(\d+) \| 持续秒数=([\d.]+)/,
accountEnd: /^账户完成: (\S+) \| 获得积分=(-?\d+) \| 原余额=(\d+) \| 现余额=(\d+) \| 持续秒数=([\d.]+)/,
runEnd: /^全部账户完成 \| 处理账户数=(\d+) \| 获得积分=(-?\d+) \| 原余额=(\d+) \| 现余额=(\d+) \| 运行分钟数=([\d.]+)/,
accountError: /^(\S+@\S+) \| 错误=([\s\S]+)$/,
flowFailed: /(\S+@\S+) 的.*流程失败:/i,
Expand Down Expand Up @@ -142,32 +141,34 @@ function pointEventSource(title, message) {
case 'SEARCH-BONUS':
return message.startsWith('必应搜索完成') ? 'bonus' : null
case 'READ-TO-EARN':
return (message.startsWith('文章后的积分变化') ||
message.startsWith('未获得积分,停止读文赚积分') ||
message.startsWith('已阅读第') ||
message.startsWith('读文赚积分完成')) ? 'read' : null
return message.startsWith('文章后的积分变化') ||
message.startsWith('未获得积分,停止读文赚积分') ||
message.startsWith('已阅读第') ||
message.startsWith('读文赚积分完成')
? 'read'
: null
case 'DAILY-CHECK-IN':
return message.startsWith('每日签到完成') ? 'checkIn' : null
case 'CLAIM-BONUS-POINTS':
return (message.startsWith('领取奖励积分完成') || message.startsWith('没有可领取的积分'))
return message.startsWith('领取奖励积分完成') || message.startsWith('没有可领取的积分')
? 'claimBonus'
: null
case 'CLAIM-REWARD':
return message.startsWith('奖励已领取') ? 'claimReward' : null
case 'URL-REWARD':
return (message.startsWith('UrlReward 完成') || message.startsWith('UrlReward 未获得积分'))
return message.startsWith('UrlReward 完成') || message.startsWith('UrlReward 未获得积分')
? 'urlReward'
: null
case 'VISUAL-SEARCH':
return (message.startsWith('每日视觉搜索完成') || message.startsWith('每日视觉搜索已记录'))
return message.startsWith('每日视觉搜索完成') || message.startsWith('每日视觉搜索已记录')
? 'visualSearch'
: null
case 'APP-REWARD':
return (message.startsWith('AppReward 完成') || message.startsWith('AppReward 完成但未获得积分'))
return message.startsWith('AppReward 完成') || message.startsWith('AppReward 完成但未获得积分')
? 'appReward'
: null
case 'PUNCHCARD':
return (message.includes('COMPLETE') || message.includes('in progress')) ? 'punchcard' : null
return message.includes('COMPLETE') || message.includes('in progress') ? 'punchcard' : null
case 'SEARCH-ON-BING-SEARCH':
return message.startsWith('SearchOnBing 活动完成') ? 'searchOnBing' : null
default:
Expand Down Expand Up @@ -244,9 +245,7 @@ function applyEdgeBrowsing(state, entry) {
if (!account) return null

const message = entry.message ?? ''
const finalReports = message.startsWith('后台 Edge 浏览活动结束')
? numericField(message, 'reports')
: null
const finalReports = message.startsWith('后台 Edge 浏览活动结束') ? numericField(message, 'reports') : null
const progress =
fractionField(message, 'reportsCompleted') ??
fractionField(message, 'report') ??
Expand Down Expand Up @@ -303,16 +302,10 @@ function applyEdgeBrowsing(state, entry) {
previous.nextReportInSeconds = null
previous.estimatedRemainingMinutes = 0
previous.waitingForBackground = false
} else if (
message === '该账户无法使用 Edge 浏览连击(Browsing Streak)' ||
message.startsWith('跳过:')
) {
} else if (message === '该账户无法使用 Edge 浏览连击(Browsing Streak)' || message.startsWith('跳过:')) {
previous.status = 'skipped'
previous.waitingForBackground = false
} else if (
message.startsWith('后台 Edge 浏览活动失败') ||
message.startsWith('意外的后台任务失败')
) {
} else if (message.startsWith('后台 Edge 浏览活动失败') || message.startsWith('意外的后台任务失败')) {
previous.status = 'failed'
previous.waitingForBackground = false
} else if (message === '后台活动已取消') {
Expand Down Expand Up @@ -422,7 +415,10 @@ export function applyLogToRunState(state, entry) {
const email = m[4]
const acc = ensureAccount(state, email)
if (acc) {
acc.earnable = { mobile: Number(m[1]), browser: Number(m[2]), app: Number(m[3]) }
const [mobile, browser, app] = m
.slice(1, 4)
.map(value => (/^\d+$/.test(value) ? Number(value) : null))
acc.earnable = { mobile, browser, app }
acc.locale ??= m[5]
}
state.currentEmail = email
Expand Down Expand Up @@ -537,4 +533,4 @@ export function summarizeRunState(state) {
},
accounts
}
}
}
Loading