Skip to content

feat(FishNew): 新增钓鱼预期收益识别#244

Open
fangfengfen wants to merge 3 commits into
1bananachicken:devfrom
fangfengfen:fish_update
Open

feat(FishNew): 新增钓鱼预期收益识别#244
fangfengfen wants to merge 3 commits into
1bananachicken:devfrom
fangfengfen:fish_update

Conversation

@fangfengfen
Copy link
Copy Markdown

@fangfengfen fangfengfen commented May 29, 2026

概述

新增钓鱼预期收益识别功能,在钓鱼结果界面自动 OCR 识别鱼名,模糊匹配价格表累计贝壳收益,通过 focus 实时推送到前端。

改动内容

  • 新增 fish_catch_logger.py:OCR 识别鱼名 + 编辑距离模糊匹配 + 累计贝壳收益 + focus 推送
  • 新增 fish_price_table.json:鱼类价格表
  • 修改 FishNew.json pipeline:钓鱼结果节点接入 fish_catch_logger,任务结束调用 fish_catch_summary 汇总
  • 修改 __init__.py:注册新 action

效果

前端实时显示:第X条 XX鱼,累计预期收益XX贝壳

Summary by Sourcery

添加钓鱼结果日志动作:从结算界面 OCR 鱼名,与价格表匹配以估算贝壳收益,并将逐次捕获更新和最终汇总推送到前端。

New Features:

  • 引入 FishCatchLogger 自定义动作,用于从钓鱼结算界面 OCR 获取鱼名,与价格表进行模糊匹配,累积捕获数量和预估贝壳收益,并向前端发送实时通知。
  • 引入 FishCatchSummary 自定义动作,用于在任务结束时向前端报告总捕获数量和预估贝壳收益的汇总信息。
  • 新增鱼类价格表 JSON 资源,用于查询单条鱼的贝壳价值。

Enhancements:

  • 在自定义动作注册表中注册新的钓鱼收益日志动作,并将其集成到 FishNew 钓鱼流水线中,以支持逐次捕获日志记录和任务结束时的结果汇总。
Original summary in English

Summary by Sourcery

Add fishing result logging actions that OCR fish names from result screens, match them against a price table to estimate shell revenue, and push both per-catch updates and a final summary to the frontend.

New Features:

  • Introduce FishCatchLogger custom action to OCR fish names from fishing result screens, fuzzy-match them to a price table, accumulate catch counts and estimated shell revenue, and send real-time notifications to the frontend.
  • Introduce FishCatchSummary custom action to report an end-of-task summary of total catches and estimated shell revenue to the frontend.
  • Add a fish price table JSON resource used to look up per-fish shell values.

Enhancements:

  • Register the new fishing revenue logging actions in the custom action registry and integrate them into the FishNew fishing pipeline for per-catch logging and end-of-task summarization.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 29, 2026

Reviewer's Guide

添加钓鱼结果日志记录和收益估算功能:在结算界面通过 OCR 识别鱼名,将其与价格表进行模糊匹配来累积贝壳收益,通过 focus 向前端流式推送每次捕获的更新,并在任务结束时汇报最终汇总结果;该功能已接入 FishNew 流水线并注册为自定义动作。

单次捕获日志记录与 focus 推送的时序图

sequenceDiagram
    participant FishNewPipeline as FishNew_pipeline
    participant FishCatchLogger as FishCatchLogger
    participant Context as Context
    participant Controller as TaskController
    participant Frontend as Frontend

    FishNewPipeline->>FishCatchLogger: run(context, argv)
    FishCatchLogger->>FishCatchLogger: reset() [first call]
    FishCatchLogger->>Controller: post_screencap()
    Controller-->>FishCatchLogger: image
    FishCatchLogger->>Context: run_recognition(FishCatchLogger_OCR_FishName, image, pipeline_override)
    Context-->>FishCatchLogger: reco_detail
    FishCatchLogger->>FishCatchLogger: _fuzzy_match(ocr_text, price_table_keys)
    FishCatchLogger->>Context: override_pipeline(FishCatchLogger_Notify)
    FishCatchLogger->>Context: run_task(FishCatchLogger_Notify)
    Context-->>Frontend: focus Node.Action.Succeeded(content=msg)
    FishCatchLogger->>Controller: post_click_key(27)
    FishCatchLogger-->>FishNewPipeline: RunResult(success=True)
Loading

最终汇总上报的时序图

