Merge branch 'main' of github.com:XcantloadX/kotones-auto-assistant into main

This commit is contained in:
YXHXianYu 2025-03-14 21:57:09 +08:00
commit 759e8c3888
8 changed files with 686 additions and 141 deletions

View File

@ -10,14 +10,14 @@
* 包括 AP 商店和金币商店
* 可以购买推荐商品,或者指定要购买的物品
* 低配版自动培育
* 支持 REGULAR 和 PRO 模式<sup>1</sup>
* 支持指定 P 偶像<sup>2</sup>
* 支持 REGULAR 和 PRO 模式
* 支持指定 P 偶像<sup>1</sup>
* 支持指定使用增强道具(笔记数量和 Pt 数量提升)
* 暂时**只能使用自动编成**回忆和支援卡
* 对于非练习周,如果没有推荐休息,暂时只会按顺序执行活动支给(活動支給)、文化课(授業)、休息这三种,不支持外出(おでかけ)和咨询(相談)
* 对于非练习周,如果没有推荐休息,暂时只会按顺序执行活动支给(活動支給)、文化课(授業)、外出(おでかけ)、休息这四种,不支持咨询(相談)
* 领取技能卡和 P 饮料时,固定领取第一个。若 P 饮料溢出,则不领取
<sup>1 2</sup> 见后文的注意事项
<sup>1</sup> 见后文的注意事项
## 安装
### 模拟器要求
@ -31,6 +31,7 @@ TODO
### 技术用户
琴音小助手通过 pip 分发,因此你可以执行下面的命令来安装:
```bash
# Python == 3.10
pip install ksaa
```
@ -63,36 +64,43 @@ kaa
TODO
### 注意事项
> [!NOTE]
> 建议使用 **REGULAR 模式**
> 因为 PRO 模式还没有经过充分测试。
> [!IMPORTANT]
> 尽管名字叫“琴音小助手”,但是强烈建议你
> **不要在自动培育中使用琴音**
>
> 因为琴音的黄色服饰会严重干扰推荐卡的检测,
> 导致打出错误的技能卡。
> [!IMPORTANT]
> 建议**使用亲密度至少为 7 的偶像**进行培育,
> 因为琴音小助手暂时无法处理亲密度提升事件。
## 开发
见[DEVELOPMENT.md](./docs/DEVELOPMENT.md)
见 [DEVELOPMENT.md](./docs/DEVELOPMENT.md)
## 路线图
下面是待实现的功能:
下面是待实现的功能:
(带删除线标记的为已完成)
* 培育
* 允许指定领取 P 饮料、P 物品、技能卡的领取选择优先级
* 允许指定行动优先级
* ~~允许指定行动优先级~~
* 自动使用 P 饮料
* 支持非凡(アノマリーノート)属性偶像的自动培育
* 支持琴音的自动培育
* ~~支持琴音的自动培育~~
* 允许优先选择活动加成高的偶像进行培育
* 支持 MASTER 培育、NIA 培育
* 竞赛
* 按分数差距优先选择
* 自动硬币扭蛋(コインガシャ)
* 模拟器启停
* 常驻运行与自动运行
* 调度
* ~~模拟器启停~~
* 常驻运行与自动运行
* 命令行接口
* 接入 ALAS
* UI
* 使用 Flet/Flutter 重写 UI
* 分离脚本与 UI允许 UI 与脚本分别独立运行
* 启动器
* 使用 C# 替换当前的简易 .bat 文件
* Linux 支持
* Android 支持
* 使用 Python for Android 移植 kaa 到 Android 平台
* 使用 Shizuku 执行 ADB 命令
* 使用 Pyjnius 绕过 ADB ,使用无障碍直接控制设备
* 开发工具
* 将开发工具通过 VSCode 扩展与 VSCode 整合

114
config.json.v1.json Normal file
View File

@ -0,0 +1,114 @@
{
"version": 1,
"user_configs": [
{
"name": "默认配置",
"id": "8eb503b546334df5a95f4818120d7c36",
"category": "default",
"description": "默认配置",
"backend": {
"type": "custom",
"adb_ip": "127.0.0.1",
"adb_port": 5555,
"screenshot_impl": "adb",
"check_emulator": true,
"emulator_path": "E:\\Programs\\leidian\\LDPlayerVK\\dnplayer.exe"
},
"keep_screenshots": true,
"options": {
"purchase": {
"enabled": true,
"money_enabled": true,
"money_items": [
14,
15,
16,
17,
18,
19,
20,
21,
22
],
"money_refresh_on": "never",
"ap_enabled": true,
"ap_items": [
0,
1,
2,
3
]
},
"activity_funds": {
"enabled": true
},
"presents": {
"enabled": true
},
"assignment": {
"enabled": true,
"mini_live_reassign_enabled": true,
"mini_live_duration": 12,
"online_live_reassign_enabled": true,
"online_live_duration": 12
},
"contest": {
"enabled": true,
"select_which_contestant": 3
},
"produce": {
"enabled": false,
"mode": "regular",
"produce_count": 1,
"idols": [],
"memory_sets": [],
"support_card_sets": [],
"auto_set_memory": false,
"auto_set_support_card": false,
"use_pt_boost": false,
"use_note_boost": false,
"follow_producer": false,
"self_study_lesson": "dance",
"prefer_lesson_ap": false,
"actions_order": [
"recommended",
"visual",
"vocal",
"dance",
"allowance",
"outing",
"study",
"rest"
],
"recommend_card_detection_mode": "normal"
},
"mission_reward": {
"enabled": true
},
"club_reward": {
"enabled": true,
"selected_note": 11
},
"upgrade_support_card": {
"enabled": true
},
"capsule_toys": {
"enabled": true,
"friend_capsule_toys_count": 0,
"sense_capsule_toys_count": 1,
"logic_capsule_toys_count": 0,
"anomaly_capsule_toys_count": 0
},
"trace": {
"recommend_card_detection": false
},
"start_game": {
"enabled": true,
"start_through_kuyo": true,
"game_package_name": "com.bandinamcoent.idolmaster_gakuen",
"kuyo_package_name": "org.kuyo.game"
}
}
}
]
}

