Skip to content

feat(os): 大世界购买行动力双模式及紧急调度增强 - #777

Closed
changqing81 wants to merge 1 commit into
wess09:devfrom
changqing81:squash-buy-ap
Closed

feat(os): 大世界购买行动力双模式及紧急调度增强#777
changqing81 wants to merge 1 commit into
wess09:devfrom
changqing81:squash-buy-ap

Conversation

@changqing81

@changqing81 changqing81 commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

📋 PR 概览

本 PR 将 16 个提交合并为单个提交,专注于 大世界(OS)智能调度+购买行动力双模式 及关联修复。

统计项 数值
合并提交数 16
变更文件数 15
新增行数 2,257
删除行数 1,115

一、版本对比

对比项 旧版 新版
行动力购买模式 单一 BuyActionPointLimit,按购买次数上限控制 双模式:BuyActionPointLimit(次数限制)+ BuyActionPointUpperThreshold(上限阈值),通过 BuyActionPointMode 切换
购买时机判断 使用上限阈值判断 模式1使用下限阈值决定购买时机;模式2仅使用上限阈值
OCR 识别可靠性 可能读到药箱数量,错误覆盖计数器 购买前强制选中石油按钮(action_point_set_button(0));新增 OCR 有效性校验
购买后 AP 数据 使用过期本地变量 购买后重新 OCR 获取最新 AP(缓存值)
BuyActionPointLimit 残留 临时覆盖后未恢复,导致后续误判"已满" 检测到残留值 ≤0 时,按游戏上限 5 次自动恢复
智能调度弹窗 高频弹窗易触发 TooManyClickError 智能调度上下文中跳过搜敌前的冗余弹窗刷新,耄耋相接跳过重复 action_point_set
月末购买 无处理 月末封锁周自动跳过本周购买
推送通知 行动力阈值推送(30 分钟最小间隔),支持 OnePush + 启动器双通道
daily_checked 类型 list 无注解,append(n) 类型不安全 list[int] 注解 + append(int(n))
GUI 翻译 fallback dic_lang["ja-JP"][key] = dic_lang["en-US"][key] 直接取值,key 缺失时 KeyError 崩溃 en-US.get(key) 安全回退

二、功能新增

