feat: add canteen stamina claim automation

This commit is contained in:
admin committed 2026-10-04 18:40:36 +08:00
1 parent f094798fc8
commit 48d5f63e36
11 files changed
+221 -4

No files matched your search

+8
View File
@@ -43,6 +43,14 @@
已在 2026-10-04 实机验证收矿、10 秒等待、白银罗盘雇佣和返回首页。找不到“一键收矿”时,脚本不雇佣并直接返回首页。使用 `--dry-run` 可只验证首页而不点击。 已在 2026-10-04 实机验证收矿、10 秒等待、白银罗盘雇佣和返回首页。找不到“一键收矿”时,脚本不雇佣并直接返回首页。使用 `--dry-run` 可只验证首页而不点击。
### 已验证的食堂领体力脚本
```bash
.venv/bin/python claim_stamina.py
```
流程为:首页 → 领地 → 食堂 → 发现“一键领取”时领取体力 → 确认“恭喜获得”弹框 → 确认烤肉库存为 0/60 → 返回首页。没有可领体力时,脚本不点击领取并直接返回首页。使用 `--dry-run` 可只验证首页而不点击。
需要 Python 3.9 或更新版本,以及带 Tk 支持的 Python。macOS 若当前 Python 缺少 Tk,可使用 python.org 官方安装包。 需要 Python 3.9 或更新版本,以及带 Tk 支持的 Python。macOS 若当前 Python 缺少 Tk,可使用 python.org 官方安装包。
macOS: macOS:
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