sequenceDiagram
    participant FishNewPipeline as FishNew_pipeline
    participant FishCatchSummary as FishCatchSummary
    participant FishCatchLogger as FishCatchLogger
    participant Context as Context
    participant Frontend as Frontend

    FishNewPipeline->>FishCatchSummary: run(context, argv)
    FishCatchSummary->>FishCatchLogger: get_summary()
    FishCatchLogger-->>FishCatchSummary: summary_text
    FishCatchSummary->>Context: override_pipeline(FishCatchSummary_Notify)
    FishCatchSummary->>Context: run_task(FishCatchSummary_Notify)
    Context-->>Frontend: focus Node.Action.Succeeded(content=summary_text)
    FishCatchSummary->>FishCatchLogger: reset()
    FishCatchSummary->>FishCatchLogger: _initialized = False
    FishCatchSummary-->>FishNewPipeline: RunResult(success=True)
Loading

文件级变更

Change Details Files
引入 FishCatchLogger 自定义动作,从结果界面 OCR 识别鱼名,与价格表进行模糊匹配,累积每次运行的统计数据,并将实时更新推送到前端。
  • 新增价格表路径解析逻辑,在两个基础目录之间进行回退查找,并从 JSON 中加载扁平化的 {fish_name: avg_price} 字典
  • 在模块内实现编辑距离和模糊匹配辅助函数,优先精确匹配,并通过最大编辑距离和长度差限制搜索范围
  • 实现 FishCatchLogger 自定义动作,在类级别维护总数量、总贝壳数以及按鱼种的计数器,每次运行初始化一次,并在第一次使用时重置
  • 截取钓鱼结果界面,在固定 ROI 上运行 OCR 提取鱼名,与价格表进行模糊匹配,并更新计数器和累计收益
  • 为每次捕获构建本地化日志消息,包括序号和累计预期贝壳数,通过动态覆写的仅 focus 流水线节点发送到前端,然后通过 Esc 键关闭结果界面
  • 提供 get_summary 和 reset 类方法,用于暴露聚合统计数据,并允许在不同运行之间从外部重置
agent/custom/action/AutoFish/fish_catch_logger.py
引入 FishCatchSummary 自定义动作,在任务结束时发送最终的钓鱼收益汇总报告,并重置状态以便下次运行。
  • 暴露一个自定义动作,从 FishCatchLogger.get_summary 读取聚合汇总信息
  • 通过临时的仅 focus 流水线节点将汇总文本推送到前端,并进行日志/气泡提示展示
  • 在上报完成后重置 FishCatchLogger 的内部状态和初始化标记,为下一次任务运行做好准备
agent/custom/action/AutoFish/fish_catch_logger.py
注册新的自定义动作,使其可以在流水线中被引用。
  • 将 FishCatchLogger 和 FishCatchSummary 添加到导出的动作列表中,使其能被 agent 框架发现
agent/custom/action/__init__.py
将新的日志和汇总动作接入 FishNew 流水线,使每次捕获以及最终结果都能被上报。
  • 将 fish_catch_logger 挂载到钓鱼结果节点上,使其在每个捕获结果界面运行
  • 在 FishNew 任务结束时调用 fish_catch_summary,以输出最终的聚合报告并重置状态
assets/resource/base/pipeline/Fish/FishNew.json
添加用于估算每条鱼贝壳收益的鱼价表。
  • 引入一个 JSON 价格表,将鱼名映射到数值或带有 avg 字段的对象,并在加载时规范化为扁平的“名称到平均值”字典
  • 将价格表放置在基础资源路径下,以便 AutoFish 自定义动作可以通过配置的搜索路径访问
assets/resource/base/fish_price_table.json

Tips and commands

Interacting with Sourcery

  • 触发新一次 review: 在 pull request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的 review 评论。
  • 从 review 评论生成 GitHub issue: 在某条 review 评论下回复,要求 Sourcery 从该评论创建 issue。你也可以直接回复 @sourcery-ai issue 来从该评论创建 issue。
  • 生成 pull request 标题: 在 pull request 标题中任意位置写上 @sourcery-ai 即可随时生成标题。你也可以在 pull request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 pull request 摘要: 在 pull request 正文中任意位置写上 @sourcery-ai summary,即可在对应位置随时生成 PR 摘要。你也可以在 pull request 中评论 @sourcery-ai summary 来(重新)生成摘要。
  • 生成 reviewer's guide: 在 pull request 中评论 @sourcery-ai guide,即可随时(重新)生成 reviewer's guide。
  • 一次性解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve,即可标记解决所有 Sourcery 评论。如果你已经处理完所有评论且不想再看到它们,这会非常有用。
  • 清除所有 Sourcery review: 在 pull request 中评论 @sourcery-ai dismiss,即可清除所有现有的 Sourcery review。若你希望以一次全新的 review 开始,这尤其有用——别忘了再评论一次 @sourcery-ai review 来触发新的 review!

