feat(task): 新增扭蛋机功能,支持任意次数的任意类型扭蛋

This commit is contained in:
YXHXianYu 2025-03-14 12:24:30 +08:00
parent 76475f4d00
commit fe3dd29ce9
12 changed files with 214 additions and 2 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 KiB

View File

@ -0,0 +1 @@
{"definitions":{"26ce71b2-59dc-4237-b50f-41690d7b8ecf":{"name":"Daily.CapsuleToys.NextPageStartPoint","displayName":"日常 扭蛋 下一页起始点","type":"hint-point","annotationId":"26ce71b2-59dc-4237-b50f-41690d7b8ecf","useHintRect":false,"description":"扭蛋页面翻页时的起始点"},"ada5c1e1-1524-4476-bdf9-aa0aab8b35ad":{"name":"Daily.CapsuleToys.NextPageEndPoint","displayName":"日常 扭蛋 下一页终点","type":"hint-point","annotationId":"ada5c1e1-1524-4476-bdf9-aa0aab8b35ad","useHintRect":false,"description":"扭蛋页面翻页时的终点"}},"annotations":[{"id":"26ce71b2-59dc-4237-b50f-41690d7b8ecf","type":"point","data":{"x":360,"y":1167}},{"id":"ada5c1e1-1524-4476-bdf9-aa0aab8b35ad","type":"point","data":{"x":362,"y":267}}]}

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB

View File

@ -0,0 +1 @@
{"definitions":{"045e21c4-350b-4b6c-bcb7-aeb007da05b3":{"name":"Daily.CapsuleToys.SliderStartPoint","displayName":"日常 扭蛋 滑动条起点","type":"hint-point","annotationId":"045e21c4-350b-4b6c-bcb7-aeb007da05b3","useHintRect":false,"description":"扭蛋数量滑动条的右端点置0时的起始点"},"5ba55176-f5a1-4d3f-8fd8-75e868715ad8":{"name":"Daily.CapsuleToys.SliderEndPoint","displayName":"日常 扭蛋 滑动条终点","type":"hint-point","annotationId":"5ba55176-f5a1-4d3f-8fd8-75e868715ad8","useHintRect":false,"description":"扭蛋数量滑动条的左端点置0时的终点"}},"annotations":[{"id":"045e21c4-350b-4b6c-bcb7-aeb007da05b3","type":"point","data":{"x":476,"y":898}},{"id":"5ba55176-f5a1-4d3f-8fd8-75e868715ad8","type":"point","data":{"x":230,"y":898}}]}

View File