+66
View File
@@ -0,0 +1,66 @@
"""领地食堂体力领取流程。"""
import time
from .engine import WorkflowError
class StaminaRunner:
def __init__(self, desktop, matcher, flow, region_provider, log=print, timeout=12):
self.desktop = desktop
self.matcher = matcher
self.flow = flow
self.region_provider = region_provider
self.log = log
self.timeout = timeout
def wait(self, *names):
deadline = time.monotonic() + self.timeout
previous, stable = None, 0
while time.monotonic() < deadline:
screenshot = self.desktop.screenshot()
region = self.region_provider(screenshot)
frame = self.matcher.frame(screenshot, region)
found = next((name for name in names
if self.matcher.matches(frame, self.flow['states'][name])), None)
stable = stable + 1 if found and found == previous else 1 if found else 0
previous = found
if stable >= 2:
return found, screenshot, region
time.sleep(0.25)
raise WorkflowError('未识别到预期页面 ' + '/'.join(names) + ',已停止。')
def click(self, action, observation):
_, screenshot, region = observation
if self.region_provider(screenshot) != region:
raise WorkflowError('识别后游戏窗口移动,已停止,请重新运行。')
self.desktop.click(self.flow['clicks'][action], region, screenshot.size)
self.log('已执行:' + action)
def close_and_return(self, canteen):
self.click('close_canteen', canteen)
self.click('territory_back', self.wait('territory'))
self.wait('home')
self.log('已确认返回首页。')
def run(self, dry_run=False):
home = self.wait('home')
if dry_run:
self.log('首页识别通过。计划:领地 → 食堂 → 发现一键领取时领体力 → 返回首页。未点击。')
return 'preview'
self.click('territory', home)
self.click('canteen', self.wait('territory'))
canteen = self.wait('available', 'empty')
if canteen[0] == 'empty':
self.log('食堂没有可领取体力,本次跳过。')
self.close_and_return(canteen)
return 'no_stamina'
self.click('claim', canteen)
reward = self.wait('reward')
self.log('已确认获得体力奖励弹框。')
self.click('dismiss_reward', reward)
empty = self.wait('empty')
self.log('已确认食堂进入无可领体力状态。')
self.close_and_return(empty)
return 'claimed'
+39
View File
@@ -0,0 +1,39 @@
"""Run from the project root: python claim_stamina.py [--dry-run]."""
import argparse
import sys
import time
from pathlib import Path
from automation.engine import Matcher, WorkflowError, load_flow
from automation.offers import MacGameDesktop
from automation.stamina import StaminaRunner
ROOT = Path(__file__).resolve().parent
def main():
parser = argparse.ArgumentParser(description='食堂:一键领取体力并返回首页')
parser.add_argument('--dry-run', action='store_true', help='只验证首页并显示计划,不点击')
args = parser.parse_args()
if sys.platform != 'darwin':
print('当前脚本已验证 macOS;Windows 版将在校准入口完成后接入。', file=sys.stderr)
return 2
try:
desktop = MacGameDesktop()
desktop.activate()
print('3 秒后开始;请保持游戏在前台。鼠标移到主屏幕左上角可紧急停止。', flush=True)
time.sleep(3)
runner = StaminaRunner(desktop, Matcher(ROOT / 'assets'),
load_flow(ROOT / 'workflows/stamina_claim.json'), desktop.region_for)
runner.run(args.dry_run)
return 0
except KeyboardInterrupt:
print('已停止。', file=sys.stderr)
return 130
except Exception as exc:
print('流程停止:' + str(exc), file=sys.stderr)
return 1
if __name__ == '__main__':
raise SystemExit(main())
@@ -1,7 +1,7 @@
--- ---
created_at: 2026-10-04 16:07:56 CST created_at: 2026-10-04 16:07:56 CST
type: 永远的蔚蓝星球首页、商城、领地与公会入口、日常任务与自动化适配记录 type: 永远的蔚蓝星球首页、商城、领地与公会入口、日常任务与自动化适配记录
revision: 第9次 revision: 第10次
revisions: revisions:
- time: 2026-10-04 16:07:56 CST - time: 2026-10-04 16:07:56 CST
version: 第1次 version: 第1次
@@ -39,7 +39,11 @@ revisions:
version: 第9次 version: 第9次
author: gpt-5 author: gpt-5
content: 在文档末尾新增已实现脚本汇总表,按首页、商城、领地、公会的入口页面顺序整理入口、功能和更新时间。 content: 在文档末尾新增已实现脚本汇总表,按首页、商城、领地、公会的入口页面顺序整理入口、功能和更新时间。
summary: 记录首页、商城、领地和公会的日常任务入口、待完善流程及已实现脚本汇总。入口红点表示内部有奖励可领取;特惠赏金战令领取、狂鼠之家收矿与优先罗盘雇佣均已实机验证并写成脚本。 - time: 2026-10-04 18:32:57 CST
version: 第10次
author: gpt-5
content: 补充食堂一键领取体力的实机验证流程、无可领取分支、脚本和汇总表条目。
summary: 记录首页、商城、领地和公会的日常任务入口、待完善流程及已实现脚本汇总。入口红点表示内部有奖励可领取;特惠赏金战令领取、狂鼠之家收矿与雇佣、食堂领体力均已实机验证并写成脚本。
--- ---
# 首页、商城、领地与公会功能及日常任务梳理 # 首页、商城、领地与公会功能及日常任务梳理
@@ -168,7 +172,7 @@ summary: 记录首页、商城、领地和公会的日常任务入口、待完
| 狂鼠之家 | 左侧中下部,矿洞建筑 | 一键收矿与优先罗盘一键雇佣已实机验证并有脚本;其他内部功能待补充 | | 狂鼠之家 | 左侧中下部,矿洞建筑 | 一键收矿与优先罗盘一键雇佣已实机验证并有脚本;其他内部功能待补充 |
| 玩法大厅 | 下方偏左,带喇叭的建筑 | 用户确认有固定流程,具体步骤待补充 | | 玩法大厅 | 下方偏左,带喇叭的建筑 | 用户确认有固定流程,具体步骤待补充 |
| 图鉴 | 下方偏右,书本建筑 | 用户确认有固定流程,具体步骤待补充 | | 图鉴 | 下方偏右,书本建筑 | 用户确认有固定流程,具体步骤待补充 |
| 食堂 | 中部,带食物的建筑 | 用户确认有固定流程,具体步骤待补充 | | 食堂 | 中部,带食物的建筑 | 一键领取体力已实机验证并有脚本;其他内部功能待补充 |
| 排行榜 | 右侧中部,石制台座区域 | 用户确认有固定流程,具体步骤待补充 | | 排行榜 | 右侧中部,石制台座区域 | 用户确认有固定流程,具体步骤待补充 |
| 巅峰决斗 | 左上部,旗帜建筑 | 用户确认有固定流程,具体步骤待补充 | | 巅峰决斗 | 左上部,旗帜建筑 | 用户确认有固定流程,具体步骤待补充 |
| 合作模式 | 上方中部,紫色传送门区域 | 用户确认有固定流程,具体步骤待补充 | | 合作模式 | 上方中部,紫色传送门区域 | 用户确认有固定流程,具体步骤待补充 |
@@ -196,6 +200,18 @@ summary: 记录首页、商城、领地和公会的日常任务入口、待完
该流程使用截图模板识别与相对坐标点击,macOS 已实机验证;Windows 仍需在对应环境校准并验证。可使用 `--dry-run` 只检查首页和执行计划,不点击游戏内容。 该流程使用截图模板识别与相对坐标点击,macOS 已实机验证;Windows 仍需在对应环境校准并验证。可使用 `--dry-run` 只检查首页和执行计划,不点击游戏内容。
### 食堂:一键领取体力(已实机验证)
2026-10-04 已在当前账号完成:**首页 → 领地 → 食堂 → 一键领取 → 确认奖励弹框 → 关闭弹框 → 关闭食堂 → 返回首页**。本次领取 30 体力,首页体力由 1042/80 变为 1072/80。领取后食堂显示烤肉库存 0/60,并出现下次领取倒计时。
对应脚本为项目根目录的 `claim_stamina.py`,配置在 `workflows/stamina_claim.json`。运行方式:
```bash
.venv/bin/python claim_stamina.py
```
脚本先确认食堂内有可领取烤肉,才点击“一键领取”;随后必须识别“恭喜获得”奖励弹框和烤肉库存 0/60,最后关闭食堂并返回首页。没有可领取体力时,会跳过领取并直接返回首页。macOS 已实机验证;Windows 仍需校准和验证。
## 公会页面 ## 公会页面
### 入口与栏目 ### 入口与栏目
@@ -236,7 +252,8 @@ summary: 记录首页、商城、领地和公会的日常任务入口、待完
| 商城:英雄招募、图腾寻宝、每日商店、金币 | 后续确定 | 已记录区域结构,日常目标、执行次数、资源消耗与完成判定尚未明确 | | 商城:英雄招募、图腾寻宝、每日商店、金币 | 后续确定 | 已记录区域结构,日常目标、执行次数、资源消耗与完成判定尚未明确 |
| 商城:关卡礼包、钻石 | 暂不处理 | 用户明确排除 | | 商城:关卡礼包、钻石 | 暂不处理 | 用户明确排除 |
| 领地:狂鼠之家 | 已验证脚本:一键收矿、等待 10 秒、按白银/黄金/彩虹顺序一键雇佣、返回首页 | 当前仅覆盖该日常流程;其他内部功能、重置规律和 Windows 适配待验证 | | 领地:狂鼠之家 | 已验证脚本:一键收矿、等待 10 秒、按白银/黄金/彩虹顺序一键雇佣、返回首页 | 当前仅覆盖该日常流程;其他内部功能、重置规律和 Windows 适配待验证 |
| 领地:玩法大厅、图鉴、食堂、排行榜、巅峰决斗、合作模式、城墙 | 待完善固定流程后确定 | 用户确认各栏目均有固定自动化流程,具体步骤、执行条件、消耗和完成判定尚未补充 | | 领地:食堂 | 已验证脚本:发现一键领取时领体力、确认奖励和库存清空、返回首页 | 当前仅覆盖体力领取;其他内部功能、重置规律和 Windows 适配待验证 |
| 领地:玩法大厅、图鉴、排行榜、巅峰决斗、合作模式、城墙 | 待完善固定流程后确定 | 用户确认各栏目均有固定自动化流程,具体步骤、执行条件、消耗和完成判定尚未补充 |
| 公会:公会商城、远征大厅、公会任务、公会祝福、每日捐献、成员大厅、公会 Boss、右下角红色角色入口 | 待完善固定流程后确定 | 用户确认八个入口均有固定自动化流程;右下角入口名称及各流程的具体步骤、消耗和完成判定待补充 | | 公会:公会商城、远征大厅、公会任务、公会祝福、每日捐献、成员大厅、公会 Boss、右下角红色角色入口 | 待完善固定流程后确定 | 用户确认八个入口均有固定自动化流程;右下角入口名称及各流程的具体步骤、消耗和完成判定待补充 |
初步方向是让脚本执行已验证的重复步骤,让 AI 辅助识别变化页面或异常。是否每次调用 AI、调用到哪一步,待明确任务完成标志与变化范围后再决定。 初步方向是让脚本执行已验证的重复步骤,让 AI 辅助识别变化页面或异常。是否每次调用 AI、调用到哪一步,待明确任务完成标志与变化范围后再决定。
@@ -264,3 +281,4 @@ summary: 记录首页、商城、领地和公会的日常任务入口、待完
| --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- |
| 1 | 战令领取(`claim_offer.py`) | 首页 | 首页 → 左侧特惠 → 战令 | 识别“超值月卡”时先切换战令;领取赏金战令奖励,确认奖励弹框和按钮变灰后返回首页;已领取时跳过。 | 2026-10-04 17:45 CST | | 1 | 战令领取(`claim_offer.py`) | 首页 | 首页 → 左侧特惠 → 战令 | 识别“超值月卡”时先切换战令;领取赏金战令奖励,确认奖励弹框和按钮变灰后返回首页;已领取时跳过。 | 2026-10-04 17:45 CST |
| 2 | 收矿与雇佣(`claim_mining.py`) | 领地 | 首页 → 底部领地 → 狂鼠之家 | 发现“一键收矿”后收矿、关闭奖励弹框、等待 10 秒;按白银罗盘、黄金罗盘、彩虹探测仪顺序一键雇佣,无可用道具时关闭弹框,最后返回首页。 | 2026-10-04 18:17 CST | | 2 | 收矿与雇佣(`claim_mining.py`) | 领地 | 首页 → 底部领地 → 狂鼠之家 | 发现“一键收矿”后收矿、关闭奖励弹框、等待 10 秒;按白银罗盘、黄金罗盘、彩虹探测仪顺序一键雇佣,无可用道具时关闭弹框,最后返回首页。 | 2026-10-04 18:17 CST |
| 3 | 领体力(`claim_stamina.py`) | 领地 | 首页 → 底部领地 → 食堂 | 发现“一键领取”时领取体力,确认“恭喜获得”弹框和烤肉库存 0/60 后返回首页;无可领体力时跳过。 | 2026-10-04 18:32 CST |
+59
View File
@@ -0,0 +1,59 @@
import unittest
from pathlib import Path
from PIL import Image
from automation.engine import Matcher, Region, load_flow
from automation.stamina import StaminaRunner
ROOT = Path(__file__).resolve().parent.parent
class StaminaDesktop:
def __init__(self, images):
self.images = images
self.index = 0
self.clicks = []
def screenshot(self):
return self.images[min(self.index, len(self.images) - 1)]
def click(self, point, region, size):
self.clicks.append(point)
self.index += 1
class StaminaTests(unittest.TestCase):
def setUp(self):
self.flow = load_flow(ROOT / 'workflows/stamina_claim.json')
self.matcher = Matcher(ROOT / 'assets')
self.region = Region(0, 0, 562, 1002)
def image(self, state):
image = Image.new('RGB', (562, 1002), (42, 58, 73))
for spec in self.flow['states'][state]:
template = Image.open(ROOT / 'assets' / spec['template']).convert('RGB')
x, y, _, _ = spec['search']
image.paste(template, (x + 5, y + 5))
return image
def runner(self, states):
desktop = StaminaDesktop([self.image(state) for state in states])
return desktop, StaminaRunner(desktop, self.matcher, self.flow,
lambda _: self.region, log=lambda _: None, timeout=0.8)
def test_claims_stamina_and_returns_home(self):
desktop, runner = self.runner(['home', 'territory', 'available', 'reward', 'empty', 'territory', 'home'])
self.assertEqual(runner.run(), 'claimed')
expected = ('territory', 'canteen', 'claim', 'dismiss_reward', 'close_canteen', 'territory_back')
self.assertEqual(desktop.clicks, [self.flow['clicks'][name] for name in expected])
def test_empty_canteen_skips_claim_and_returns_home(self):
desktop, runner = self.runner(['home', 'territory', 'empty', 'territory', 'home'])
self.assertEqual(runner.run(), 'no_stamina')
expected = ('territory', 'canteen', 'close_canteen', 'territory_back')
self.assertEqual(desktop.clicks, [self.flow['clicks'][name] for name in expected])
if __name__ == '__main__':
unittest.main()
+27
View File
@@ -0,0 +1,27 @@
{
"name": "食堂一键领取体力",
"states": {
"home": [{"template": "home.png", "search": [150, 730, 410, 865]}],
"territory": [{"template": "territory_rat_house.png", "search": [15, 440, 220, 570]}],
"available": [
{"template": "canteen_title.png", "search": [80, 35, 480, 225]},
{"template": "canteen_available.png", "search": [120, 205, 330, 440]}
],
"empty": [
{"template": "canteen_title.png", "search": [80, 35, 480, 225]},
{"template": "canteen_empty.png", "search": [145, 625, 430, 760]}
],
"reward": [
{"template": "canteen_reward.png", "search": [120, 220, 440, 370], "threshold": 0.8},
{"template": "canteen_dismiss.png", "search": [145, 610, 420, 730]}
]
},
"clicks": {
"territory": [0.68, 0.94],
"canteen": [0.55, 0.50],
"claim": [0.50, 0.735],
"dismiss_reward": [0.50, 0.67],
"close_canteen": [0.50, 0.90],
"territory_back": [0.68, 0.94]
}
}