View File

@ -1,14 +1,20 @@
from gc import enable
import os
import json
import shutil
from importlib import resources
from typing import Literal, Dict, NamedTuple, Tuple, TypeVar, Generic
from typing import Literal, Dict, NamedTuple, Tuple, TypeVar, Generic, Any
from typing_extensions import assert_never
from enum import IntEnum, Enum
from pydantic import BaseModel, ConfigDict
# TODO: from kotonebot import config (context) 会和 kotonebot.config 冲突
from kotonebot import logging
from kotonebot.backend.context import config
logger = logging.getLogger(__name__)
T = TypeVar('T')
class ConfigEnum(Enum):
def display(self) -> str:
@ -31,86 +37,269 @@ class APShopItems(IntEnum):
"""回忆再生成券"""
class PIdol(Enum):
倉本千奈_BASE = 0
十王星南_BASE = 100
姫崎莉波_BASE = 200
月村手毬_BASE = 300
有村麻央_BASE = 400
篠泽广_BASE = 500
紫云清夏_BASE = 600
花海佑芽_BASE = 700
花海咲季_BASE = 800
葛城リーリヤ_BASE = 900
藤田ことね_BASE = 1000
class PIdol(IntEnum):
"""P偶像"""
倉本千奈_Campusmode = ["倉本千奈", "Campus", "mode", "!!"]
倉本千奈_WonderScale = ["倉本千奈", "Wonder", "Scale"]
倉本千奈_ようこそ初星温泉 = ["倉本千奈", "ようこそ初星温泉"]
倉本千奈_仮装狂騒曲 = ["倉本千奈", "仮装狂騒曲"]
倉本千奈_初心 = ["倉本千奈", "初心"]
倉本千奈_学園生活 = ["倉本千奈", "学園生活"]
倉本千奈_日々_発見的ステップ = ["倉本千奈", "日々、発見的ステップ!"]
倉本千奈_胸を張って一歩ずつ = ["倉本千奈", "胸を張って一歩ずつ"]
十王星南_Campusmode = ["十王星南", "Campus", "mode", "!!"]
十王星南_一番星 = ["十王星南", "一番星"]
十王星南_学園生活 = ["十王星南", "学園生活"]
十王星南_小さな野望 = ["十王星南", "小さな野望"]
姫崎莉波_clumsytrick = ["姫崎莉波", "clumsy", "trick"]
姫崎莉波_私らしさのはじまり = ["姫崎莉波", "『私らしさ』のはじまり"]
姫崎莉波_キミとセミブルー = ["姫崎莉波", "キミとセミブルー"]
姫崎莉波_Campusmode = ["姫崎莉波", "Campus", "mode", "!!"]
姫崎莉波_LUV = ["姫崎莉波", "L.U.V"]
姫崎莉波_ようこそ初星温泉 = ["姫崎莉波", "ようこそ初星温泉"]
姫崎莉波_ハッピーミルフィーユ = ["姫崎莉波", "ハッピーミルフィーユ"]
姫崎莉波_初心 = ["姫崎莉波", "初心"]
姫崎莉波_学園生活 = ["姫崎莉波", "学園生活"]
月村手毬_Lunasaymaybe = ["月村手毬", "Luna", "say", "maybe"]
月村手毬_一匹狼 = ["月村手毬", "一匹狼"]
月村手毬_Campusmode = ["月村手毬", "Campus mode!!"]
月村手毬_アイヴイ = ["月村手毬", "アイヴイ"]
月村手毬_初声 = ["月村手毬", "初声"]
月村手毬_学園生活 = ["月村手毬", "学園生活"]
月村手毬_仮装狂騒曲 = ["月村手毬", "仮装狂騒曲"]
有村麻央_Fluorite = ["有村麻央", "Fluorite"]
有村麻央_はじまりはカッコよく = ["有村麻央", "はじまりはカッコよく"]
有村麻央_Campusmode = ["有村麻央", "Campus", "mode", "!!"]
有村麻央_FeelJewelDream = ["有村麻央", "Feel", "Jewel", "Dream"]
有村麻央_キミとセミブルー = ["有村麻央", "キミとセミブルー"]
有村麻央_初恋 = ["有村麻央", "初恋"]
有村麻央_学園生活 = ["有村麻央", "学園生活"]
篠泽广_コントラスト = ["篠泽广", "コントラスト"]
篠泽广_一番向いていないこと = ["篠泽广", "一番向いていないこと"]
篠泽广_光景 = ["篠泽广", "光景"]
篠泽广_Campusmode = ["篠泽广", "Campus", "mode", "!!"]
篠泽广_仮装狂騒曲 = ["篠泽广", "仮装狂騒曲"]
篠泽广_ハッピーミルフィーユ = ["篠泽广", "ハッピーミルフィーユ"]
篠泽广_初恋 = ["篠泽广", "初恋"]
篠泽广_学園生活 = ["篠泽广", "学園生活"]
紫云清夏_TameLieOneStep = ["紫云清夏", "Tame", "Lie", "One", "Step"]
紫云清夏_カクシタワタシ = ["紫云清夏", "カクシタワタシ"]
紫云清夏_夢へのリスタート = ["紫云清夏", "夢へのリスタート"]
紫云清夏_Campusmode = ["紫云清夏", "Campus", "mode", "!!"]
紫云清夏_キミとセミブルー = ["紫云清夏", "キミとセミブルー"]
紫云清夏_初恋 = ["紫云清夏", "初恋"]
紫云清夏_学園生活 = ["紫云清夏", "学園生活"]
花海佑芽_WhiteNightWhiteWish = ["花海佑芽", "White", "Night", "Wish", "!"]
花海佑芽_学園生活 = ["花海佑芽", "学園生活"]
花海佑芽_Campusmode = ["花海佑芽", "Campus", "mode", "!!"]
花海佑芽_TheRollingRiceball = ["花海佑芽", "The", "Rolling", "Riceball"]
花海佑芽_アイドル_はじめっ = ["花海佑芽", "アイドル、はじめっ!"]
花海咲季_BoomBoomPow = ["花海咲季", "Boom", "Boom", "Pow"]
花海咲季_Campusmode = ["花海咲季", "Campus", "mode", "!!"]
花海咲季_FightingMyWay = ["花海咲季", "Fighting", "My", "Way"]
花海咲季_わたしが一番 = ["花海咲季", "わたしが一番!"]
花海咲季_冠菊 = ["花海咲季", "冠菊"]
花海咲季_初声 = ["花海咲季", "初声"]
花海咲季_古今東西ちょちょいのちょい = ["花海咲季", "古今東西ちょちょいのちょい"]
花海咲季_学園生活 = ["花海咲季", "学園生活"]
葛城リーリヤ_一つ踏み出した先に = ["葛城リーリヤ", "一つ踏み出した先に"]
葛城リーリヤ_白線 = ["葛城リーリヤ", "白線"]
葛城リーリヤ_Campusmode = ["葛城リーリヤ", "Campus", "mode", "!!"]
葛城リーリヤ_WhiteNightWhiteWish = ["葛城リーリヤ", "White", "Night", "Wish", "!"]
葛城リーリヤ_冠菊 = ["葛城リーリヤ", "冠菊"]
葛城リーリヤ_初心 = ["葛城リーリヤ", "初心"]
葛城リーリヤ_学園生活 = ["葛城リーリヤ", "学園生活"]
藤田ことね_カワイイ_はじめました = ["藤田ことね", "カワイイ", "はじめました"]
藤田ことね_世界一可愛い私 = ["藤田ことね", "世界一可愛い私"]
藤田ことね_Campusmode = ["藤田ことね", "Campus", "mode", "!!"]
藤田ことね_YellowBigBang = ["藤田ことね", "Yellow", "Big", "Bang", "!"]
藤田ことね_WhiteNightWhiteWish = ["藤田ことね", "White", "Night", "Wish", "!"]
藤田ことね_冠菊 = ["藤田ことね", "冠菊"]
藤田ことね_初声 = ["藤田ことね", "初声"]
藤田ことね_学園生活 = ["藤田ことね", "学園生活"]
倉本千奈_Campusmode = 倉本千奈_BASE + 0
倉本千奈_WonderScale = 倉本千奈_BASE + 1
倉本千奈_ようこそ初星温泉 = 倉本千奈_BASE + 2
倉本千奈_仮装狂騒曲 = 倉本千奈_BASE + 3
倉本千奈_初心 = 倉本千奈_BASE + 4
倉本千奈_学園生活 = 倉本千奈_BASE + 5
倉本千奈_日々_発見的ステップ = 倉本千奈_BASE + 6
倉本千奈_胸を張って一歩ずつ = 倉本千奈_BASE + 7
十王星南_Campusmode = 十王星南_BASE + 0
十王星南_一番星 = 十王星南_BASE + 1
十王星南_学園生活 = 十王星南_BASE + 2
十王星南_小さな野望 = 十王星南_BASE + 3
姫崎莉波_clumsytrick = 姫崎莉波_BASE + 0
姫崎莉波_私らしさのはじまり = 姫崎莉波_BASE + 1
姫崎莉波_キミとセミブルー = 姫崎莉波_BASE + 2
姫崎莉波_Campusmode = 姫崎莉波_BASE + 3
姫崎莉波_LUV = 姫崎莉波_BASE + 4
姫崎莉波_ようこそ初星温泉 = 姫崎莉波_BASE + 5
姫崎莉波_ハッピーミルフィーユ = 姫崎莉波_BASE + 6
姫崎莉波_初心 = 姫崎莉波_BASE + 7
姫崎莉波_学園生活 = 姫崎莉波_BASE + 8
月村手毬_Lunasaymaybe = 月村手毬_BASE + 0
月村手毬_一匹狼 = 月村手毬_BASE + 1
月村手毬_Campusmode = 月村手毬_BASE + 2
月村手毬_アイヴイ = 月村手毬_BASE + 3
月村手毬_初声 = 月村手毬_BASE + 4
月村手毬_学園生活 = 月村手毬_BASE + 5
月村手毬_仮装狂騒曲 = 月村手毬_BASE + 6
有村麻央_Fluorite = 有村麻央_BASE + 0
有村麻央_はじまりはカッコよく = 有村麻央_BASE + 1
有村麻央_Campusmode = 有村麻央_BASE + 2
有村麻央_FeelJewelDream = 有村麻央_BASE + 3
有村麻央_キミとセミブルー = 有村麻央_BASE + 4
有村麻央_初恋 = 有村麻央_BASE + 5
有村麻央_学園生活 = 有村麻央_BASE + 6
篠泽广_コントラスト = 篠泽广_BASE + 0
篠泽广_一番向いていないこと = 篠泽广_BASE + 1
篠泽广_光景 = 篠泽广_BASE + 2
篠泽广_Campusmode = 篠泽广_BASE + 3
篠泽广_仮装狂騒曲 = 篠泽广_BASE + 4
篠泽广_ハッピーミルフィーユ = 篠泽广_BASE + 5
篠泽广_初恋 = 篠泽广_BASE + 6
篠泽广_学園生活 = 篠泽广_BASE + 7
紫云清夏_TameLieOneStep = 紫云清夏_BASE + 0
紫云清夏_カクシタワタシ = 紫云清夏_BASE + 1
紫云清夏_夢へのリスタート = 紫云清夏_BASE + 2
紫云清夏_Campusmode = 紫云清夏_BASE + 3
紫云清夏_キミとセミブルー = 紫云清夏_BASE + 4
紫云清夏_初恋 = 紫云清夏_BASE + 5
紫云清夏_学園生活 = 紫云清夏_BASE + 6
花海佑芽_WhiteNightWhiteWish = 花海佑芽_BASE + 0
花海佑芽_学園生活 = 花海佑芽_BASE + 1
花海佑芽_Campusmode = 花海佑芽_BASE + 2
花海佑芽_TheRollingRiceball = 花海佑芽_BASE + 3
花海佑芽_アイドル_はじめっ = 花海佑芽_BASE + 4
花海咲季_BoomBoomPow = 花海咲季_BASE + 0
花海咲季_Campusmode = 花海咲季_BASE + 1
花海咲季_FightingMyWay = 花海咲季_BASE + 2
花海咲季_わたしが一番 = 花海咲季_BASE + 3
花海咲季_冠菊 = 花海咲季_BASE + 4
花海咲季_初声 = 花海咲季_BASE + 5
花海咲季_古今東西ちょちょいのちょい = 花海咲季_BASE + 6
花海咲季_学園生活 = 花海咲季_BASE + 7
葛城リーリヤ_一つ踏み出した先に = 葛城リーリヤ_BASE + 0
葛城リーリヤ_白線 = 葛城リーリヤ_BASE + 1
葛城リーリヤ_Campusmode = 葛城リーリヤ_BASE + 2
葛城リーリヤ_WhiteNightWhiteWish = 葛城リーリヤ_BASE + 3
葛城リーリヤ_冠菊 = 葛城リーリヤ_BASE + 4
葛城リーリヤ_初心 = 葛城リーリヤ_BASE + 5
葛城リーリヤ_学園生活 = 葛城リーリヤ_BASE + 6
藤田ことね_カワイイ_はじめました = 藤田ことね_BASE + 0
藤田ことね_世界一可愛い私 = 藤田ことね_BASE + 1
藤田ことね_Campusmode = 藤田ことね_BASE + 2
藤田ことね_YellowBigBang = 藤田ことね_BASE + 3
藤田ことね_WhiteNightWhiteWish = 藤田ことね_BASE + 4
藤田ことね_冠菊 = 藤田ことね_BASE + 5
藤田ことね_初声 = 藤田ことね_BASE + 6
藤田ことね_学園生活 = 藤田ことね_BASE + 7
def to_title(self) -> list[str]:
match self:
case PIdol.倉本千奈_Campusmode:
return ["倉本千奈", "Campus", "mode", "!!"]
case PIdol.倉本千奈_WonderScale:
return ["倉本千奈", "Wonder", "Scale"]
case PIdol.倉本千奈_ようこそ初星温泉:
return ["倉本千奈", "ようこそ初星温泉"]
case PIdol.倉本千奈_仮装狂騒曲:
return ["倉本千奈", "仮装狂騒曲"]
case PIdol.倉本千奈_初心:
return ["倉本千奈", "初心"]
case PIdol.倉本千奈_学園生活:
return ["倉本千奈", "学園生活"]
case PIdol.倉本千奈_日々_発見的ステップ:
return ["倉本千奈", "日々、発見的ステップ!"]
case PIdol.倉本千奈_胸を張って一歩ずつ:
return ["倉本千奈", "胸を張って一歩ずつ"]
case PIdol.十王星南_Campusmode:
return ["十王星南", "Campus", "mode", "!!"]
case PIdol.十王星南_一番星:
return ["十王星南", "一番星"]
case PIdol.十王星南_学園生活:
return ["十王星南", "学園生活"]
case PIdol.十王星南_小さな野望:
return ["十王星南", "小さな野望"]
case PIdol.姫崎莉波_clumsytrick:
return ["姫崎莉波", "clumsy", "trick"]
case PIdol.姫崎莉波_私らしさのはじまり:
return ["姫崎莉波", "『私らしさ』のはじまり"]
case PIdol.姫崎莉波_キミとセミブルー:
return ["姫崎莉波", "キミとセミブルー"]
case PIdol.姫崎莉波_Campusmode:
return ["姫崎莉波", "Campus", "mode", "!!"]
case PIdol.姫崎莉波_LUV:
return ["姫崎莉波", "L.U.V"]
case PIdol.姫崎莉波_ようこそ初星温泉:
return ["姫崎莉波", "ようこそ初星温泉"]
case PIdol.姫崎莉波_ハッピーミルフィーユ:
return ["姫崎莉波", "ハッピーミルフィーユ"]
case PIdol.姫崎莉波_初心:
return ["姫崎莉波", "初心"]
case PIdol.姫崎莉波_学園生活:
return ["姫崎莉波", "学園生活"]
case PIdol.月村手毬_Lunasaymaybe:
return ["月村手毬", "Luna", "say", "maybe"]
case PIdol.月村手毬_一匹狼:
return ["月村手毬", "一匹狼"]
case PIdol.月村手毬_Campusmode:
return ["月村手毬", "Campus mode!!"]
case PIdol.月村手毬_アイヴイ:
return ["月村手毬", "アイヴイ"]
case PIdol.月村手毬_初声:
return ["月村手毬", "初声"]
case PIdol.月村手毬_学園生活:
return ["月村手毬", "学園生活"]
case PIdol.月村手毬_仮装狂騒曲:
return ["月村手毬", "仮装狂騒曲"]
case PIdol.有村麻央_Fluorite:
return ["有村麻央", "Fluorite"]
case PIdol.有村麻央_はじまりはカッコよく:
return ["有村麻央", "はじまりはカッコよく"]
case PIdol.有村麻央_Campusmode:
return ["有村麻央", "Campus", "mode", "!!"]
case PIdol.有村麻央_FeelJewelDream:
return ["有村麻央", "Feel", "Jewel", "Dream"]
case PIdol.有村麻央_キミとセミブルー:
return ["有村麻央", "キミとセミブルー"]
case PIdol.有村麻央_初恋:
return ["有村麻央", "初恋"]
case PIdol.有村麻央_学園生活:
return ["有村麻央", "学園生活"]
case PIdol.篠泽广_コントラスト:
return ["篠泽广", "コントラスト"]
case PIdol.篠泽广_一番向いていないこと:
return ["篠泽广", "一番向いていないこと"]
case PIdol.篠泽广_光景:
return ["篠泽广", "光景"]
case PIdol.篠泽广_Campusmode:
return ["篠泽广", "Campus", "mode", "!!"]
case PIdol.篠泽广_仮装狂騒曲:
return ["篠泽广", "仮装狂騒曲"]
case PIdol.篠泽广_ハッピーミルフィーユ:
return ["篠泽广", "ハッピーミルフィーユ"]
case PIdol.篠泽广_初恋:
return ["篠泽广", "初恋"]
case PIdol.篠泽广_学園生活:
return ["篠泽广", "学園生活"]
case PIdol.紫云清夏_TameLieOneStep:
return ["紫云清夏", "Tame", "Lie", "One", "Step"]
case PIdol.紫云清夏_カクシタワタシ:
return ["紫云清夏", "カクシタワタシ"]
case PIdol.紫云清夏_夢へのリスタート:
return ["紫云清夏", "夢へのリスタート"]
case PIdol.紫云清夏_Campusmode:
return ["紫云清夏", "Campus", "mode", "!!"]
case PIdol.紫云清夏_キミとセミブルー:
return ["紫云清夏", "キミとセミブルー"]
case PIdol.紫云清夏_初恋:
return ["紫云清夏", "初恋"]
case PIdol.紫云清夏_学園生活:
return ["紫云清夏", "学園生活"]
case PIdol.花海佑芽_WhiteNightWhiteWish:
return ["花海佑芽", "White", "Night", "Wish", "!"]
case PIdol.花海佑芽_学園生活:
return ["花海佑芽", "学園生活"]
case PIdol.花海佑芽_Campusmode:
return ["花海佑芽", "Campus", "mode", "!!"]
case PIdol.花海佑芽_TheRollingRiceball:
return ["花海佑芽", "The", "Rolling", "Riceball"]
case PIdol.花海佑芽_アイドル_はじめっ:
return ["花海佑芽", "アイドル、はじめっ!"]
case PIdol.花海咲季_BoomBoomPow:
return ["花海咲季", "Boom", "Boom", "Pow"]
case PIdol.花海咲季_Campusmode:
return ["花海咲季", "Campus", "mode", "!!"]
case PIdol.花海咲季_FightingMyWay:
return ["花海咲季", "Fighting", "My", "Way"]
case PIdol.花海咲季_わたしが一番:
return ["花海咲季", "わたしが一番!"]
case PIdol.花海咲季_冠菊:
return ["花海咲季", "冠菊"]
case PIdol.花海咲季_初声:
return ["花海咲季", "初声"]
case PIdol.花海咲季_古今東西ちょちょいのちょい:
return ["花海咲季", "古今東西ちょちょいのちょい"]
case PIdol.花海咲季_学園生活:
return ["花海咲季", "学園生活"]
case PIdol.葛城リーリヤ_一つ踏み出した先に:
return ["葛城リーリヤ", "一つ踏み出した先に"]
case PIdol.葛城リーリヤ_白線:
return ["葛城リーリヤ", "白線"]
case PIdol.葛城リーリヤ_Campusmode:
return ["葛城リーリヤ", "Campus", "mode", "!!"]
case PIdol.葛城リーリヤ_WhiteNightWhiteWish:
return ["葛城リーリヤ", "White", "Night", "Wish", "!"]
case PIdol.葛城リーリヤ_冠菊:
return ["葛城リーリヤ", "冠菊"]
case PIdol.葛城リーリヤ_初心:
return ["葛城リーリヤ", "初心"]
case PIdol.葛城リーリヤ_学園生活:
return ["葛城リーリヤ", "学園生活"]
case PIdol.藤田ことね_カワイイ_はじめました:
return ["藤田ことね", "カワイイ", "はじめました"]
case PIdol.藤田ことね_世界一可愛い私:
return ["藤田ことね", "世界一可愛い私"]
case PIdol.藤田ことね_Campusmode:
return ["藤田ことね", "Campus", "mode", "!!"]
case PIdol.藤田ことね_YellowBigBang:
return ["藤田ことね", "Yellow", "Big", "Bang", "!"]
case PIdol.藤田ことね_WhiteNightWhiteWish:
return ["藤田ことね", "White", "Night", "Wish", "!"]
case PIdol.藤田ことね_冠菊:
return ["藤田ことね", "冠菊"]
case PIdol.藤田ことね_初声:
return ["藤田ことね", "初声"]
case PIdol.藤田ことね_学園生活:
return ["藤田ことね", "学園生活"]
case _:
assert_never(self)
class DailyMoneyShopItems(IntEnum):
"""日常商店物品"""
@ -168,33 +357,57 @@ class DailyMoneyShopItems(IntEnum):
@classmethod
def to_ui_text(cls, item: "DailyMoneyShopItems") -> str:
"""获取枚举值对应的UI显示文本"""
MAP = {
cls.Recommendations: "所有推荐商品",
cls.LessonNote: "课程笔记",
cls.VeteranNote: "老手笔记",
cls.SupportEnhancementPt: "支援强化点数",
cls.SenseNoteVocal: "感性笔记(声乐)",
cls.SenseNoteDance: "感性笔记(舞蹈)",
cls.SenseNoteVisual: "感性笔记(形象)",
cls.LogicNoteVocal: "理性笔记(声乐)",
cls.LogicNoteDance: "理性笔记(舞蹈)",
cls.LogicNoteVisual: "理性笔记(形象)",
cls.AnomalyNoteVocal: "非凡笔记(声乐)",
cls.AnomalyNoteDance: "非凡笔记(舞蹈)",
cls.AnomalyNoteVisual: "非凡笔记(形象)",
cls.RechallengeTicket: "重新挑战券",
cls.RecordKey: "记录钥匙",
cls.IdolPiece_倉本千奈_WonderScale: "倉本千奈 WonderScale 碎片",
cls.IdolPiece_篠泽广_光景: "篠泽广 光景 碎片",
cls.IdolPiece_紫云清夏_TameLieOneStep: "紫云清夏 Tame-Lie-One-Step 碎片",
cls.IdolPiece_葛城リーリヤ_白線: "葛城リーリヤ 白線 碎片",
cls.IdolPiece_姫崎薪波_cIclumsy_trick: "姫崎薪波 cIclumsy trick 碎片",
cls.IdolPiece_花海咲季_FightingMyWay: "花海咲季 FightingMyWay 碎片",
cls.IdolPiece_藤田ことね_世界一可愛い私: "藤田ことね 世界一可愛い私 碎片",
cls.IdolPiece_花海佑芽_TheRollingRiceball: "花海佑芽 The Rolling Riceball 碎片",
cls.IdolPiece_月村手毬_LunaSayMaybe: "月村手毬 Luna say maybe 碎片"
}
return MAP.get(item, str(item))
match item:
case cls.Recommendations:
return "所有推荐商品"
case cls.LessonNote:
return "课程笔记"
case cls.VeteranNote:
return "老手笔记"
case cls.SupportEnhancementPt:
return "支援强化点数"
case cls.SenseNoteVocal:
return "感性笔记(声乐)"
case cls.SenseNoteDance:
return "感性笔记(舞蹈)"
case cls.SenseNoteVisual:
return "感性笔记(形象)"
case cls.LogicNoteVocal:
return "理性笔记(声乐)"
case cls.LogicNoteDance:
return "理性笔记(舞蹈)"
case cls.LogicNoteVisual:
return "理性笔记(形象)"
case cls.AnomalyNoteVocal:
return "非凡笔记(声乐)"
case cls.AnomalyNoteDance:
return "非凡笔记(舞蹈)"
case cls.AnomalyNoteVisual:
return "非凡笔记(形象)"
case cls.RechallengeTicket:
return "重新挑战券"
case cls.RecordKey:
return "记录钥匙"
case cls.IdolPiece_倉本千奈_WonderScale:
return "倉本千奈 WonderScale 碎片"
case cls.IdolPiece_篠泽广_光景:
return "篠泽广 光景 碎片"
case cls.IdolPiece_紫云清夏_TameLieOneStep:
return "紫云清夏 Tame-Lie-One-Step 碎片"
case cls.IdolPiece_葛城リーリヤ_白線:
return "葛城リーリヤ 白線 碎片"
case cls.IdolPiece_姫崎薪波_cIclumsy_trick:
return "姫崎薪波 cIclumsy trick 碎片"
case cls.IdolPiece_花海咲季_FightingMyWay:
return "花海咲季 FightingMyWay 碎片"
case cls.IdolPiece_藤田ことね_世界一可愛い私:
return "藤田ことね 世界一可愛い私 碎片"
case cls.IdolPiece_花海佑芽_TheRollingRiceball:
return "花海佑芽 The Rolling Riceball 碎片"
case cls.IdolPiece_月村手毬_LunaSayMaybe:
return "月村手毬 Luna say maybe 碎片"
case _:
assert_never(item)
@classmethod
def all(cls) -> list[tuple[str, 'DailyMoneyShopItems']]:
@ -263,7 +476,7 @@ class DailyMoneyShopItems(IntEnum):
case DailyMoneyShopItems.IdolPiece_月村手毬_LunaSayMaybe:
return R.Shop.IdolPiece.月村手毬_LunaSayMaybe
case _:
raise ValueError(f"Unknown daily shop item: {self}")
assert_never(self)
class ConfigBaseModel(BaseModel):
model_config = ConfigDict(use_attribute_docstrings=True)
@ -517,6 +730,215 @@ def sprite_path(path: str) -> str:
return standalone
return str(resources.files('kotonebot.tasks.sprites') / path)
def upgrade_config() -> str | None:
"""
升级配置文件
"""
with open('config.json', 'r', encoding='utf-8') as f:
root = json.load(f)
user_configs = root['user_configs']
old_version = root['version']
messages = []
def upgrade_user_config(version: int, user_config: dict[str, Any]) -> int:
nonlocal messages
while True:
match version:
case 1:
logger.info('Upgrading config: v1 -> v2')
user_config, msg = upgrade_v1_to_v2(user_config['options'])
messages.append(msg)
version = 2
case _:
logger.info('No config upgrade needed.')
return version
for user_config in user_configs:
new_version = upgrade_user_config(old_version, user_config)
root['version'] = new_version
with open('config.json', 'w', encoding='utf-8') as f:
json.dump(root, f, ensure_ascii=False, indent=4)
return '\n'.join(messages)
def upgrade_v1_to_v2(options: dict[str, Any]) -> tuple[dict[str, Any], str | None]:
"""
v1 -> v2 变更
1. PIdol 的枚举值改为整数
"""
msg = ''
# 转换 PIdol 的枚举值
def map_idol(idol: list[str]) -> PIdol | None:
logger.debug("Converting %s", idol)
match idol:
case ["倉本千奈", "Campus mode!!"]:
return PIdol.倉本千奈_Campusmode
case ["倉本千奈", "Wonder Scale"]:
return PIdol.倉本千奈_WonderScale
case ["倉本千奈", "ようこそ初星温泉"]:
return PIdol.倉本千奈_ようこそ初星温泉
case ["倉本千奈", "仮装狂騒曲"]:
return PIdol.倉本千奈_仮装狂騒曲
case ["倉本千奈", "初心"]:
return PIdol.倉本千奈_初心
case ["倉本千奈", "学園生活"]:
return PIdol.倉本千奈_学園生活
case ["倉本千奈", "日々、発見的ステップ!"]:
return PIdol.倉本千奈_日々_発見的ステップ
case ["倉本千奈", "胸を張って一歩ずつ"]:
return PIdol.倉本千奈_胸を張って一歩ずつ
case ["十王星南", "Campus mode!!"]:
return PIdol.十王星南_Campusmode
case ["十王星南", "一番星"]:
return PIdol.十王星南_一番星
case ["十王星南", "学園生活"]:
return PIdol.十王星南_学園生活
case ["十王星南", "小さな野望"]:
return PIdol.十王星南_小さな野望
case ["姫崎莉波", "clumsy trick"]:
return PIdol.姫崎莉波_clumsytrick
case ["姫崎莉波", "『私らしさ』のはじまり"]:
return PIdol.姫崎莉波_私らしさのはじまり
case ["姫崎莉波", "キミとセミブルー"]:
return PIdol.姫崎莉波_キミとセミブルー
case ["姫崎莉波", "Campus mode!!"]:
return PIdol.姫崎莉波_Campusmode
case ["姫崎莉波", "L.U.V"]:
return PIdol.姫崎莉波_LUV
case ["姫崎莉波", "ようこそ初星温泉"]:
return PIdol.姫崎莉波_ようこそ初星温泉
case ["姫崎莉波", "ハッピーミルフィーユ"]:
return PIdol.姫崎莉波_ハッピーミルフィーユ
case ["姫崎莉波", "初心"]:
return PIdol.姫崎莉波_初心
case ["姫崎莉波", "学園生活"]:
return PIdol.姫崎莉波_学園生活
case ["月村手毬", "Luna say maybe"]:
return PIdol.月村手毬_Lunasaymaybe
case ["月村手毬", "一匹狼"]:
return PIdol.月村手毬_一匹狼
case ["月村手毬", "Campus mode!!"]:
return PIdol.月村手毬_Campusmode
case ["月村手毬", "アイヴイ"]:
return PIdol.月村手毬_アイヴイ
case ["月村手毬", "初声"]:
return PIdol.月村手毬_初声
case ["月村手毬", "学園生活"]:
return PIdol.月村手毬_学園生活
case ["月村手毬", "仮装狂騒曲"]:
return PIdol.月村手毬_仮装狂騒曲
case ["有村麻央", "Fluorite"]:
return PIdol.有村麻央_Fluorite
case ["有村麻央", "はじまりはカッコよく"]:
return PIdol.有村麻央_はじまりはカッコよく
case ["有村麻央", "Campus mode!!"]:
return PIdol.有村麻央_Campusmode
case ["有村麻央", "Feel Jewel Dream"]:
return PIdol.有村麻央_FeelJewelDream
case ["有村麻央", "キミとセミブルー"]:
return PIdol.有村麻央_キミとセミブルー
case ["有村麻央", "初恋"]:
return PIdol.有村麻央_初恋
case ["有村麻央", "学園生活"]:
return PIdol.有村麻央_学園生活
case ["篠泽广", "コントラスト"]:
return PIdol.篠泽广_コントラスト
case ["篠泽广", "一番向いていないこと"]:
return PIdol.篠泽广_一番向いていないこと
case ["篠泽广", "光景"]:
return PIdol.篠泽广_光景
case ["篠泽广", "Campus mode!!"]:
return PIdol.篠泽广_Campusmode
case ["篠泽广", "仮装狂騒曲"]:
return PIdol.篠泽广_仮装狂騒曲
case ["篠泽广", "ハッピーミルフィーユ"]:
return PIdol.篠泽广_ハッピーミルフィーユ
case ["篠泽广", "初恋"]:
return PIdol.篠泽广_初恋
case ["篠泽广", "学園生活"]:
return PIdol.篠泽广_学園生活
case ["紫云清夏", "Tame-Lie-One-Step"]:
return PIdol.紫云清夏_TameLieOneStep
case ["紫云清夏", "カクシタワタシ"]:
return PIdol.紫云清夏_カクシタワタシ
case ["紫云清夏", "夢へのリスタート"]:
return PIdol.紫云清夏_夢へのリスタート
case ["紫云清夏", "Campus mode!!"]:
return PIdol.紫云清夏_Campusmode
case ["紫云清夏", "キミとセミブルー"]:
return PIdol.紫云清夏_キミとセミブルー
case ["紫云清夏", "初恋"]:
return PIdol.紫云清夏_初恋
case ["紫云清夏", "学園生活"]:
return PIdol.紫云清夏_学園生活
case ["花海佑芽", "White Night! White Wish!"]:
return PIdol.花海佑芽_WhiteNightWhiteWish
case ["花海佑芽", "学園生活"]:
return PIdol.花海佑芽_学園生活
case ["花海佑芽", "Campus mode!!"]:
return PIdol.花海佑芽_Campusmode
case ["花海佑芽", "The Rolling Riceball"]:
return PIdol.花海佑芽_TheRollingRiceball
case ["花海佑芽", "アイドル、はじめっ!"]:
return PIdol.花海佑芽_アイドル_はじめっ
case ["花海咲季", "Boom Boom Pow"]:
return PIdol.花海咲季_BoomBoomPow
case ["花海咲季", "Campus mode!!"]:
return PIdol.花海咲季_Campusmode
case ["花海咲季", "Fighting My Way"]:
return PIdol.花海咲季_FightingMyWay
case ["花海咲季", "わたしが一番!"]:
return PIdol.花海咲季_わたしが一番
case ["花海咲季", "冠菊"]:
return PIdol.花海咲季_冠菊
case ["花海咲季", "初声"]:
return PIdol.花海咲季_初声
case ["花海咲季", "古今東西ちょちょいのちょい"]:
return PIdol.花海咲季_古今東西ちょちょいのちょい
case ["花海咲季", "学園生活"]:
return PIdol.花海咲季_学園生活
case ["葛城リーリヤ", "一つ踏み出した先に"]:
return PIdol.葛城リーリヤ_一つ踏み出した先に
case ["葛城リーリヤ", "白線"]:
return PIdol.葛城リーリヤ_白線
case ["葛城リーリヤ", "Campus mode!!"]:
return PIdol.葛城リーリヤ_Campusmode
case ["葛城リーリヤ", "White Night! White Wish!"]:
return PIdol.葛城リーリヤ_WhiteNightWhiteWish
case ["葛城リーリヤ", "冠菊"]:
return PIdol.葛城リーリヤ_冠菊
case ["葛城リーリヤ", "初心"]:
return PIdol.葛城リーリヤ_初心
case ["葛城リーリヤ", "学園生活"]:
return PIdol.葛城リーリヤ_学園生活
case ["藤田ことね", "カワイイ", "はじめました"]:
return PIdol.藤田ことね_カワイイ_はじめました
case ["藤田ことね", "世界一可愛い私"]:
return PIdol.藤田ことね_世界一可愛い私
case ["藤田ことね", "Campus mode!!"]:
return PIdol.藤田ことね_Campusmode
case ["藤田ことね", "Yellow Big Bang"]:
return PIdol.藤田ことね_YellowBigBang
case ["藤田ことね", "White Night! White Wish!"]:
return PIdol.藤田ことね_WhiteNightWhiteWish
case ["藤田ことね", "冠菊"]:
return PIdol.藤田ことね_冠菊
case ["藤田ことね", "初声"]:
return PIdol.藤田ことね_初声
case ["藤田ことね", "学園生活"]:
return PIdol.藤田ことね_学園生活
case _:
nonlocal msg
if msg == '':
msg = '培育设置中的以下偶像升级失败。请尝试手动添加。\n'
msg += f'{idol} 未找到\n'
return None
old_idols = options['produce']['idols']
new_idols = list(filter(lambda x: x is not None, map(map_idol, old_idols)))
options['produce']['idols'] = new_idols
shutil.copy('config.json', 'config.json.v1.json')
return options, msg
if __name__ == '__main__':
print(PurchaseConfig.model_fields['money_refresh_on'].description)