2.1 购买行动力双模式(BuyActionPointMode

项目 内容
功能描述 OpsiScheduling 中新增购买行动力模式配置,用户可选择 off(关闭)、hazard1_leveling(侵蚀1练级模式)、meowfficer_farming(耄耋相接模式)。
实现方式 CoinTaskMixin 中新增状态管理:STATE_KEY_BUY_AP_COUNT(本周已购次数)、STATE_KEY_BUY_AP_WEEK_ID(ISO 周标识),通过 BuyActionPointUpperThreshold / BuyActionPointLowerThreshold 控制阈值。
预期效果 用户可按不同任务类型精细化控制购买策略,避免行动力溢出或石油浪费。

2.2 月末行动力清理(MonthEndActionPointCleanupEnable

项目 内容
功能描述 在月末前自动检测是否处于"月末封锁周",自动跳过本周购买。
实现方式 action_point.py_is_in_month_end_purchase_block_week():基于服务器时间计算当前自然周与下月首周的重合关系。
预期效果 月末前一周不再浪费石油购买行动力,避免溢出。

2.3 推送通知增强

项目 内容
功能描述 行动力阈值变化时自动推送通知,支持 OnePush + 启动器双通道,30 分钟最小间隔防重复。
实现方式 CoinTaskMixin.notify_push():校验推送配置有效性(_is_push_config_valid),格式化启动器通知文案(_format_launcher_notification,喵~风格),发送成功后记录时间戳。

三、问题修复

# 问题 根因 修复文件 修复方案
1 购买行动力 OCR 读到药箱数量,错误覆盖计数器 OCR 区域未限定为石油按钮 scheduling.py 购买前执行 action_point_set_button(0) 选中石油
2 OCR 结果无效(斜杠 / 误识别为 1 缺少 OCR 有效性校验 scheduling.py 新增 _is_buy_action_point_ocr_valid 校验逻辑
3 server 未导入导致 ModuleNotFoundError 缺少 import module.config.server scheduling.py 添加导入语句
4 BuyActionPointLimit 临时覆盖残留导致错误延迟 覆盖后未恢复,后续判断"已满" action_point.py 检测到 buy_limit <= 0 时按游戏上限 5 次恢复
5 action_point_buyBuyActionPointLimit 冲突 同上覆盖残留 action_point.py 同上
6 买行动力模式使用上限阈值判断(应为下限) 逻辑反向 scheduling.py 模式1改用 BuyActionPointLowerThreshold
7 行动力弹窗频率过高触发 TooManyClickError 智能调度+ 中每次循环都刷新弹窗 scheduling.py 减少弹窗触发频率,增加间隔控制
8 智能调度下搜敌前冗余弹窗刷新 外层已查询行动力,内层重复查询 map.py + meowfficer_farming.py map.py:检测到 _smart_scheduling_context 时跳过;meowfficer_farming.py:仅在非智能调度上下文时调用 action_point_set
9 购买后使用过期本地变量中的 AP 值 未重新获取 scheduling.py 购买后使用缓存的 AP 值
10 BuyActionPointUpperThreshold 未生效 逻辑同时检查 Limit 和 UpperThreshold scheduling.py 模式2仅使用 UpperThreshold
11 daily_checked 类型不一致 无类型注解,append(n) 可能存非 int daily.py daily_checked: list[int] + append(int(n))
12 GUI 启动时 KeyError 崩溃 en-US 直接 [] 取值,key 缺失抛出异常 lang.py 改为 en-US.get(key) 安全回退

四、实现说明

4.1 变更文件一览

文件 变更类型 行数变化 说明
module/os/tasks/scheduling.py 修改 +693 核心:双模式、推送、月末清理、OCR 校验、11 处修复
module/os_handler/action_point.py 修改 +6 BuyActionPointLimit 残留恢复逻辑
module/os/map.py 修改 +7 智能调度上下文跳过冗余弹窗
module/os/tasks/meowfficer_farming.py 修改 +4-1 智能调度上下文跳过重复 action_point_set
module/daily/daily.py 修改 +4-2 daily_checked 类型注解
module/webui/lang.py 修改 +4-1 en-US.get(key) 安全回退
config/template.json 修改 配置模板更新
module/config/argument/argument.yaml 修改 新增配置项定义
module/config/argument/args.json 修改 配置定义生成
module/config/config_generated.py 修改 配置类生成
module/config/i18n/*.json (×5) 修改 五种语言翻译

4.2 双模式决策逻辑

配置常量CoinTaskMixin):

STATE_KEY_BUY_AP_COUNT = 'BuyActionPointCount'       # 本周已购买次数
STATE_KEY_BUY_AP_WEEK_ID = 'BuyActionPointWeekId'    # ISO 周标识 "2026-W32"
BUY_AP_MODE_OFF = 'off'
BUY_AP_MODE_HAZARD1 = 'hazard1_leveling'
BUY_AP_MODE_MEOWFFICER = 'meowfficer_farming'
CONFIG_PATH_BUY_AP_UPPER = 'OpsiScheduling.OpsiScheduling.BuyActionPointUpperThreshold'
CONFIG_PATH_BUY_AP_LOWER = 'OpsiScheduling.OpsiScheduling.BuyActionPointLowerThreshold'

模式读取OpsiScheduling):

def _get_buy_action_point_mode(self):
    return self.config.cross_get(
        keys=self.CONFIG_PATH_BUY_AP_MODE,
        default=self.BUY_AP_MODE_OFF,
    )

def _is_buy_action_point_hazard1_mode(self):
    return self._get_buy_action_point_mode() == self.BUY_AP_MODE_HAZARD1

def _is_buy_action_point_meowfficer_mode(self):
    return self._get_buy_action_point_mode() == self.BUY_AP_MODE_MEOWFFICER

阈值读取(含容错,避免非法配置值):

def _get_buy_action_point_upper_threshold(self):
    """上限阈值(仅功能2使用),默认 200"""
    value = self.config.cross_get(keys=self.CONFIG_PATH_BUY_AP_UPPER, default=200)
    try:
        return max(1, int(value or 200))
    except (TypeError, ValueError):
        return 200

def _get_buy_action_point_lower_threshold(self):
    """下限阈值(仅功能1使用),默认 100"""
    value = self.config.cross_get(keys=self.CONFIG_PATH_BUY_AP_LOWER, default=100)
    try:
        return max(0, int(value or 100))
    except (TypeError, ValueError):
        return 100

计数器跨周自动重置(基于服务器时间 ISO 周标识):

def _get_current_purchase_week_id(self):
    """基于服务器时间获取 ISO 周标识,如 "2026-W32" """
    diff = server_time_offset()
    server_now = current_time() - diff
    iso_year, iso_week, _ = server_now.isocalendar()
    return f'{iso_year}-W{iso_week:02d}'

def _reset_buy_action_point_count_if_new_week(self):
    """跨周检测:比较持久化的周标识与当前周,不一致时重置计数器"""
    current_week_id = self._get_current_purchase_week_id()
    stored_week_id = self._get_smart_scheduling_state_value(
        self.STATE_KEY_BUY_AP_WEEK_ID,
    )
    if stored_week_id != current_week_id:
        self._set_smart_scheduling_state_value(self.STATE_KEY_BUY_AP_COUNT, 0)
        self._set_smart_scheduling_state_value(
            self.STATE_KEY_BUY_AP_WEEK_ID, current_week_id
        )

双模式决策表

4.3 智能调度弹窗抑制(双文件联动)

问题:智能调度+代跑子任务时,OpsiScheduling 外层已查询过行动力,子任务内部的搜敌和进入海域逻辑又会重复触发行动力弹窗,导致高频弹窗,可能触发 TooManyClickError

抑制点 1 — module/os/map.py(搜敌前跳过冗余弹窗刷新):

# 智能调度+上下文中,外层已查询过行动力,跳过搜敌前的冗余弹窗刷新
if getattr(self, "_smart_scheduling_context", False) \
        or getattr(self.config, "_smart_scheduling_context", False):
    self._meow_search_start_time = time.time()
    self._meow_search_start_ap = getattr(self, "_action_point_current", None)
    return  # ← 直接返回,不调用 start_meow_search_timer()

# 正常流程(非智能调度上下文时)
self._meow_search_start_time, self._meow_search_start_ap = (
    start_meow_search_timer(self)
)

抑制点 2 — module/os/tasks/meowfficer_farming.py(跳过重复的 action_point_set):

python
# 智能调度+上下文中外层已查询行动力,跳过冗余弹窗
if not self.is_running_smart_scheduling_task():
    self.action_point_set(cost=120, keep_current_ap=True, check_rest_ap=True)
self.fleet_set(self.config.OpsiFleet_Fleet)
**`_smart_scheduling_context` 的传递机制**:同时检查 `self` 和 `self.config` 两个位置,确保跨对象传递时可被正确识别。

---

完整调用链路

五、测试情况

测试项 范围 结果
模式切换 off / hazard1_leveling / meowfficer_farming 三模式 逻辑分支覆盖完整
OCR 校验 _is_buy_action_point_ocr_valid 检测斜杠误识别 新增校验,失败时跳过购买
残留恢复 BuyActionPointLimit <= 0 时自动按游戏上限 5 恢复 已处理
月末封锁 _is_in_month_end_purchase_block_week() 服务器时间偏移 基于 server_time_offset() 计算
推送间隔 AP_NOTIFY_MIN_INTERVAL_MINUTES = 30 最小间隔控制已实现
弹窗抑制 智能调度上下文跳过冗余弹窗 map.py + meowfficer_farming.py 双点处理
翻译 fallback en-US.get(key) 替代 en-US[key] 消除 KeyError
类型安全 daily_checked: list[int] + append(int(n)) 类型一致

六、注意事项

  1. 首次启用双模式:切换 BuyActionPointMode 后需等待 ISO 周重置(周一凌晨),BuyActionPointWeekId 才能正确更新
  2. 月末购买跳过:当前处于月末封锁周时,所有模式的购买均被自动跳过,属预期行为
  3. 推送配置:行动力推送需正确配置 OnePush(OpsiGeneral_OpsiOnePushConfigError_OnePushConfig),provider 不能为 null
  4. 推送间隔:同一通知类型有 30 分钟最小间隔,频繁刷新不会重复推送
  5. OCR 依赖:购买计数基于 cnocr 模型(日服白色字符),极端模糊截图下可能误识别,系统已自动重试

Summary by Sourcery

为 OS 智能调度引入可配置的双模式行动点购买机制,包含每周计数和动态任务优先级,以及相关的调度、OCR 和配置增强。

新功能:

  • 添加 BuyActionPointMode,支持 hazard1 练级和喵菲瑟(meowfficer)刷取两种模式,在 OS 智能调度中驱动不同的行动点购买与执行循环。
  • 基于服务器 ISO 周跟踪每周行动点购买次数,并通过游戏内 OCR 同步,用于限制购买次数并驱动动态任务优先级表。
  • 为 OS 调度、OCR 模型选择、Opsi 商店记录、委托宝石通知、秘书自动化以及 Opsi 舰队自动更换行为新增配置选项。

错误修复:

  • 防止过期的 BuyActionPointLimit 覆写导致购买上限被强制为 0,在需要时恢复为游戏内的最大值。
  • 校验行动点购买的 OCR 识别区域并确保选择了石油按钮,避免误读药箱数量,在 OCR 失败时安全回退。
  • 在智能调度场景中减少多余的行动点弹窗(尤其是喵菲瑟刷取和 hazard1 练级时),避免 TooManyClickError 和不必要的提示。
  • 修复 GUI 语言回退逻辑,通过安全的字典访问方式避免在缺少 ja-JP 键时抛出 KeyError
  • 确保 daily_checked 始终存储整数,并具有正确的类型注解,避免类型不一致。

改进优化:

  • 添加带短 TTL 的行动点读取缓存以及强制刷新选项,以在保持调度决策准确性的同时减少弹窗频率。
  • 调整月末清理流程,在做出决策前始终强制刷新行动点数值。
  • 优化 hazard1 练级和喵菲瑟任务,使其遵守预先校验的行动点和智能调度上下文,减少重复检查和弹窗。
Original summary in English

Summary by Sourcery

Introduce configurable dual-mode action point purchasing for OS smart scheduling, with weekly counters and dynamic task prioritization, plus related scheduling, OCR, and config enhancements.

New Features:

  • Add BuyActionPointMode with hazard1 leveling and meowfficer farming modes that drive distinct action point purchasing and execution loops in OS smart scheduling.
  • Track weekly action point purchase counts based on server ISO week and sync them from in-game OCR, using them to limit buys and drive dynamic task priority tables.
  • Introduce new configuration options for OS scheduling, OCR model selection, Opsi shop records, commission gem notifications, secretary automation, and Opsi fleet auto-change behaviour.

Bug Fixes:

  • Prevent stale BuyActionPointLimit overrides from forcing zero purchase limits by restoring to the in-game maximum when needed.
  • Validate action point purchase OCR regions and ensure the oil button is selected to avoid misreading medicine box counts, falling back safely when OCR fails.
  • Reduce redundant action point popups in smart scheduling contexts for meowfficer farming and hazard1 leveling, avoiding TooManyClickError and unnecessary prompts.
  • Fix GUI language fallback by using safe dictionary access to avoid KeyError when ja-JP keys are missing.
  • Ensure daily_checked always stores integers with proper type annotations to avoid inconsistent types.

Enhancements:

  • Add cached action point reads with short TTL and a force-refresh option to reduce popup frequency while keeping scheduling decisions accurate.
  • Adjust month-end cleanup to always force-refresh action point values before decisions.
  • Refine hazard1 leveling and meowfficer tasks to respect pre-validated action points and smart-scheduling context, reducing duplicate checks and popups.

合并以下 16 个提交为单个 PR 提交:

[feat] 大世界智能调度新增购买行动力双模式功能
  - 新增 BuyActionPointMode 配置(off / hazard1_leveling / meowfficer_farming)
  - 新增 BuyActionPointUpperThreshold / BuyActionPointLowerThreshold 双阈值控制
  - 新增月末行动力清理功能(MonthEndActionPointCleanupEnable)
  - 新增行动力阈值推送通知(30 分钟最小间隔)
  - 新增 CoinTaskMixin(黄币补充任务通用 Mixin)
  - 配置/i18n 同步更新(5 种语言)

[fix] 购买行动力逻辑修复
  - 添加购买行动力 OCR 有效性校验,防止错误覆盖计数器
  - 修复 _is_buy_action_point_ocr_valid 缺少 server 导入
  - 同步购买计数前先选中石油按钮,防止 OCR 读到药箱数量
  - 修复 BuyActionPointLimit 临时覆盖残留导致错误延迟
  - 修复 action_point_buy 与 BuyActionPointLimit 冲突
  - 买行动力模式改用下限阈值决定购买时机
  - 减少买行动力模式下行动力弹窗频率,防止 TooManyClickError
  - 消除智能调度下冗余行动力弹窗(os/map.py + meowfficer_farming.py)
  - 购买后使用缓存 AP 而非过期本地变量
  - 修复 BuyActionPointUpperThreshold 未生效
  - 功能2购买逻辑改为仅使用 UpperThreshold

[fix] 其他
  - fix(daily): 为 daily_checked 添加 list[int] 类型注解
  - fix(webui): 修复翻译文件 key 缺失导致 GUI 启动 KeyError 崩溃
@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

CI 检查报告

检查结果

检查 结果 耗时
Post PR report ➖ unknown -

导入冒烟测试

  • 结果:✅
  • 扫描模块:499(通过 496,已知失败 3,意外失败 0,过期白名单 0)

@sourcery-ai

sourcery-ai Bot commented Aug 16, 2026

Copy link
Copy Markdown

审阅者指南

实现了一个与智能调度深度集成的双模式 OS 行动力购买系统,增加了具备每周购买追踪和改进 OCR 校验的 AP 感知调度循环,引入月末 AP 购买阻断、行动力缓存和弹窗抑制以避免 TooManyClickError,并更新配置、OCR 选项以及类型安全/GUI 回退逻辑。

OS 智能调度双模式行动力购买的时序图

sequenceDiagram
    participant OpsiScheduling
    participant Config
    participant ActionPointUI
    participant HazardLeveling
    participant MeowfficerFarming

    OpsiScheduling->>OpsiScheduling: run_smart_scheduling_once()
    OpsiScheduling->>OpsiScheduling: _is_buy_action_point_mode_active()
    alt BuyActionPointMode enabled and not is_running_prevent_action_point_overflow_task()
        OpsiScheduling->>OpsiScheduling: _run_buy_action_point_mode()
        OpsiScheduling->>OpsiScheduling: _sync_buy_action_point_count_with_game()
        OpsiScheduling->>ActionPointUI: action_point_enter()
        OpsiScheduling->>ActionPointUI: action_point_safe_get()
        OpsiScheduling->>ActionPointUI: action_point_set_button(0)
        OpsiScheduling->>ActionPointUI: action_point_get_buy_remain()
        OpsiScheduling->>OpsiScheduling: _is_buy_action_point_ocr_valid()
        ActionPointUI-->>OpsiScheduling: remain
        OpsiScheduling->>ActionPointUI: action_point_quit()
        OpsiScheduling->>Config: OpsiGeneral_BuyActionPointLimit
        alt buy_limit <= 0 and sync_result == 0
            OpsiScheduling->>OpsiScheduling: notify_push()
            OpsiScheduling->>OpsiScheduling: _delay_smart_scheduling_to_server_update()
            OpsiScheduling-->>OpsiScheduling: return True
        else buy_limit > 0 or restored to 5
            OpsiScheduling->>OpsiScheduling: _get_buy_action_point_count()
            alt hazard1_leveling mode
                OpsiScheduling->>OpsiScheduling: _run_buy_ap_hazard1_loop(buy_limit)
                loop while current_count < buy_limit
                    OpsiScheduling->>OpsiScheduling: _buy_one_action_point()
                    OpsiScheduling->>ActionPointUI: action_point_enter()
                    OpsiScheduling->>ActionPointUI: action_point_safe_get()
                    OpsiScheduling->>ActionPointUI: action_point_buy()
                    OpsiScheduling->>ActionPointUI: action_point_quit()
                    OpsiScheduling->>OpsiScheduling: _set_buy_action_point_count()
                    OpsiScheduling->>OpsiScheduling: _run_scheduled_hazard1_leveling()
                    OpsiScheduling->>HazardLeveling: run_hazard1_leveling_once(ap_preserve, ap_checked=True)
                    HazardLeveling-->>OpsiScheduling: ActionPointLimit (when AP exhausted)
                end
            else meowfficer_farming mode
                OpsiScheduling->>OpsiScheduling: _run_buy_ap_meowfficer_loop(buy_limit)
                loop while current_count < buy_limit
                    OpsiScheduling->>OpsiScheduling: _get_scheduling_action_point()
                    alt current_ap < upper_threshold
                        OpsiScheduling->>OpsiScheduling: _buy_one_action_point()
                        OpsiScheduling->>ActionPointUI: action_point_enter()
                        OpsiScheduling->>ActionPointUI: action_point_safe_get()
                        OpsiScheduling->>ActionPointUI: action_point_buy()
                        OpsiScheduling->>ActionPointUI: action_point_quit()
                        OpsiScheduling->>OpsiScheduling: _set_buy_action_point_count()
                    end
                    OpsiScheduling->>OpsiScheduling: _get_priority_table_for_buy_count()
                    OpsiScheduling->>OpsiScheduling: _get_filtered_priority_table()
                    OpsiScheduling->>OpsiScheduling: notify_push()
                    OpsiScheduling->>Config: temporary(OpsiGeneral_BuyActionPointLimit=0)
                    loop for each task_name in filtered_table
                        OpsiScheduling->>OpsiScheduling: _run_scheduled_coin_task_once(task_name, 0)
                        alt success
                            OpsiScheduling->>OpsiScheduling: _get_scheduling_action_point(force_refresh=True)
                        else ActionPointLimit or AP < lower_threshold
                            OpsiScheduling-->>OpsiScheduling: break to next purchase
                        end
                    end
                end
            end
        end
    else BuyActionPointMode disabled or prevent_overflow task running
        OpsiScheduling->>OpsiScheduling: normal yellow_coins/AP scheduling
    end
Loading

文件级改动

Change Details Files
添加双模式 OS 行动力购买逻辑并与智能调度集成,包含每周计数器和模式专属循环。
  • 在 OpsiScheduling/CoinTaskMixin 中引入 BuyActionPointMode、行动力上下阈值以及持久化的每周 AP 购买计数器。
  • 实现 _run_buy_action_point_mode 入口,通过 OCR 同步购买次数,在遵守 BuyActionPointLimit 的同时在误配置时进行恢复,并根据模式分发到 hazard1 或 meowfficer。
  • 添加 hazard1 练级模式循环:每轮只购买一次 AP,然后在临时禁用 BuyActionPointLimit 的情况下运行 hazard1 练级直到行动力耗尽。
  • 添加 meowfficer 刷币模式循环:利用行动力上下阈值、按购买次数动态调整的任务优先级表,以及基于启用的金币任务进行过滤后的优先级。
  • 将买 AP 模式接入 run_smart_scheduling_once,使其与现有 AP/黄币调度互斥,并在溢出预防任务运行时跳过。
module/os/tasks/scheduling.py
module/os/tasks/hazard_leveling.py
module/os/tasks/meowfficer_farming.py
改进行动力读取、缓存和弹窗行为,以避免重复弹窗和 TooManyClickError。
  • 添加带 60 秒 TTL 和 force_refresh 标志的 _get_scheduling_action_point 缓存,并在调度和月末清理中使用,以降低弹窗频率。
  • 将 ap_checked 透传到 hazard1 练级和 meowfficer 刷币中,以便在调度上下文已经验证过行动力时跳过内部的 action_point_set。
  • 在地图 on_meow_search_start 中检测 self/config 上的智能调度上下文标记,并跳过启动猫搜计时器,以避免多余的行动力弹窗。
  • 在 meowfficer 模式中,购买后使用缓存的 AP,避免在购买完成后立刻通过弹窗重新读取行动力。
module/os/tasks/scheduling.py
module/os/tasks/hazard_leveling.py
module/os/tasks/meowfficer_farming.py
module/os/map.py
强化 AP 购买 OCR 以及 BuyActionPointLimit 的处理逻辑,包括基于周的限制和月末阻断。
  • 导入 server_time_offset 和 module.config.server,新增 _get_current_purchase_week_id 与 _reset_buy_action_point_count_if_new_week,用于按服务器时间的 ISO 周追踪购买次数。
  • 新增 _is_buy_action_point_ocr_valid,使用裁剪/抽取字母和按服务器配置的像素计数启发式来判断购买剩余次数的 OCR 结果是否可信。
  • 在 _sync_buy_action_point_count_with_game 中强制先选择油按钮,再读取剩余购买次数,避免把药箱数量误识别为 AP 购买次数。
  • 更新 action_point_buy,当 OCR 展示剩余购买次数时,从 0 恢复 BuyActionPointLimit 到游戏内上限,并遵守月末购买阻断周。
  • 确保买模式入口在同步持久化计数器时,能够在 OCR 读取失败时避免覆盖已存储的值。
module/os/tasks/scheduling.py
module/os_handler/action_point.py
扩展配置和生成配置,支持新的 AP 购买模式、OCR 模型选项以及附加功能。
  • 在 argument.yaml、args.json、template.json 和 GeneratedConfig 中添加 OpsiScheduling.BuyActionPointMode/UpperThreshold/LowerThreshold,并设置合适的选项和默认值。
  • 扩展 Optimization.* OCR 模型版本选项,为英语/简体中文/日语/繁体中文提供具体的模型标识,并在 GeneratedConfig 中反映这些变化。
  • 在 argument.yaml 和 GeneratedConfig 中添加新的 DropRecord.OpsiShopRecord、Commission.GemNotify、秘书分组选项以及 OpsiFleetAutoChange.AnyShipReached。
  • 在 IslandBusinessShop 的角色选项列表中移除 DaVinci,并在 GeneratedConfig 中保持一致。
module/config/argument/argument.yaml
module/config/argument/args.json
config/template.json
module/config/config_generated.py
在主调度逻辑之外增加与 AP 相关的修复、GUI 可靠性增强以及类型安全改进。
  • 将 Daily.daily_checked 修改为 list[int],并将追加的值统一转换为 int 以提升类型安全性。
  • 让日文回退翻译使用 dic_lang['en-US'].get(key),并在键缺失时进行防护,以避免 GUI 启动时出现 KeyError。
  • 保证月末清理在检查阈值及上报结果时始终强制刷新行动力。
  • 在所有受支持的本地化中新增与新配置和功能相关的少量 i18n 文本条目。
module/daily/daily.py
module/webui/lang.py
module/os/tasks/scheduling.py
module/config/i18n/en-US.json
module/config/i18n/ja-JP.json
module/config/i18n/zh-CN.json
module/config/i18n/zh-MIAO.json
module/config/i18n/zh-TW.json

可能关联的 Issue

  • #0: PR 在 meowfficer_farming 中跳过智能调度下的 action_point_set,修复短猫导致智能调度误认为行动力不足的问题。

提示与命令

与 Sourcery 交互

  • 触发新审阅: 在 Pull Request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审阅评论。
  • 从审阅评论生成 GitHub Issue: 在审阅评论下回复,要求 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 来(重新)生成摘要。
  • 生成审阅者指南: 在 Pull Request 中评论 @sourcery-ai guide,即可随时(重新)生成审阅者指南。
  • 解决所有 Sourcery 评论: 在 Pull Request 中评论 @sourcery-ai resolve,即可将所有 Sourcery 评论标记为已解决。如果你已经处理完所有评论且不想再看到它们,这会很有用。
  • 关闭所有 Sourcery 审阅: 在 Pull Request 中评论 @sourcery-ai dismiss,即可关闭所有现有的 Sourcery 审阅。如果你想从一次全新的审阅开始,这尤其有用——记得再评论 @sourcery-ai review 以触发新审阅!

自定义你的体验

访问你的 控制面板 以:

  • 启用或禁用审阅特性,例如 Sourcery 生成的 Pull Request 摘要、审阅者指南等。
  • 更改审阅语言。
  • 添加、移除或编辑自定义审阅指令。
  • 调整其他审阅设置。

获取帮助

Original review guide in English

Reviewer's Guide

Implements a dual-mode OS action point buying system tightly integrated with smart scheduling, adds AP-aware scheduling loops with weekly purchase tracking and improved OCR validation, introduces month-end AP purchase blocking, action point caching and popup suppression to avoid TooManyClickError, and updates configuration, OCR options, and type safety/GUI fallbacks.

Sequence diagram for OS smart scheduling dual-mode action point buying

sequenceDiagram
    participant OpsiScheduling
    participant Config
    participant ActionPointUI
    participant HazardLeveling
    participant MeowfficerFarming

    OpsiScheduling->>OpsiScheduling: run_smart_scheduling_once()
    OpsiScheduling->>OpsiScheduling: _is_buy_action_point_mode_active()
    alt BuyActionPointMode enabled and not is_running_prevent_action_point_overflow_task()
        OpsiScheduling->>OpsiScheduling: _run_buy_action_point_mode()
        OpsiScheduling->>OpsiScheduling: _sync_buy_action_point_count_with_game()
        OpsiScheduling->>ActionPointUI: action_point_enter()
        OpsiScheduling->>ActionPointUI: action_point_safe_get()
        OpsiScheduling->>ActionPointUI: action_point_set_button(0)
        OpsiScheduling->>ActionPointUI: action_point_get_buy_remain()
        OpsiScheduling->>OpsiScheduling: _is_buy_action_point_ocr_valid()
        ActionPointUI-->>OpsiScheduling: remain
        OpsiScheduling->>ActionPointUI: action_point_quit()
        OpsiScheduling->>Config: OpsiGeneral_BuyActionPointLimit
        alt buy_limit <= 0 and sync_result == 0
            OpsiScheduling->>OpsiScheduling: notify_push()
            OpsiScheduling->>OpsiScheduling: _delay_smart_scheduling_to_server_update()
            OpsiScheduling-->>OpsiScheduling: return True
        else buy_limit > 0 or restored to 5
            OpsiScheduling->>OpsiScheduling: _get_buy_action_point_count()
            alt hazard1_leveling mode
                OpsiScheduling->>OpsiScheduling: _run_buy_ap_hazard1_loop(buy_limit)
                loop while current_count < buy_limit
                    OpsiScheduling->>OpsiScheduling: _buy_one_action_point()
                    OpsiScheduling->>ActionPointUI: action_point_enter()
                    OpsiScheduling->>ActionPointUI: action_point_safe_get()
                    OpsiScheduling->>ActionPointUI: action_point_buy()
                    OpsiScheduling->>ActionPointUI: action_point_quit()
                    OpsiScheduling->>OpsiScheduling: _set_buy_action_point_count()
                    OpsiScheduling->>OpsiScheduling: _run_scheduled_hazard1_leveling()
                    OpsiScheduling->>HazardLeveling: run_hazard1_leveling_once(ap_preserve, ap_checked=True)
                    HazardLeveling-->>OpsiScheduling: ActionPointLimit (when AP exhausted)
                end
            else meowfficer_farming mode
                OpsiScheduling->>OpsiScheduling: _run_buy_ap_meowfficer_loop(buy_limit)
                loop while current_count < buy_limit
                    OpsiScheduling->>OpsiScheduling: _get_scheduling_action_point()
                    alt current_ap < upper_threshold
                        OpsiScheduling->>OpsiScheduling: _buy_one_action_point()
                        OpsiScheduling->>ActionPointUI: action_point_enter()
                        OpsiScheduling->>ActionPointUI: action_point_safe_get()
                        OpsiScheduling->>ActionPointUI: action_point_buy()
                        OpsiScheduling->>ActionPointUI: action_point_quit()
                        OpsiScheduling->>OpsiScheduling: _set_buy_action_point_count()
                    end
                    OpsiScheduling->>OpsiScheduling: _get_priority_table_for_buy_count()
                    OpsiScheduling->>OpsiScheduling: _get_filtered_priority_table()
                    OpsiScheduling->>OpsiScheduling: notify_push()
                    OpsiScheduling->>Config: temporary(OpsiGeneral_BuyActionPointLimit=0)
                    loop for each task_name in filtered_table
                        OpsiScheduling->>OpsiScheduling: _run_scheduled_coin_task_once(task_name, 0)
                        alt success
                            OpsiScheduling->>OpsiScheduling: _get_scheduling_action_point(force_refresh=True)
                        else ActionPointLimit or AP < lower_threshold
                            OpsiScheduling-->>OpsiScheduling: break to next purchase
                        end
                    end
                end
            end
        end
    else BuyActionPointMode disabled or prevent_overflow task running
        OpsiScheduling->>OpsiScheduling: normal yellow_coins/AP scheduling
    end
Loading

File-Level Changes

Change Details Files
Add dual-mode OS action point purchasing integrated into smart scheduling with weekly counters and mode-specific loops.
  • Introduce BuyActionPointMode, upper/lower AP thresholds, and persistent weekly counters for AP purchases in OpsiScheduling/CoinTaskMixin.
  • Implement _run_buy_action_point_mode entry that syncs purchase count via OCR, respects BuyActionPointLimit with recovery when misconfigured, and dispatches to hazard1 or meowfficer modes.
  • Add hazard1 leveling mode loop that buys AP once per round then runs hazard1 leveling until AP exhausted with BuyActionPointLimit temporarily disabled.
  • Add meowfficer farming mode loop that uses upper/lower AP thresholds, dynamic task priority tables per purchase count, and filtered priorities based on enabled coin tasks.
  • Wire buy-AP mode into run_smart_scheduling_once so it is mutually exclusive with the existing AP/yellow coin scheduling and skipped for overflow-prevention tasks.
module/os/tasks/scheduling.py
module/os/tasks/hazard_leveling.py
module/os/tasks/meowfficer_farming.py
Improve action point reading, caching, and popup behavior to avoid redundant dialogs and TooManyClickError.
  • Add _get_scheduling_action_point cache with 60s TTL and force_refresh flag, and use it from scheduling and month-end cleanup to reduce popup frequency.
  • Propagate ap_checked into hazard1 leveling and meowfficer farming so they can skip internal action_point_set when AP was already verified in the scheduling context.
  • In map on_meow_search_start, detect smart scheduling context flags on self/config and skip starting the meow search timer to avoid redundant AP popups.
  • Use cached AP after purchases to avoid rereading AP via popup immediately after buying in meowfficer mode.
module/os/tasks/scheduling.py
module/os/tasks/hazard_leveling.py
module/os/tasks/meowfficer_farming.py
module/os/map.py
Harden AP purchase OCR and BuyActionPointLimit handling, including week-based limits and month-end blocking.
  • Import server_time_offset and module.config.server, add _get_current_purchase_week_id and _reset_buy_action_point_count_if_new_week to track purchases per ISO week on server time.
  • Add _is_buy_action_point_ocr_valid using crop/extract_letters and a pixel-count heuristic per server to detect when the buy-remaining OCR result is trustworthy.
  • Force selecting the oil button before reading remaining buys in _sync_buy_action_point_count_with_game to avoid interpreting medicine box counts as AP purchases.
  • Update action_point_buy to recover BuyActionPointLimit from 0 back to the in-game max when OCR shows remaining buys, and honor month-end purchase block weeks.
  • Ensure buy-mode entry synchronizes persistent counters with OCR while avoiding overwriting stored values on failed OCR reads.
module/os/tasks/scheduling.py
module/os_handler/action_point.py
Extend configuration and generated config for new AP buy mode, OCR model options, and additional features.
  • Add OpsiScheduling.BuyActionPointMode/UpperThreshold/LowerThreshold to argument.yaml, args.json, template.json, and GeneratedConfig with proper options and defaults.
  • Expand Optimization.* OCR model version options for English/Chinese/Japanese/Traditional Chinese to concrete model identifiers and reflect them in GeneratedConfig.
  • Add new DropRecord.OpsiShopRecord, Commission.GemNotify, Secretary group options, and OpsiFleetAutoChange.AnyShipReached to argument.yaml and GeneratedConfig.
  • Adjust IslandBusinessShop character option lists to drop DaVinci from the allowed values consistently in GeneratedConfig.
module/config/argument/argument.yaml
module/config/argument/args.json
config/template.json
module/config/config_generated.py
Add AP-related and GUI robustness fixes and type-safety improvements outside the main scheduling logic.
  • Change Daily.daily_checked to list[int] and cast appended values to int for type safety.
  • Make JA fallback translation use dic_lang['en-US'].get(key) and guard against missing keys to avoid KeyError on GUI startup.
  • Ensure month-end cleanup always forces AP refresh when checking thresholds and reporting results.
  • Add minor i18n text entries for new configuration and features across all supported locales.
module/daily/daily.py
module/webui/lang.py
module/os/tasks/scheduling.py
module/config/i18n/en-US.json
module/config/i18n/ja-JP.json
module/config/i18n/zh-CN.json
module/config/i18n/zh-MIAO.json
module/config/i18n/zh-TW.json

Possibly linked issues

  • #0: PR在meowfficer_farming中跳过智能调度下的action_point_set,修复短猫导致智能调度误认为行动力不足的问题。

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

  • _run_buy_ap_meowfficer_loop 中,push_content 代码块引用了 bought_this_roundbuy_round,但 bought_this_round 从未被定义,而 buy_round 只在购买分支中被赋值;当当前 AP ≥ 上限阈值时,这会导致 NameError 和/或 UnboundLocalError。建议显式维护一个表示“本轮是否有购买”的布尔变量,并在两条执行路径中都安全地初始化 buy_round
  • 60 秒的 AP 缓存 TTL 逻辑在 _get_scheduling_action_point_run_scheduled_hazard1_leveling 中重复出现;可以考虑把 TTL 和缓存检查提取到一个小的辅助函数或共享常量中,以降低这两个调用点未来出现差异的风险。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `_run_buy_ap_meowfficer_loop`, the `push_content` block references `bought_this_round` and `buy_round`, but `bought_this_round` is never defined and `buy_round` is only set in the purchase branch, which will cause a `NameError` and/or `UnboundLocalError` when current AP ≥ upper threshold; consider explicitly tracking a boolean for "bought this round" and initializing `buy_round` safely for both paths.
- The 60-second AP cache TTL logic is duplicated between `_get_scheduling_action_point` and `_run_scheduled_hazard1_leveling`; factoring the TTL and cache-check into a small helper or shared constant would reduce the risk of the two callsites diverging over time.

## Individual Comments

### Comment 1
<location path="module/os/tasks/scheduling.py" line_range="1300-1301" />
<code_context>
+                f'[大世界-买行动力] 第 {buy_count} 次购买后优先级表: {task_names}'
+            )
+
+            # 推送本轮购买状态和下一步任务
+            if bought_this_round:
+                push_content = (
+                    f'第 {buy_round}/{buy_limit} 次购买行动力完成\n'
</code_context>
<issue_to_address>
**issue (bug_risk):** Variable `bought_this_round` is used but never defined in `_run_buy_ap_meowfficer_loop`, which will raise at runtime.

In this function, `bought_this_round` is only read when composing the push content and is never set in any branch, so this path will immediately raise a `NameError`. You likely want a flag indicating whether a purchase occurred in the current loop iteration, e.g. compute it (such as `bought_this_round = current_ap < upper_threshold and self._buy_one_action_point()`) before this block and use that value here.
</issue_to_address>

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

Hey - I've found 1 issue, and left some high level feedback:

  • In _run_buy_ap_meowfficer_loop, the push_content block references bought_this_round and buy_round, but bought_this_round is never defined and buy_round is only set in the purchase branch, which will cause a NameError and/or UnboundLocalError when current AP ≥ upper threshold; consider explicitly tracking a boolean for "bought this round" and initializing buy_round safely for both paths.
  • The 60-second AP cache TTL logic is duplicated between _get_scheduling_action_point and _run_scheduled_hazard1_leveling; factoring the TTL and cache-check into a small helper or shared constant would reduce the risk of the two callsites diverging over time.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `_run_buy_ap_meowfficer_loop`, the `push_content` block references `bought_this_round` and `buy_round`, but `bought_this_round` is never defined and `buy_round` is only set in the purchase branch, which will cause a `NameError` and/or `UnboundLocalError` when current AP ≥ upper threshold; consider explicitly tracking a boolean for "bought this round" and initializing `buy_round` safely for both paths.
- The 60-second AP cache TTL logic is duplicated between `_get_scheduling_action_point` and `_run_scheduled_hazard1_leveling`; factoring the TTL and cache-check into a small helper or shared constant would reduce the risk of the two callsites diverging over time.

## Individual Comments

### Comment 1
<location path="module/os/tasks/scheduling.py" line_range="1300-1301" />
<code_context>
+                f'[大世界-买行动力] 第 {buy_count} 次购买后优先级表: {task_names}'
+            )
+
+            # 推送本轮购买状态和下一步任务
+            if bought_this_round:
+                push_content = (
+                    f'第 {buy_round}/{buy_limit} 次购买行动力完成\n'
</code_context>
<issue_to_address>
**issue (bug_risk):** Variable `bought_this_round` is used but never defined in `_run_buy_ap_meowfficer_loop`, which will raise at runtime.

In this function, `bought_this_round` is only read when composing the push content and is never set in any branch, so this path will immediately raise a `NameError`. You likely want a flag indicating whether a purchase occurred in the current loop iteration, e.g. compute it (such as `bought_this_round = current_ap < upper_threshold and self._buy_one_action_point()`) before this block and use that value here.
</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.

Comment on lines +1300 to +1301
# 推送本轮购买状态和下一步任务
if bought_this_round:

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 (bug_risk): 变量 bought_this_round_run_buy_ap_meowfficer_loop 中被使用但从未定义,运行时会抛出异常。

在这个函数中,bought_this_round 只在构造推送内容时被读取,但在任何分支中都没有被赋值,因此这条执行路径会立即触发 NameError。你可能需要一个标志位来表示当前循环迭代中是否发生了购买,例如在该代码块之前计算它(比如 bought_this_round = current_ap < upper_threshold and self._buy_one_action_point()),然后在这里使用这个值。

Original comment in English

issue (bug_risk): Variable bought_this_round is used but never defined in _run_buy_ap_meowfficer_loop, which will raise at runtime.

In this function, bought_this_round is only read when composing the push content and is never set in any branch, so this path will immediately raise a NameError. You likely want a flag indicating whether a purchase occurred in the current loop iteration, e.g. compute it (such as bought_this_round = current_ap < upper_threshold and self._buy_one_action_point()) before this block and use that value here.

@changqing81 changqing81 reopened this Aug 16, 2026
@Beatrice-betty

Copy link
Copy Markdown
Collaborator

不是哥们,你AI改的代码能看一下吗?你自己看看改的什么东西,自己重新改,还有大世界智能调度麻烦用有点脑子的AI改,这坨屎山太容易出问题了

@changqing81
changqing81 deleted the squash-buy-ap branch August 16, 2026 13:31
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