@ -0,0 +1,127 @@
"""扭蛋机,支持任意次数的任意扭蛋类型"""
import logging
from kotonebot import task, action, device, image, sleep
from kotonebot.backend.image import TemplateMatchResult
from . import R
from .common import conf
from .actions.scenes import at_home, goto_home
logger = logging.getLogger(__name__)
@action('抽某种类型的扭蛋times次')
def draw_capsule_toys(button: TemplateMatchResult, times: int):
"""
抽某种类型的扭蛋N次
:param button: 扭蛋按钮
:param times: 抽取次数
"""
device.click(button)
sleep(0.5)
device.swipe(
R.Daily.CapsuleToys.SliderStartPoint.x,
R.Daily.CapsuleToys.SliderStartPoint.y,
R.Daily.CapsuleToys.SliderEndPoint.x,
R.Daily.CapsuleToys.SliderEndPoint.y,
duration=1.0
)
sleep(0.5)
# 点击加号按钮
add_button = image.expect_wait(R.Daily.ButtonShopCountAdd, timeout=5)
for _ in range(times):
device.click(add_button)
sleep(0.5)
# 点击确认按钮
device.click(image.expect_wait(R.Common.ButtonConfirm, timeout=5))
sleep(0.5)
# 点击关闭按钮(这里同时处理了两种情况:成功,关闭提示页面;扭蛋次数不足,关闭抽扭蛋页面)
if image.wait_for(R.Common.ButtonIconClose, timeout=5):
device.click()
sleep(1)
@action('获取抽扭蛋按钮')
def get_capsule_toys_draw_buttons():
"""
在扭蛋页面中获取两个抽扭蛋按钮并按y轴排序
"""
buttons = image.find_all(R.Daily.ButtonShopCapsuleToysDraw)
if len(buttons) != 2:
logger.error('Failed to find 2 capsule toys buttons.')
return []
# 按y轴排序
buttons.sort(key=lambda x: x.position[1])
return buttons
@task('扭蛋机')
def capsule_toys():
"""
扭蛋机支持任意次数的任意扭蛋类型
自动化思路\n
进入扭蛋机页面后可以发现扭蛋机总共有4种类型\n
通过硬编码的滑动翻页把每两种扭蛋分为同一页
第一页好友扭蛋+感性扭蛋
第二页逻辑扭蛋+非凡扭蛋\n
划到某一页之后识别截图中所有抽扭蛋按钮再按照y轴排序即可以实现选择扭蛋类型
"""
#[screenshots/shop/capsule_toys_upper.png]
#[screenshots/shop/capsule_toys_lower.png]
if not conf().capsule_toys.enabled:
logger.info('"Capsule Toys" is disabled.')
return
if not at_home():
goto_home()
# 进入扭蛋机页面
logger.info('Entering Capsule Toys page')
device.click(image.expect_wait(R.Daily.ButtonShop, timeout=5))
device.click(image.expect_wait(R.Daily.ButtonShopCapsuleToys, timeout=5))
sleep(1)
# 处理好友扭蛋和感性扭蛋
buttons = get_capsule_toys_draw_buttons();
if len(buttons) != 2:
return
if conf().capsule_toys.friend_capsule_toys_count > 0:
draw_capsule_toys(buttons[0], conf().capsule_toys.friend_capsule_toys_count)
if conf().capsule_toys.sense_capsule_toys_count > 0:
draw_capsule_toys(buttons[1], conf().capsule_toys.sense_capsule_toys_count)
# 划到第二页
device.swipe(
R.Daily.CapsuleToys.NextPageStartPoint.x,
R.Daily.CapsuleToys.NextPageStartPoint.y,
R.Daily.CapsuleToys.NextPageEndPoint.x,
R.Daily.CapsuleToys.NextPageEndPoint.y,
duration=2.0 # 划慢点,确保精确定位
# FIXME: adb不支持swipe duration失效
)
sleep(1) # 等待滑动静止由于swipe duration失效所以这里需要手动等待
# 处理逻辑扭蛋扭蛋和非凡扭蛋
buttons = get_capsule_toys_draw_buttons();
if len(buttons) != 2:
return
if conf().capsule_toys.logic_capsule_toys_count > 0:
draw_capsule_toys(buttons[0], conf().capsule_toys.logic_capsule_toys_count)
if conf().capsule_toys.anomaly_capsule_toys_count > 0:
draw_capsule_toys(buttons[1], conf().capsule_toys.anomaly_capsule_toys_count)
if __name__ == '__main__':
import logging
logging.basicConfig(level=logging.INFO, format='[%(asctime)s] [%(levelname)s] [%(name)s] [%(funcName)s] [%(lineno)d] %(message)s')
logger.setLevel(logging.DEBUG)
capsule_toys()

View File

@ -1,3 +1,4 @@
from gc import enable
import os
from importlib import resources
from typing import Literal, Dict, NamedTuple, Tuple, TypeVar, Generic
@ -434,6 +435,22 @@ class UpgradeSupportCardConfig(ConfigBaseModel):
enabled: bool = False
"""是否启用支援卡升级"""
class CapsuleToysConfig(ConfigBaseModel):
enabled: bool = False
"""是否启用扭蛋机"""
friend_capsule_toys_count: int = 0
"""好友扭蛋机次数"""
sense_capsule_toys_count: int = 0
"""感性扭蛋机次数"""
logic_capsule_toys_count: int = 0
"""理性扭蛋机次数"""
anomaly_capsule_toys_count: int = 0
"""非凡扭蛋机次数"""
class TraceConfig(ConfigBaseModel):
recommend_card_detection: bool = False
"""跟踪推荐卡检测"""
@ -479,6 +496,9 @@ class BaseConfig(ConfigBaseModel):
upgrade_support_card: UpgradeSupportCardConfig = UpgradeSupportCardConfig()
"""支援卡升级配置"""
capsule_toys: CapsuleToysConfig = CapsuleToysConfig()
"""扭蛋机配置"""
trace: TraceConfig = TraceConfig()
"""跟踪配置"""

View File