Customizing Your Experience

前往你的 dashboard 以:

  • 启用或禁用诸如 Sourcery 自动生成的 pull request 摘要、reviewer's guide 等 review 功能。
  • 更改 review 语言。
  • 添加、删除或编辑自定义 review 指南。
  • 调整其他 review 设置。

Getting Help

Original review guide in English

Reviewer's Guide

Adds a fishing result logging and revenue estimation feature: OCRs fish names on the result screen, fuzzy-matches them to a price table to accumulate shell revenue, streams per-catch updates to the frontend via focus, and reports a final summary at task end, wired into the FishNew pipeline and registered as custom actions.

Sequence diagram for per-catch logging and focus push

sequenceDiagram
    participant FishNewPipeline as FishNew_pipeline
    participant FishCatchLogger as FishCatchLogger
    participant Context as Context
    participant Controller as TaskController
    participant Frontend as Frontend

    FishNewPipeline->>FishCatchLogger: run(context, argv)
    FishCatchLogger->>FishCatchLogger: reset() [first call]
    FishCatchLogger->>Controller: post_screencap()
    Controller-->>FishCatchLogger: image
    FishCatchLogger->>Context: run_recognition(FishCatchLogger_OCR_FishName, image, pipeline_override)
    Context-->>FishCatchLogger: reco_detail
    FishCatchLogger->>FishCatchLogger: _fuzzy_match(ocr_text, price_table_keys)
    FishCatchLogger->>Context: override_pipeline(FishCatchLogger_Notify)
    FishCatchLogger->>Context: run_task(FishCatchLogger_Notify)
    Context-->>Frontend: focus Node.Action.Succeeded(content=msg)
    FishCatchLogger->>Controller: post_click_key(27)
    FishCatchLogger-->>FishNewPipeline: RunResult(success=True)
Loading

Sequence diagram for final summary reporting

sequenceDiagram
    participant FishNewPipeline as FishNew_pipeline
    participant FishCatchSummary as FishCatchSummary
    participant FishCatchLogger as FishCatchLogger
    participant Context as Context
    participant Frontend as Frontend

    FishNewPipeline->>FishCatchSummary: run(context, argv)
    FishCatchSummary->>FishCatchLogger: get_summary()
    FishCatchLogger-->>FishCatchSummary: summary_text
    FishCatchSummary->>Context: override_pipeline(FishCatchSummary_Notify)
    FishCatchSummary->>Context: run_task(FishCatchSummary_Notify)
    Context-->>Frontend: focus Node.Action.Succeeded(content=summary_text)
    FishCatchSummary->>FishCatchLogger: reset()
    FishCatchSummary->>FishCatchLogger: _initialized = False
    FishCatchSummary-->>FishNewPipeline: RunResult(success=True)
Loading

File-Level Changes

Change Details Files
Introduce FishCatchLogger custom action to OCR fish names from the result screen, fuzzy-match them to a price table, accumulate per-run stats, and push real-time updates to the frontend.
  • Add price table path resolution with fallback between two base directories and load a flattened {fish_name: avg_price} dict from JSON
  • Implement an in-module edit distance and fuzzy match helper that prioritizes exact matches and bounds search by max edit distance and length difference
  • Implement FishCatchLogger custom action with class-level counters for total count, total shells, and per-fish counts, initialized once per run and reset on first use
  • Capture the fishing result screen, run OCR over a fixed ROI to extract the fish name, fuzzy match against the price table, and update counters and cumulative revenue
  • Build a localized log message per catch, including index and cumulative expected shells, and send it to frontend using a dynamically overridden focus-only pipeline node, then close the result screen via Esc key
  • Provide get_summary and reset classmethods to expose aggregated stats and allow external reset between runs
agent/custom/action/AutoFish/fish_catch_logger.py
Introduce FishCatchSummary custom action to send a final summarized fishing revenue report at the end of the task and reset state for the next run.
  • Expose a custom action that reads the aggregate summary from FishCatchLogger.get_summary
  • Push the summary text to the frontend via a temporary focus-only pipeline node with log/toast display
  • Reset FishCatchLogger internal state and initialization flag after reporting to prepare for the next task run
