Skip to content

Commit e5995e2

Browse files
authored
fix(MaaEnd): 修复新版自动采集失败明细漏采 (#1788)
## 摘要 - 适配新版“物品名采集失败”日志,修复自动采集失败通知缺少物品明细的问题,同时兼容旧版路线编号前缀。 - 清理日志中的富文本标签,补充新旧格式、通知开关及重试归属的回归测试。 ## 验证 - 新版 28 条采集失败文案全部匹配,相关 68 项测试及 Ruff 检查通过。 - 回放作者本机实跑日志,正确生成“❌ 失败: 自动采集(协议纹石)”,重试后未重复列出明细。 - 测试提交:`b99338451`;环境:Windows、Python 3.12.10。未在项目指定的 Python 3.12.13 环境复验,未运行 `@real` E2E harness。 ```text uv run --no-sync python -m pytest tests/task/test_maaend_log_details.py tests/task/test_log_box_adapter_sessions.py tests/task/test_log_capture_lifecycle.py tests/utils/test_log_box_pipeline.py -q ``` ## Sourcery 摘要 修复自动采集失败通知中的物品明细采集问题,并兼容不同版本的日志格式。 错误修复: - 修复新版自动采集失败日志未包含物品明细,导致失败通知内容不完整的问题。 功能增强: - 兼容新旧采集失败日志格式,并在提取失败明细前清理富文本标签。 测试: - 补充针对新旧日志格式、通知开关、任务重试和归属边界的回归测试。 <details> <summary>Original summary in English</summary> ## Sourcery 摘要 修复自动采集失败通知中的物品明细收集,并提升对不同版本日志格式的兼容性。 错误修复: - 修复新版自动采集失败日志未能提取物品明细,导致失败通知内容不完整的问题。 功能增强: - 兼容新旧自动采集失败日志格式,并清理富文本标签,以确保正确识别明细。 测试: - 补充回归测试,覆盖日志格式、通知开关,以及任务和重试的边界情况。 <details> <summary>Original summary in English</summary> ## Summary by Sourcery 修复自动采集失败通知中的物品明细收集,并提升不同版本日志格式的兼容性。 Bug Fixes: - 修复新版自动采集失败日志未能提取物品明细,导致失败通知内容不完整的问题。 Enhancements: - 兼容新旧自动采集失败日志格式,并清理富文本标签以确保明细正确识别。 Tests: - 补充覆盖日志格式、通知开关以及任务和重试边界的回归测试。 </details> </details>
1 parent 83459ac commit e5995e2

4 files changed

Lines changed: 91 additions & 6 deletions

File tree

‎app/task/MaaEnd/AutoProxy.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
from app.utils.common.log import sources as log_source
8080

8181
from .inventory_report import INVENTORY_REPORT_RULE, InventoryReport, inventory_resolve
82-
from .push_log import MAAEND_PUSH_RULES, maaend_resolve
82+
from .push_log import MAAEND_PUSH_RULES, maaend_plain_text, maaend_resolve
8383
from .resource_loader import (
8484
MaaEndResourceLoader,
8585
get_loaded_maaend_options,
@@ -3391,6 +3391,7 @@ async def _append_stage_log_details(self, paths: list[Path]) -> None:
33913391

33923392
def read() -> list[log_box.LogEntry]:
33933393
pipeline = log_box.LogPipeline()
3394+
pipeline.preprocessors.append(maaend_plain_text)
33943395
for rule in MAAEND_PUSH_RULES:
33953396
pipeline.add_rule(*rule)
33963397
pipeline.postprocessors.append(maaend_resolve)

‎app/task/MaaEnd/push_log.py‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
- ``[时间戳] 任务完成: <任务名>``:明确成功。
3838
- ``[时间戳] 任务失败: <任务名>``:明确失败(含账号切换失败等)。
3939
- 失败明细行(出现在任务「开始」与「终态」之间,产出 ``DETAIL:`` 中间标记):
40-
- ``[时间戳] 线路/路线N:<采集物>采集失败``:采集任务的路线失败明细
41-
(前缀有「线路/路线」两种,实测混用);
40+
- ``[时间戳] <采集物>采集失败``:新版采集失败明细;兼容旧版
41+
``线路/路线N:`` 前缀,匹配前清理上游富文本标签;
4242
- ``[时间戳] 🚨发生严重错误,已停止任务(原因)``:自动囤货致命中断
4343
(原因枚举见上游 ``locales/go-service/zh_cn.json`` 的 abort 表);
4444
- ``[时间戳] ⚠️ 寻路或寻找干员失败``:送礼任务寻路失败(上游固定文案)。
@@ -73,6 +73,7 @@
7373
import re
7474
from contextlib import suppress
7575
from datetime import datetime
76+
from html import unescape
7677

7778
from app.utils import LOG_TYPE_NORMAL
7879

@@ -99,10 +100,10 @@
99100
),
100101
# ── 失败明细行(DETAIL 标记):归因到当前开始标记的任务,仅任务最终
101102
# 失败时折叠进失败行;行内时间戳随终态行即可,不单独提取 ──
102-
# 采集路线失败(前缀「线路/路线」实测混用):仅采集物名
103+
# 新版只输出采集物名,旧版还带「线路/路线」编号前缀。
103104
(
104-
r"^\[([^\]]+)\]\s*(?:线路|路线)\s*\d+\s*:\s*.+?采集失败",
105-
r'"DETAIL:" + $((?:^\[)(?:[^\]]+)(?:\]\s*(?:线路|路线)\s*\d+\s*:\s*)(.+?)(?:采集失败))',
105+
r"^\[([^\]]+)\]\s*(?:(?:线路|路线)\s*\d+\s*[::]\s*)?.+?采集失败\s*$",
106+
r'"DETAIL:" + $((?:^\[)(?:[^\]]+)(?:\]\s*)(?:(?:线路|路线)\s*\d+\s*[::]\s*)?(.+?)(?:采集失败\s*$))',
106107
),
107108
# 自动囤货致命中断:仅中断原因(🚨 行后必跟「任务失败」)
108109
(
@@ -134,6 +135,11 @@
134135
_LINE_TS_FORMAT = "%Y-%m-%d %H:%M:%S.%f"
135136

136137

138+
def maaend_plain_text(line: str) -> str:
139+
"""将上游用户可见富文本还原为纯文本,保留时间戳。"""
140+
return re.sub(r"<[^>]*>", "", unescape(line))
141+
142+
137143
# ── 节点名 emoji 清理 ────────────────────────────────────────────────────────
138144
# MaaEnd 任务名自带图标(🤝拜访好友、🎱基质刷取、❌关闭游戏(PC) 等),与推送
139145
# 报告的状态标记(✅ 成功/❌ 失败)混排时视觉杂乱,且「❌关闭游戏」这类名字
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
project: end
2+
修复新版自动采集失败时通知缺少物品明细的问题
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""MaaEnd 新旧日志格式均应进入当前用户的任务报告。"""
2+
3+
import asyncio
4+
from types import SimpleNamespace
5+
6+
import pytest
7+
8+
from app.task.MaaEnd.AutoProxy import AutoProxyTask
9+
10+
11+
@pytest.mark.parametrize(
12+
"detail",
13+
[
14+
"线路1:岩天使叶采集失败",
15+
"路线 2:岩天使叶采集失败",
16+
"岩天使叶采集失败",
17+
'<span style="color: red; font-weight: bold;">岩天使叶采集失败</span>',
18+
"&lt;span style=&quot;color: red; font-weight: bold;&quot;&gt;岩天使叶采集失败&lt;/span&gt;",
19+
],
20+
)
21+
@pytest.mark.parametrize("mode", ["逐条", "汇总", "关闭"])
22+
def test_collection_failure_reaches_user_report(tmp_path, detail, mode):
23+
proxy = AutoProxyTask.__new__(AutoProxyTask)
24+
proxy.cur_user_item = SimpleNamespace(push_log=[], push_log_mode=mode)
25+
proxy.push_log_enabled = mode != "关闭"
26+
path = tmp_path / "run.log"
27+
path.write_text(
28+
"[2026-11-02 10:13:00.000] 任务开始: 🧺自动采集\n"
29+
f"[2026-11-02 10:14:00.000] {detail}\n"
30+
"[2026-11-02 10:15:00.000] 血菌、武陵石采集失败\n"
31+
"[2026-11-02 10:16:00.000] 种植食材箱(路线1)采集失败\n"
32+
"[2026-11-02 10:17:00.000] 任务失败: 🧺自动采集\n",
33+
encoding="utf-8",
34+
)
35+
asyncio.run(proxy._append_stage_log_details([path]))
36+
assert [entry[1] for entry in proxy.cur_user_item.push_log] == (
37+
[]
38+
if mode == "关闭"
39+
else ["❌ 失败: 自动采集(岩天使叶、血菌、武陵石、种植食材箱(路线1))"]
40+
)
41+
42+
43+
def test_details_do_not_cross_task_or_retry_boundaries(tmp_path):
44+
proxy = AutoProxyTask.__new__(AutoProxyTask)
45+
proxy.cur_user_item = SimpleNamespace(push_log=[], push_log_mode="汇总")
46+
proxy.push_log_enabled = True
47+
path = tmp_path / "run.log"
48+
messages = [
49+
"无主采集失败",
50+
"任务开始: 自动采集",
51+
"岩天使叶采集失败",
52+
"任务失败: 自动采集",
53+
"任务开始: 自动采集",
54+
"任务完成: 自动采集",
55+
"迟到采集失败",
56+
"任务开始: 自动囤货",
57+
"🚨发生严重错误,已停止任务(商品识别结果不可用)",
58+
"任务失败: 自动囤货",
59+
"任务开始: 送礼",
60+
"⚠️ 寻路或寻找干员失败,尝试重试...",
61+
"任务失败: 送礼",
62+
"任务开始: 拜访好友",
63+
"现在游戏时间是周一,根据执行周期跳过任务",
64+
"任务完成: 拜访好友",
65+
]
66+
path.write_text(
67+
"".join(f"[2026-11-02 10:13:00.000] {text}\n" for text in messages),
68+
encoding="utf-8",
69+
)
70+
asyncio.run(proxy._append_stage_log_details([path]))
71+
assert [entry[1] for entry in proxy.cur_user_item.push_log] == [
72+
"✅ 成功: 自动采集",
73+
"❌ 失败: 自动囤货(商品识别结果不可用)",
74+
"❌ 失败: 送礼(寻路或寻找干员失败)",
75+
"⏭ 跳过: 拜访好友",
76+
]

0 commit comments

Comments
 (0)