@ -1,3 +1,4 @@
from operator import gt
import os
import zipfile
import logging
@ -13,9 +14,9 @@ import gradio as gr
from kotonebot.backend.context import task_registry, ContextStackVars
from kotonebot.config.manager import load_config, save_config
from kotonebot.tasks import upgrade_support_card
from kotonebot.tasks import capsule_toys, upgrade_support_card
from kotonebot.tasks.common import (
BaseConfig, APShopItems, ClubRewardConfig, PurchaseConfig, ActivityFundsConfig,
BaseConfig, APShopItems, CapsuleToysConfig, ClubRewardConfig, PurchaseConfig, ActivityFundsConfig,
PresentsConfig, AssignmentConfig, ContestConfig, ProduceConfig,
MissionRewardConfig, PIdol, DailyMoneyShopItems, ProduceAction,
RecommendCardDetectionMode, TraceConfig, StartGameConfig, UpgradeSupportCardConfig
@ -268,6 +269,12 @@ class KotoneBotUI:
selected_note: DailyMoneyShopItems,
# upgrade support card
upgrade_support_card_enabled: bool,
# capsule toys
capsule_toys_enabled: bool,
friend_capsule_toys_count: int,
sense_capsule_toys_count: int,
logic_capsule_toys_count: int,
anomaly_capsule_toys_count: int,
# start game
start_game_enabled: bool,
start_through_kuyo: bool,
@ -343,6 +350,13 @@ class KotoneBotUI:
upgrade_support_card=UpgradeSupportCardConfig(
enabled=upgrade_support_card_enabled
),
capsule_toys=CapsuleToysConfig(
enabled=capsule_toys_enabled,
friend_capsule_toys_count=friend_capsule_toys_count,
sense_capsule_toys_count=sense_capsule_toys_count,
logic_capsule_toys_count=logic_capsule_toys_count,
anomaly_capsule_toys_count=anomaly_capsule_toys_count
),
start_game=StartGameConfig(
enabled=start_game_enabled,
start_through_kuyo=start_through_kuyo,
@ -721,6 +735,52 @@ class KotoneBotUI:
)
return club_reward_enabled, selected_note
def _create_capsule_toys_settings(self) -> Tuple[gr.Checkbox, gr.Number, gr.Number, gr.Number, gr.Number]:
with gr.Column():
gr.Markdown("### 扭蛋设置")
capsule_toys_enabled = gr.Checkbox(
label="是否启用自动扭蛋机",
value=self.current_config.options.capsule_toys.enabled,
info=CapsuleToysConfig.model_fields['enabled'].description
)
min_value = 0
max_value = 10
with gr.Group(visible=self.current_config.options.capsule_toys.enabled) as capsule_toys_group:
friend_capsule_toys_count = gr.Number(
value=self.current_config.options.capsule_toys.friend_capsule_toys_count,
label="好友扭蛋机的扭蛋次数",
info=CapsuleToysConfig.model_fields['friend_capsule_toys_count'].description,
minimum=0,
maximum=5
)
sense_capsule_toys_count = gr.Number(
value=self.current_config.options.capsule_toys.sense_capsule_toys_count,
label="感性扭蛋机的扭蛋次数",
info=CapsuleToysConfig.model_fields['sense_capsule_toys_count'].description,
minimum=0,
maximum=5
)
logic_capsule_toys_count = gr.Number(
value=self.current_config.options.capsule_toys.logic_capsule_toys_count,
label="逻辑扭蛋机的扭蛋次数",
info=CapsuleToysConfig.model_fields['logic_capsule_toys_count'].description,
minimum=0,
maximum=5
)
anomaly_capsule_toys_count = gr.Number(
value=self.current_config.options.capsule_toys.anomaly_capsule_toys_count,
label="非凡扭蛋机的扭蛋次数",
info=CapsuleToysConfig.model_fields['anomaly_capsule_toys_count'].description,
minimum=0,
maximum=5
)
capsule_toys_enabled.change(
fn=lambda x: gr.Group(visible=x),
inputs=[capsule_toys_enabled],
outputs=[capsule_toys_group]
)
return capsule_toys_enabled, friend_capsule_toys_count, sense_capsule_toys_count, logic_capsule_toys_count, anomaly_capsule_toys_count
def _create_start_game_settings(self) -> Tuple[gr.Checkbox, gr.Checkbox, gr.Textbox, gr.Textbox]:
with gr.Column():
@ -852,6 +912,8 @@ class KotoneBotUI:
info=UpgradeSupportCardConfig.model_fields['enabled'].description
)
capsule_toys_settings = self._create_capsule_toys_settings()
# 跟踪设置
with gr.Column():
gr.Markdown("### 跟踪设置")
@ -883,6 +945,7 @@ class KotoneBotUI:
mission_reward,
*club_reward_settings,
upgrade_support_card_enabled,
*capsule_toys_settings,
*start_game_settings
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 KiB