All checks were successful
ci/woodpecker/push/check_format Pipeline was successful
504 lines
15 KiB
Python
504 lines
15 KiB
Python
from datetime import timedelta
|
|
from typing import Literal, get_args, get_origin
|
|
|
|
from pydantic import BaseModel, model_validator
|
|
from pydantic_core import PydanticUndefined
|
|
|
|
from mower.utils.scene import Scene
|
|
|
|
|
|
class ConfModel(BaseModel):
|
|
@model_validator(mode="before")
|
|
@classmethod
|
|
def nested_defaults(cls, data):
|
|
for name, field in cls.model_fields.items():
|
|
expected_type = field.annotation
|
|
if name not in data:
|
|
if field.default is PydanticUndefined:
|
|
data[name] = expected_type()
|
|
else:
|
|
data[name] = field.default
|
|
continue
|
|
value = data[name]
|
|
|
|
# 处理嵌套的 BaseModel
|
|
if isinstance(field.annotation, type) and issubclass(
|
|
field.annotation, BaseModel
|
|
):
|
|
if not isinstance(value, dict): # 确保嵌套类型为字典
|
|
value = {}
|
|
# 递归调用嵌套模型的验证器
|
|
data[name] = field.annotation(**value)
|
|
|
|
# 检查 Literal 类型并修正
|
|
elif get_origin(expected_type) is Literal:
|
|
valid_literals = get_args(expected_type)
|
|
if value not in valid_literals:
|
|
# 修正为默认值
|
|
data[name] = (
|
|
field.default
|
|
if field.default is not PydanticUndefined
|
|
else None
|
|
)
|
|
|
|
elif get_origin(expected_type) is dict:
|
|
key_type, val_type = get_args(expected_type)
|
|
result = field.default
|
|
for key, val in value.items():
|
|
if not isinstance(key, key_type):
|
|
continue
|
|
if key not in field.default:
|
|
continue
|
|
if not isinstance(val, val_type):
|
|
continue
|
|
result[key] = val
|
|
data[name] = result
|
|
|
|
return data
|
|
|
|
|
|
class CluePart(ConfModel):
|
|
class CreditFightConf(ConfModel):
|
|
direction: str = "Right"
|
|
"部署方向"
|
|
operator: str = "风笛"
|
|
"使用干员"
|
|
skill: int = 1
|
|
"技能"
|
|
squad: int = 1
|
|
"编队序号"
|
|
x: int = 5
|
|
"横坐标"
|
|
y: int = 3
|
|
"纵坐标"
|
|
apply_friend: bool = False
|
|
"优先借陌生人的助战,并申请好友"
|
|
|
|
credit_fight: CreditFightConf
|
|
"信用作战设置"
|
|
leifeng_mode: int = 1
|
|
"雷锋模式"
|
|
maa_mall_blacklist: str = "加急许可,碳,碳素,家具零件"
|
|
"黑名单"
|
|
maa_mall_buy: str = "招聘许可,技巧概要·卷2"
|
|
"优先购买"
|
|
maa_mall_ignore_blacklist_when_full: bool = False
|
|
"信用溢出时无视黑名单"
|
|
|
|
|
|
class EmailPart(ConfModel):
|
|
class CustomSMTPServerConf(ConfModel):
|
|
enable: bool = False
|
|
"启用自定义邮箱"
|
|
server: str = ""
|
|
"SMTP服务器"
|
|
encryption: str = "starttls"
|
|
"加密方式"
|
|
ssl_port: int = 587
|
|
"端口号"
|
|
|
|
mail_enable: int = 0
|
|
"邮件提醒"
|
|
account: str = ""
|
|
"邮箱用户名"
|
|
pass_code: str = ""
|
|
"邮箱密码"
|
|
recipient: list[str] = []
|
|
"收件人"
|
|
custom_smtp_server: CustomSMTPServerConf
|
|
"自定义邮箱"
|
|
mail_subject: str = "[mower-ng通知]"
|
|
"标题前缀"
|
|
notification_level: str = "INFO"
|
|
"邮件通知等级"
|
|
|
|
|
|
class EmulatorPart(ConfModel):
|
|
class EmulatorConf(ConfModel):
|
|
name: str = "MuMu12"
|
|
"名称"
|
|
index: str | int = "0"
|
|
"多开编号"
|
|
emulator_folder: str = ""
|
|
"文件夹"
|
|
wait_time: int = 30
|
|
"启动时间"
|
|
hotkey: str = ""
|
|
"老板键"
|
|
|
|
class CustomScreenshotConf(ConfModel):
|
|
command: str = "adb -s 127.0.0.1:5555 shell screencap -p 2>/dev/null"
|
|
"截图命令"
|
|
|
|
class TapToLaunchGameConf(ConfModel):
|
|
enable: bool = False
|
|
"点击屏幕启动游戏"
|
|
x: int = 0
|
|
"横坐标"
|
|
y: int = 0
|
|
"纵坐标"
|
|
|
|
class DroidCastConf(ConfModel):
|
|
orientation: Literal["portrait", "landscape"] = "landscape"
|
|
"屏幕方向"
|
|
rotate: Literal[0, 90, 180, 270] = 0
|
|
"截图旋转角度"
|
|
|
|
class MuMuMultiAppConf(ConfModel):
|
|
app_index: int = 0
|
|
"应用分身编号"
|
|
|
|
adb: str = "127.0.0.1:16384"
|
|
"ADB连接地址"
|
|
emulator: EmulatorConf
|
|
"模拟器"
|
|
maa_adb_path: str = ""
|
|
"ADB路径"
|
|
package_type: int = 1
|
|
"游戏服务器"
|
|
droidcast: DroidCastConf
|
|
"DroidCast截图设置"
|
|
custom_screenshot: CustomScreenshotConf
|
|
"自定义截图"
|
|
tap_to_launch_game: TapToLaunchGameConf
|
|
"点击屏幕启动游戏"
|
|
mumu_multi_app: MuMuMultiAppConf
|
|
"MuMu应用分身"
|
|
strategy_when_idle: Literal[
|
|
"do_nothing", "switch_to_home", "exit_game", "close_emulator"
|
|
] = "switch_to_home"
|
|
"任务结束时采取的策略"
|
|
kill_server_when_idle: bool = True
|
|
"任务结束时关闭adb-server"
|
|
screencap_strategy: Literal[
|
|
"adb",
|
|
"droidcast",
|
|
"droidcast_raw",
|
|
"mumuipc",
|
|
"ldopengl",
|
|
"diy",
|
|
"scrcpy",
|
|
] = "droidcast"
|
|
"截图方案"
|
|
control_strategy: Literal["scrcpy", "mumuipc"] = "scrcpy"
|
|
"触控方案"
|
|
app_control_strategy: Literal["adb", "mumumanager", "scrcpy", "adb_multiuser"] = (
|
|
"adb"
|
|
)
|
|
"应用控制方案"
|
|
|
|
|
|
class ExtraPart(ConfModel):
|
|
class WebViewConf(ConfModel):
|
|
port: int = 58000
|
|
"端口号"
|
|
token: str = ""
|
|
"远程连接密钥"
|
|
|
|
start_automatically: bool = False
|
|
"启动后自动开始任务"
|
|
webview: WebViewConf
|
|
"GUI相关设置"
|
|
screenshot_interval: int = 500
|
|
"截图最短间隔(毫秒)"
|
|
screenshot: float = 24
|
|
"截图保留时长(小时)"
|
|
waiting_scene: dict[str, list] = {
|
|
str(Scene.CONNECTING): [1000, 20],
|
|
str(Scene.UNKNOWN): [1000, 120],
|
|
str(Scene.UNKNOWN_WITH_NAVBAR): [0, 10],
|
|
str(Scene.UNKNOWN_ROGUE): [0, 10],
|
|
str(Scene.LOADING): [3000, 120],
|
|
str(Scene.LOGIN_LOADING): [3000, 120],
|
|
str(Scene.LOGIN_MAIN_NOENTRY): [3000, 120],
|
|
str(Scene.OPERATOR_ONGOING): [10000, 300],
|
|
str(Scene.DOUBLE_CONFIRM): [0, 5],
|
|
}
|
|
"等待时间"
|
|
telemetry: bool = True
|
|
"上报数据"
|
|
|
|
|
|
class FightPart(ConfModel):
|
|
class ChooseConf(ConfModel):
|
|
add_low_trust_opers: bool = False
|
|
"补充低信赖干员"
|
|
|
|
class SupportConf(ConfModel):
|
|
elite: int = 2
|
|
"精英化"
|
|
level: int = 60
|
|
"等级"
|
|
refresh: int = 10
|
|
"最大刷新次数"
|
|
|
|
class WorkConf(ConfModel):
|
|
class WorkItem(ConfModel):
|
|
stage: str = ""
|
|
"关卡"
|
|
retry_times: int = 2
|
|
"重试次数"
|
|
filename: str = ""
|
|
"作业文件"
|
|
|
|
squad: int = 0
|
|
"作战编队"
|
|
use_medicine: bool = True
|
|
"是否使用理智药"
|
|
enemy_breach_strategy: Literal["overlook", "give_up", "restart_game"] = (
|
|
"give_up"
|
|
)
|
|
"被敌人进入时采取的策略"
|
|
intelligent_select: bool = True
|
|
"智能选择"
|
|
works: list[WorkItem] = []
|
|
"自动作战列表"
|
|
|
|
work_enable: bool = False
|
|
"是否执行自动作战列表"
|
|
avatar_recog_pause: bool = False
|
|
"作战中识别干员时暂停"
|
|
first_swipe_duration: int = 400
|
|
"部署干员第一段滑动持续时间"
|
|
second_swipe_duration: int = 200
|
|
"部署干员第二段滑动(滑方向)持续时间"
|
|
choose: ChooseConf
|
|
"自动选干员设置"
|
|
support: SupportConf
|
|
"借助战要求"
|
|
work: WorkConf
|
|
"自动作战列表设置"
|
|
|
|
|
|
class LongTaskPart(ConfModel):
|
|
class RogueConf(ConfModel):
|
|
squad: Literal["蓝图测绘分队", "点刺成锭分队"] = "点刺成锭分队"
|
|
"分队"
|
|
roles: str = ""
|
|
"职业"
|
|
core_char: str = ""
|
|
"干员"
|
|
use_support: bool = False
|
|
"开局干员使用助战"
|
|
use_nonfriend_support: bool = False
|
|
"开局干员使用非好友助战"
|
|
mode: int = 1
|
|
"策略"
|
|
refresh_trader_with_dice: bool = False
|
|
"刷新商店(指路鳞)"
|
|
expected_collapsal_paradigms: list[str] = [
|
|
"目空一些",
|
|
"睁眼瞎",
|
|
"图像损坏",
|
|
"一抹黑",
|
|
]
|
|
"需要刷的坍缩范式"
|
|
|
|
class ReclamationAlgorithmConf(ConfModel):
|
|
timeout: int = 30
|
|
"生息演算和隐秘战线的超时时间"
|
|
|
|
class SecretFrontConf(ConfModel):
|
|
target: str = "结局A"
|
|
"隐秘战线结局"
|
|
auto_next: bool = True
|
|
"当前结局通关后自动切换至下一结局"
|
|
stop_after_finish: bool = True
|
|
"通关后停止大型任务"
|
|
|
|
class SSSConf(ConfModel):
|
|
choose_agent: bool = True
|
|
"自动编队"
|
|
mode: Literal["normal", "ex"] = "normal"
|
|
"难度:正常为normal,应急为ex"
|
|
finish_while_full: bool = True
|
|
"模组刷满时直接结束"
|
|
ope_limit_stage: int = 6
|
|
"使用代理卡最低层数要求"
|
|
|
|
class TrialsConf(ConfModel):
|
|
level: Literal["TN-1", "TN-2", "TN-3", "TN-4"] = "TN-1"
|
|
"试炼关卡"
|
|
mode: Literal["init", "direct", "grand"] = "init"
|
|
"难度:初始试炼/定向试炼/恢宏试炼"
|
|
|
|
maa_long_task_type: str = "rogue"
|
|
"大型任务类型"
|
|
maa_rg_theme: Literal["Phantom", "Mizuki", "Sami", "Sarkaz"] = "Sarkaz"
|
|
"肉鸽主题"
|
|
rogue: RogueConf
|
|
"肉鸽设置"
|
|
reclamation_algorithm: ReclamationAlgorithmConf
|
|
"生息演算"
|
|
secret_front: SecretFrontConf
|
|
"隐秘战线"
|
|
sss: SSSConf
|
|
"保全派驻"
|
|
trials: TrialsConf
|
|
"引航者试炼"
|
|
vb_full_stop: bool = True
|
|
"矢量突破刷满后停止"
|
|
|
|
|
|
class MaaPart(ConfModel):
|
|
maa_path: str = "D:\\MAA-v4.13.0-win-x64"
|
|
maa_conn_preset: str = "General"
|
|
maa_touch_option: str = "maatouch"
|
|
|
|
|
|
class RecruitPart(ConfModel):
|
|
recruit_enable: bool = True
|
|
"公招开关"
|
|
recruit_robot: bool = True
|
|
"保留支援机械标签"
|
|
recruitment_permit: int = 30
|
|
"三星招募阈值"
|
|
recruit_gap: float = 9
|
|
"启动间隔"
|
|
recruit_auto_5: int = 1
|
|
"五星招募策略,1自动,2手动"
|
|
recruit_auto_only5: bool = False
|
|
"五星词条组合唯一时自动选择"
|
|
|
|
|
|
class RegularTaskPart(ConfModel):
|
|
class WeeklyConf(BaseModel):
|
|
general: list[str]
|
|
"常驻关卡"
|
|
custom: list[str]
|
|
"自定义关卡"
|
|
|
|
delete_read_mail: bool = False
|
|
"删除已读邮件"
|
|
maa_gap: float = 3
|
|
"日常任务间隔"
|
|
use_all_medicine: bool = False
|
|
"自动使用全部理智药"
|
|
maa_expiring_medicine: bool = True
|
|
"自动使用将要过期(约3天)的理智药"
|
|
exipring_medicine_on_weekend: bool = False
|
|
"仅在周末使用将要过期的理智药"
|
|
originite: int = 0
|
|
"用于刷关的源石数量,use_all_medicine为True时生效"
|
|
weekly_plan: list[WeeklyConf] = [{"general": [], "custom": []}] * 7
|
|
"刷理智周计划,周一开始"
|
|
custom_stages: list[str | None] = [None] * 3
|
|
"刷理智自定义关卡"
|
|
activity_shop_blacklist: list[str] = ["数据增补仪", "数据增补条", "家具零件"]
|
|
|
|
|
|
class RIICPart(ConfModel):
|
|
class RunOrderGrandetModeConf(ConfModel):
|
|
enable: bool = True
|
|
"葛朗台跑单开关"
|
|
buffer_time: int = 15
|
|
"缓冲时间"
|
|
back_to_index: bool = False
|
|
"跑单前返回基建首页"
|
|
|
|
drone_count_limit: int = 100
|
|
"无人机使用阈值"
|
|
drone_room: str = ""
|
|
"无人机使用房间"
|
|
drone_interval: float = 3
|
|
"无人机加速间隔"
|
|
free_blacklist: str = ""
|
|
"宿舍黑名单"
|
|
reload_room: str = ""
|
|
"搓玉补货房间"
|
|
run_order_delay: float = 3
|
|
"跑单前置延时"
|
|
resting_threshold: float = 0.65
|
|
"心情阈值"
|
|
run_order_grandet_mode: RunOrderGrandetModeConf
|
|
"葛朗台跑单"
|
|
free_room: bool = False
|
|
"宿舍不养闲人模式"
|
|
trust_limit: int = 200
|
|
"信赖值阈值"
|
|
|
|
|
|
class SchedulerPart(ConfModel):
|
|
task: dict[str, bool] = {
|
|
"mower.solvers.base_manager.BaseManager": True,
|
|
"mower.solvers.sign_in.SignInManager": True,
|
|
"mower.solvers.credit.CreditSolver": True,
|
|
"mower.solvers.infra.report.ReportSolver": True,
|
|
"mower.solvers.infra.switch_assistants.SwitchAssistantsSolver": True,
|
|
"mower.solvers.infra.switch_activity_users.SwitchActivityUsersSolver": False,
|
|
"mower.solvers.skland.SKLand": False,
|
|
"mower.solvers.mail.MailSolver": True,
|
|
"mower.solvers.fight.credit_fight.CreditFight": True,
|
|
"mower.solvers.trade_token.TradeTokenSolver": False,
|
|
"mower.solvers.infra.clue.ClueManager": True,
|
|
"mower.solvers.recruit.RecruitSolver": True,
|
|
"mower.solvers.fight.copy_works.CopyWorksSolver": False,
|
|
"mower.solvers.operation.OperationManager": True,
|
|
"mower.solvers.depotREC.depotREC": False,
|
|
"mower.solvers.mission.MissionSolver": True,
|
|
"mower.solvers.exit.ExitSolver": True,
|
|
"mower.solvers.long_task.LongTask": True,
|
|
}
|
|
"启用任务"
|
|
postpone_non_riic: bool = True
|
|
"将基建以外的任务推迟到基建任务后执行"
|
|
|
|
|
|
class SKLandPart(ConfModel):
|
|
class SKLandAccount(BaseModel):
|
|
account: str = ""
|
|
"账号"
|
|
password: str = ""
|
|
"密码"
|
|
cultivate_select: bool = True
|
|
"服务器"
|
|
isCheck: bool = True
|
|
"签到"
|
|
sign_in_bilibili: bool = False
|
|
"官服签到"
|
|
sign_in_official: bool = False
|
|
"B服签到"
|
|
|
|
skland_info: list[SKLandAccount] = []
|
|
"森空岛账号"
|
|
|
|
|
|
class Conf(
|
|
CluePart,
|
|
EmailPart,
|
|
EmulatorPart,
|
|
ExtraPart,
|
|
FightPart,
|
|
LongTaskPart,
|
|
MaaPart,
|
|
RecruitPart,
|
|
RegularTaskPart,
|
|
RIICPart,
|
|
SchedulerPart,
|
|
SKLandPart,
|
|
):
|
|
@property
|
|
def APPNAME(self):
|
|
return (
|
|
"com.hypergryph.arknights"
|
|
if self.package_type == 1
|
|
else "com.hypergryph.arknights.bilibili"
|
|
)
|
|
|
|
@property
|
|
def run_order_buffer_time(self):
|
|
"""
|
|
> 0 葛朗台跑单的缓冲时间
|
|
<= 0 无人机跑单
|
|
"""
|
|
if self.run_order_grandet_mode.enable:
|
|
return self.run_order_grandet_mode.buffer_time
|
|
return -1
|
|
|
|
@property
|
|
def sc_interval(self) -> timedelta:
|
|
return timedelta(milliseconds=self.screenshot_interval)
|
|
|
|
@property
|
|
def no_restart_emulator(self):
|
|
return self.emulator.name == "" or self.app_control_strategy == "adb_multiuser"
|