agent/custom/action/AutoFish/fish_catch_logger.py
Register the new custom actions so they can be referenced from pipelines.
  • Add FishCatchLogger and FishCatchSummary to the exported action list so they are discoverable by the agent framework
agent/custom/action/__init__.py
Wire the new logging and summary actions into the FishNew pipeline so each catch and the final result are reported.
  • Attach fish_catch_logger to the fishing result node so it runs on each catch result screen
  • Invoke fish_catch_summary at the end of the FishNew task to emit a final aggregate report and reset state
assets/resource/base/pipeline/Fish/FishNew.json
Add a fish price table used to estimate shell revenue for each fish.
  • Introduce a JSON price table mapping fish names to either numeric values or objects with an avg field, to be normalized by the loader into a flat name-to-average dict
  • Place the price table under the base assets path so it is accessible from the AutoFish custom action via the configured search paths
assets/resource/base/fish_price_table.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了两个问题,并给出了一些整体性的反馈:

  • 在 FishCatchLogger 中使用类级别的可变状态(例如 _total_count_catch_log_initialized)使得逻辑实际上是全局的;建议将这些计数器与任务/会话 ID 或实例状态绑定,以避免跨任务相互影响以及潜在的并发问题。
  • fish_catch_loggerfish_catch_summary 在错误报告时都直接使用 print;如果项目中已经有日志系统,建议通过日志器输出这些错误,以便在生产环境中更容易监控和过滤。
  • _recognize_fish_name 中,OCR 的 ROI 和 pipeline 的 override 是硬编码的;如果后续会有不同的分辨率或 UI 变体,建议从配置或 JSON pipeline 中读取这些设置,这样在布局调整时就不需要修改 Python 代码。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The use of class-level mutable state in FishCatchLogger (e.g., _total_count, _catch_log, _initialized) makes the logic effectively global; consider tying the counters to a task/session identifier or instance state to avoid cross-task interference and potential concurrency issues.
- Both fish_catch_logger and fish_catch_summary use bare `print` for error reporting; if the project has a logging facility, routing these errors through the logger would make them easier to monitor and filter in production.
- In _recognize_fish_name, the OCR ROI and pipeline override are hardcoded; if different resolutions or UI variants are expected, consider reading these settings from configuration or the JSON pipeline to avoid having to change Python code for layout tweaks.

## Individual Comments

