mower-ng/mower/utils/config/conf.py
Elaina 0ce8c63594
All checks were successful
ci/woodpecker/push/check_format Pipeline was successful
引航者conf
2025-07-04 16:00:47 +08:00

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"