View File

@ -56,7 +56,7 @@ def select_idol(target_titles: list[str] | PIdol):
device.screenshot()
if isinstance(target_titles, PIdol):
target_titles = target_titles.value
target_titles = target_titles.to_title()
_target_titles = [contains(t) for t in target_titles]
device.screenshot()
# 定位滑动基准
@ -207,7 +207,7 @@ def do_produce(
device.click(image.expect_wait(R.InPurodyuusu.ButtonCancel))
return False
# 1. 选择 PIdol [screenshots/produce/select_p_idol.png]
select_idol(idol.value)
select_idol(idol.to_title())
device.click(image.expect_wait(R.Common.ButtonNextNoIcon))
# 2. 选择支援卡 自动编成 [screenshots/produce/select_support_card.png]
ocr.expect_wait(contains('サポート'), rect=R.Produce.BoxStepIndicator)

View File

@ -1,3 +1,2 @@
# kotonebot.ui
此文件夹下存放的是与用户交互相关的代码,包括前后端交互、配置文件的读写等。
但是不包括前端项目。前端项目在 `/kotonebot-ui` 文件夹下。
~~此文件夹下存放的是与用户交互相关的代码,包括前后端交互、配置文件的读写等。但是不包括前端项目。前端项目在 `/kotonebot-ui` 文件夹下。~~

View File

@ -2,10 +2,8 @@ from operator import gt
import os
import zipfile
import logging
import traceback
import importlib.metadata
from functools import partial
from importlib import resources
from datetime import datetime, timedelta
from typing import List, Dict, Tuple, Literal, Generator
@ -14,12 +12,12 @@ 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 capsule_toys, upgrade_support_card
from kotonebot.tasks.common import (
BaseConfig, APShopItems, CapsuleToysConfig, ClubRewardConfig, PurchaseConfig, ActivityFundsConfig,
PresentsConfig, AssignmentConfig, ContestConfig, ProduceConfig,
MissionRewardConfig, PIdol, DailyMoneyShopItems, ProduceAction,
RecommendCardDetectionMode, TraceConfig, StartGameConfig, UpgradeSupportCardConfig
RecommendCardDetectionMode, TraceConfig, StartGameConfig, UpgradeSupportCardConfig,
upgrade_config
)
from kotonebot.config.base_config import UserConfig, BackendConfig
from kotonebot.backend.bot import KotoneBot
@ -41,6 +39,9 @@ root_logger.addHandler(file_handler)
logging.getLogger("kotonebot").setLevel(logging.DEBUG)
# 升级配置
upgrade_msg = upgrade_config()
logger = logging.getLogger(__name__)
def _save_bug_report(
@ -381,11 +382,12 @@ class KotoneBotUI:
def _create_status_tab(self) -> None:
with gr.Tab("状态"):
gr.Markdown("## 状态")
progress_bar = gr.Progress()
with gr.Row():
run_btn = gr.Button("启动", scale=1)
debug_btn = gr.Button("调试", scale=1)
if upgrade_msg:
gr.Markdown('### 配置升级报告')
gr.Markdown(upgrade_msg)
gr.Markdown('脚本报错或者卡住?点击"日志"选项卡中的"一键导出报告"可以快速反馈!')
task_status = gr.Dataframe(

View File

@ -25,7 +25,7 @@ dependencies = [
"websockets==14.1",
"psutil==6.1.1",
# GUI
"gradio==5.12.0",
"gradio==5.21.0",
# Configuration
"pydantic==2.10.4",
# Resource

View File

@ -14,7 +14,7 @@ python-multipart==0.0.20
websockets==14.1
psutil==6.1.1
# GUI
gradio==5.12.0
gradio==5.21.0
# Configuration
pydantic==2.10.4
# Other