### Comment 1
<location path="agent/custom/action/AutoFish/fish_catch_logger.py" line_range="134" />
<code_context>
+
+        return CustomAction.RunResult(success=True)
+
+    def _recognize_fish_name(self, context: Context, image) -> str:
+        """OCR 识别鱼名,并模糊匹配到价格表"""
+        reco_detail = context.run_recognition(
</code_context>
<issue_to_address>
**issue:** The return type annotation should include `None` to match the actual return values.

This method returns `None` in some cases (e.g. empty OCR text or no fuzzy match), but its signature is `-> str`. Update the annotation to `-> str | None` (or `Optional[str]`) so it accurately reflects the possible return values for type checkers and IDEs.
</issue_to_address>

### Comment 2
<location path="agent/custom/action/AutoFish/fish_catch_logger.py" line_range="18-20" />
<code_context>
+
+
+# 价格表路径
+_PRICE_TABLE_PATH = Path(__file__).parents[4] / "assets" / "resource" / "base" / "fish_price_table.json"
+if not _PRICE_TABLE_PATH.exists():
+    _PRICE_TABLE_PATH = Path(__file__).parents[4] / "resource" / "base" / "fish_price_table.json"
+
+# 加载价格表
</code_context>
<issue_to_address>
**suggestion:** When neither price table path exists, the code fails silently and uses an empty table.

If both paths are missing, `_load_price_table` returns `{}` without any signal, so all prices default to 0 and configuration/deployment issues are easy to miss. Please add a log or warning when no price table file is found so such misconfigurations are visible.

Suggested implementation:

```python
import json
import logging
from pathlib import Path

```

```python
def _load_price_table() -> dict:
    """加载价格表,返回 {鱼名: avg价格} 的扁平dict"""
    if not _PRICE_TABLE_PATH.exists():
        logging.warning(
            "Fish price table file not found at expected locations, using empty price table. "
            "Please check deployment configuration."
        )
        return {}

    with open(_PRICE_TABLE_PATH, "r", encoding="utf-8") as f:
        raw = json.load(f)
    result = {}
    for name, val in raw.items():
        if isinstance(val, dict):
            result[name] = val.get("avg", 0)
        else:

```
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得这次评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的代码评审。
Original comment in English

Hey - I've found 2 issues, and left some high level feedback:

  • The use of class-level mutable state in FishCatchLogger (e.g., _total_count, _catch_log, _initialized) makes the logic effectively global; consider tying the counters to a task/session identifier or instance state to avoid cross-task interference and potential concurrency issues.
  • Both fish_catch_logger and fish_catch_summary use bare print for error reporting; if the project has a logging facility, routing these errors through the logger would make them easier to monitor and filter in production.
  • In _recognize_fish_name, the OCR ROI and pipeline override are hardcoded; if different resolutions or UI variants are expected, consider reading these settings from configuration or the JSON pipeline to avoid having to change Python code for layout tweaks.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The use of class-level mutable state in FishCatchLogger (e.g., _total_count, _catch_log, _initialized) makes the logic effectively global; consider tying the counters to a task/session identifier or instance state to avoid cross-task interference and potential concurrency issues.
- Both fish_catch_logger and fish_catch_summary use bare `print` for error reporting; if the project has a logging facility, routing these errors through the logger would make them easier to monitor and filter in production.
- In _recognize_fish_name, the OCR ROI and pipeline override are hardcoded; if different resolutions or UI variants are expected, consider reading these settings from configuration or the JSON pipeline to avoid having to change Python code for layout tweaks.

## Individual Comments

### Comment 1
<location path="agent/custom/action/AutoFish/fish_catch_logger.py" line_range="134" />
<code_context>
+
+        return CustomAction.RunResult(success=True)
+
+    def _recognize_fish_name(self, context: Context, image) -> str:
+        """OCR 识别鱼名,并模糊匹配到价格表"""
+        reco_detail = context.run_recognition(
</code_context>
<issue_to_address>
**issue:** The return type annotation should include `None` to match the actual return values.

This method returns `None` in some cases (e.g. empty OCR text or no fuzzy match), but its signature is `-> str`. Update the annotation to `-> str | None` (or `Optional[str]`) so it accurately reflects the possible return values for type checkers and IDEs.
</issue_to_address>

### Comment 2
<location path="agent/custom/action/AutoFish/fish_catch_logger.py" line_range="18-20" />
<code_context>
+
+
+# 价格表路径
+_PRICE_TABLE_PATH = Path(__file__).parents[4] / "assets" / "resource" / "base" / "fish_price_table.json"
+if not _PRICE_TABLE_PATH.exists():
+    _PRICE_TABLE_PATH = Path(__file__).parents[4] / "resource" / "base" / "fish_price_table.json"
+
+# 加载价格表
</code_context>
<issue_to_address>
**suggestion:** When neither price table path exists, the code fails silently and uses an empty table.

If both paths are missing, `_load_price_table` returns `{}` without any signal, so all prices default to 0 and configuration/deployment issues are easy to miss. Please add a log or warning when no price table file is found so such misconfigurations are visible.

Suggested implementation:

```python
import json
import logging
from pathlib import Path

```

```python
def _load_price_table() -> dict:
    """加载价格表,返回 {鱼名: avg价格} 的扁平dict"""
    if not _PRICE_TABLE_PATH.exists():
        logging.warning(
            "Fish price table file not found at expected locations, using empty price table. "
            "Please check deployment configuration."
        )
        return {}

    with open(_PRICE_TABLE_PATH, "r", encoding="utf-8") as f:
        raw = json.load(f)
    result = {}
    for name, val in raw.items():
        if isinstance(val, dict):
            result[name] = val.get("avg", 0)
        else:

```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.


return CustomAction.RunResult(success=True)

def _recognize_fish_name(self, context: Context, image) -> str:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: 返回类型注解应包含 None,以匹配实际返回值。

该方法在某些情况下会返回 None(例如 OCR 文本为空或模糊匹配失败),但当前函数签名为 -> str。请将注解更新为 -> str | None(或 Optional[str]),以便类型检查器和 IDE 能够准确反映所有可能的返回值。

Original comment in English

issue: The return type annotation should include None to match the actual return values.

This method returns None in some cases (e.g. empty OCR text or no fuzzy match), but its signature is -> str. Update the annotation to -> str | None (or Optional[str]) so it accurately reflects the possible return values for type checkers and IDEs.

Comment thread agent/custom/action/AutoFish/fish_catch_logger.py
@EeeMaoY
Copy link
Copy Markdown
Collaborator

EeeMaoY commented May 31, 2026

  1. 不要用print打印日志消息,使用agent\utils\logger.py内已有的日志方式
  2. 使用agent\utils\maafocus.py内的工具输出用户消息,可选完成国际化

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants