starrailassistant 2.17.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- SRACore/__init__.py +1 -0
- SRACore/__main__.py +115 -0
- SRACore/cli2.py +344 -0
- SRACore/localization/__init__.py +3 -0
- SRACore/localization/resource.py +471 -0
- SRACore/localization/resource.toml +224 -0
- SRACore/localization/resource_en-us.json +58 -0
- SRACore/localization/resource_zh-cn.json +58 -0
- SRACore/models/__init__.py +0 -0
- SRACore/models/app_settings.py +413 -0
- SRACore/models/tasks_config.py +300 -0
- SRACore/notification/__init__.py +25 -0
- SRACore/notification/channels/__init__.py +33 -0
- SRACore/notification/channels/bark.py +43 -0
- SRACore/notification/channels/base.py +15 -0
- SRACore/notification/channels/common.py +44 -0
- SRACore/notification/channels/dingtalk.py +50 -0
- SRACore/notification/channels/discord.py +56 -0
- SRACore/notification/channels/email.py +65 -0
- SRACore/notification/channels/feishu.py +51 -0
- SRACore/notification/channels/onebot.py +45 -0
- SRACore/notification/channels/serverchan.py +30 -0
- SRACore/notification/channels/system.py +26 -0
- SRACore/notification/channels/telegram.py +50 -0
- SRACore/notification/channels/webhook.py +20 -0
- SRACore/notification/channels/wecom.py +63 -0
- SRACore/notification/channels/xxtui.py +28 -0
- SRACore/notification/dispatcher.py +40 -0
- SRACore/notification/http_client.py +112 -0
- SRACore/notification/models.py +29 -0
- SRACore/notification/service.py +241 -0
- SRACore/operators/__init__.py +0 -0
- SRACore/operators/browser_operator.py +425 -0
- SRACore/operators/ioperator.py +689 -0
- SRACore/operators/model.py +56 -0
- SRACore/operators/operator.py +313 -0
- SRACore/runtime/__init__.py +4 -0
- SRACore/runtime/event_listener.py +116 -0
- SRACore/runtime/trigger_manager.py +83 -0
- SRACore/task/__init__.py +124 -0
- SRACore/thread/__init__.py +2 -0
- SRACore/thread/task_process.py +312 -0
- SRACore/triggers/AutoPlotTrigger.py +42 -0
- SRACore/triggers/BaseTrigger.py +24 -0
- SRACore/triggers/__init__.py +3 -0
- SRACore/util/__init__.py +0 -0
- SRACore/util/const.py +32 -0
- SRACore/util/data_persister.py +69 -0
- SRACore/util/encryption.py +27 -0
- SRACore/util/errors.py +238 -0
- SRACore/util/image_util.py +59 -0
- SRACore/util/logger.py +28 -0
- SRACore/util/sys_util.py +100 -0
- starrailassistant-2.17.0.dist-info/METADATA +175 -0
- starrailassistant-2.17.0.dist-info/RECORD +59 -0
- starrailassistant-2.17.0.dist-info/WHEEL +5 -0
- starrailassistant-2.17.0.dist-info/entry_points.txt +2 -0
- starrailassistant-2.17.0.dist-info/licenses/LICENSE +674 -0
- starrailassistant-2.17.0.dist-info/top_level.txt +1 -0
SRACore/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""StarRailAssistant Core - 崩坏:星穹铁道自动化助手"""
|
SRACore/__main__.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
sys.path.append(os.getcwd()) # 将当前工作目录添加到 sys.path,以便导入 tasks
|
|
6
|
+
|
|
7
|
+
from SRACore.localization import Resource
|
|
8
|
+
from SRACore.util.const import VERSION
|
|
9
|
+
from SRACore.util.data_persister import load_app_settings
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main():
|
|
13
|
+
settings = load_app_settings()
|
|
14
|
+
language: int = settings.Display.language
|
|
15
|
+
Resource.set_language(language)
|
|
16
|
+
parser = argparse.ArgumentParser(
|
|
17
|
+
description=Resource.argparse_description,
|
|
18
|
+
epilog=Resource.argparse_epilog,
|
|
19
|
+
formatter_class=argparse.RawTextHelpFormatter
|
|
20
|
+
)
|
|
21
|
+
setup_argumentparser(parser)
|
|
22
|
+
# 解析参数
|
|
23
|
+
args = parser.parse_known_args()[0]
|
|
24
|
+
if not is_admin():
|
|
25
|
+
if args.no_admin:
|
|
26
|
+
sys.argv.remove('--no-admin') # 移除参数,不向下传递
|
|
27
|
+
print(Resource.cli_noAdminWarning)
|
|
28
|
+
else:
|
|
29
|
+
restart_as_admin()
|
|
30
|
+
from SRACore.util.logger import logger, setup_logger
|
|
31
|
+
# 设置日志记录器
|
|
32
|
+
setup_logger(level=args.log_level)
|
|
33
|
+
logger.info(f"Current version: {VERSION}")
|
|
34
|
+
logger.debug(f"cwd: {os.getcwd()}")
|
|
35
|
+
|
|
36
|
+
if args.command:
|
|
37
|
+
for cmd in args.command:
|
|
38
|
+
sys.argv.remove(cmd) # 移除命令参数, 避免重复执行
|
|
39
|
+
cmd_str = " ".join(args.command).replace('&', '+')
|
|
40
|
+
commands = cmd_str.split('+')
|
|
41
|
+
for cmd in commands:
|
|
42
|
+
sys.argv.append(cmd)
|
|
43
|
+
print(sys.argv)
|
|
44
|
+
inline = args.inline
|
|
45
|
+
if inline:
|
|
46
|
+
sys.argv.remove('--inline')
|
|
47
|
+
# 延迟导入 SRACli
|
|
48
|
+
from SRACore.cli2 import SRACli
|
|
49
|
+
cli_instance = SRACli(settings=settings)
|
|
50
|
+
# 配置交互式模式(隐藏提示符)
|
|
51
|
+
if inline:
|
|
52
|
+
cli_instance.intro = ''
|
|
53
|
+
cli_instance.prompt = ''
|
|
54
|
+
cli_instance.cmdloop()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def setup_argumentparser(parser: argparse.ArgumentParser) -> None:
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
'--inline',
|
|
60
|
+
action='store_true',
|
|
61
|
+
help=Resource.argparse_inline_help
|
|
62
|
+
)
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
'--command', '-c', '--execute', '-e',
|
|
65
|
+
nargs='*',
|
|
66
|
+
type=str,
|
|
67
|
+
help='The command to execute AFTER launch',
|
|
68
|
+
)
|
|
69
|
+
parser.add_argument(
|
|
70
|
+
'-v', '--version',
|
|
71
|
+
action='version',
|
|
72
|
+
version=f'{VERSION}',
|
|
73
|
+
help=Resource.argparse_version_help
|
|
74
|
+
)
|
|
75
|
+
parser.add_argument(
|
|
76
|
+
'--log-level',
|
|
77
|
+
type=str,
|
|
78
|
+
choices=['TRACE', 'DEBUG', 'INFO', 'SUCCESS', 'WARNING', 'ERROR', 'CRITICAL'],
|
|
79
|
+
default='TRACE',
|
|
80
|
+
help=Resource.argparse_log_level_help
|
|
81
|
+
)
|
|
82
|
+
parser.add_argument(
|
|
83
|
+
'--no-admin',
|
|
84
|
+
action='store_true',
|
|
85
|
+
help="Do not require admin privileges"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def restart_as_admin():
|
|
90
|
+
"""以管理员权限重启当前进程"""
|
|
91
|
+
if sys.platform == 'win32' and not is_admin():
|
|
92
|
+
import ctypes
|
|
93
|
+
cmdline = subprocess.list2cmdline(sys.argv)
|
|
94
|
+
executable = sys.executable.removesuffix('.exe')
|
|
95
|
+
executable += '.exe'
|
|
96
|
+
result = ctypes.windll.shell32.ShellExecuteW(None, 'runas', 'wt.exe', f'"{executable}" {cmdline}', None, 1)
|
|
97
|
+
if result > 32:
|
|
98
|
+
sys.exit(0)
|
|
99
|
+
else:
|
|
100
|
+
result = ctypes.windll.shell32.ShellExecuteW(None, 'runas', executable, cmdline, None, 1)
|
|
101
|
+
sys.exit(result)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def is_admin() -> bool:
|
|
105
|
+
"""检查当前用户是否具有管理员权限(仅限 Windows)"""
|
|
106
|
+
try:
|
|
107
|
+
import ctypes
|
|
108
|
+
return ctypes.windll.shell32.IsUserAnAdmin() != 0 # NOQA
|
|
109
|
+
except Exception as e:
|
|
110
|
+
print(f"Error checking administrator privileges: {e}")
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
if __name__ == '__main__':
|
|
115
|
+
main()
|
SRACore/cli2.py
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
import cmd2
|
|
4
|
+
from loguru import logger
|
|
5
|
+
from rich.text import Text
|
|
6
|
+
|
|
7
|
+
from SRACore.localization import Resource
|
|
8
|
+
from SRACore.models.app_settings import AppSettings
|
|
9
|
+
from SRACore.runtime.event_listener import KeyboardListener
|
|
10
|
+
from SRACore.runtime.trigger_manager import TriggerManager
|
|
11
|
+
from SRACore.thread.task_process import TaskManager
|
|
12
|
+
from SRACore.util.const import VERSION, CORE
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SRACli(cmd2.Cmd):
|
|
16
|
+
def __init__(self, settings: AppSettings):
|
|
17
|
+
super().__init__(startup_script=".srarc")
|
|
18
|
+
self.intro = f"Welcome to SRA-cli (version {VERSION}, core {CORE}). \nType 'help' to list commands."
|
|
19
|
+
self.prompt = 'sra> '
|
|
20
|
+
self.default_error = Resource.cli_defaultError
|
|
21
|
+
self.settings = settings
|
|
22
|
+
|
|
23
|
+
# 移除不需要的 settable 选项
|
|
24
|
+
# for attr in ["debug", "timing", "quiet", "feedback_to_output",
|
|
25
|
+
# "max_completion_items", "allow_style", "always_show_hint",
|
|
26
|
+
# "scripts_add_to_history", "echo"]:
|
|
27
|
+
# self.remove_settable(attr)
|
|
28
|
+
|
|
29
|
+
# 移除不需要的内置命令
|
|
30
|
+
for cmd_name in ["run_pyscript"]:
|
|
31
|
+
if hasattr(cmd2.Cmd, f"do_{cmd_name}"):
|
|
32
|
+
delattr(cmd2.Cmd, f"do_{cmd_name}")
|
|
33
|
+
# 初始化任务管理器
|
|
34
|
+
self.task_manager = TaskManager(settings)
|
|
35
|
+
# 初始化触发器管理器
|
|
36
|
+
self.trigger_manager = TriggerManager()
|
|
37
|
+
|
|
38
|
+
# 初始化键盘监听器
|
|
39
|
+
stop_hotkey = settings.General.hotkeyStop.lower() or 'f9'
|
|
40
|
+
self.event_listener = KeyboardListener()
|
|
41
|
+
self.event_listener.register_key_event(stop_hotkey, self._task_stop)
|
|
42
|
+
self.event_listener.start()
|
|
43
|
+
|
|
44
|
+
# region 任务管理
|
|
45
|
+
@staticmethod
|
|
46
|
+
def _build_task_parser() -> argparse.ArgumentParser:
|
|
47
|
+
task_description = Text.assemble(Resource.task_description)
|
|
48
|
+
task_parser = cmd2.argparse_custom.DEFAULT_ARGUMENT_PARSER(description=task_description)
|
|
49
|
+
task_parser.add_subparsers(metavar="SUBCOMMAND", required=True)
|
|
50
|
+
return task_parser
|
|
51
|
+
|
|
52
|
+
@cmd2.with_argparser(_build_task_parser(), preserve_quotes=True)
|
|
53
|
+
def do_task(self, args: argparse.Namespace) -> None:
|
|
54
|
+
handler = args.cmd2_handler.get()
|
|
55
|
+
handler(args)
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def _build_task_run_parser() -> cmd2.Cmd2ArgumentParser:
|
|
59
|
+
task_run_description = Text.assemble(Resource.run_description)
|
|
60
|
+
task_run_parser = cmd2.argparse_custom.DEFAULT_ARGUMENT_PARSER(description=task_run_description)
|
|
61
|
+
task_run_parser.add_argument('config', nargs='*', help=Resource.run_configHelp)
|
|
62
|
+
return task_run_parser
|
|
63
|
+
|
|
64
|
+
@cmd2.as_subcommand_to("task", "run", _build_task_run_parser(), help=Resource.run_configHelp)
|
|
65
|
+
def _task_run(self, args: argparse.Namespace) -> None:
|
|
66
|
+
if self.task_manager.is_thread_running():
|
|
67
|
+
self.poutput(Resource.cli_task_taskAlreadyRunning)
|
|
68
|
+
return
|
|
69
|
+
self.task_manager.run_in_thread(*args.config)
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def _build_task_single_parser() -> cmd2.Cmd2ArgumentParser:
|
|
73
|
+
task_single_description = Text.assemble(Resource.single_description)
|
|
74
|
+
task_single_parser = cmd2.argparse_custom.DEFAULT_ARGUMENT_PARSER(description=task_single_description)
|
|
75
|
+
task_single_parser.add_argument('task', help=Resource.single_taskHelp)
|
|
76
|
+
task_single_parser.add_argument('--config', help=Resource.single_configHelp)
|
|
77
|
+
return task_single_parser
|
|
78
|
+
|
|
79
|
+
@cmd2.as_subcommand_to("task", "single", _build_task_single_parser(), help=Resource.single_description)
|
|
80
|
+
def _task_single(self, args: argparse.Namespace) -> None:
|
|
81
|
+
if self.task_manager.is_thread_running():
|
|
82
|
+
self.poutput(Resource.cli_task_taskAlreadyRunning)
|
|
83
|
+
return
|
|
84
|
+
if self.task_manager.run_task_in_thread(args.task, args.config):
|
|
85
|
+
self.poutput(Resource.cli_run_started)
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def _build_task_stop_parser() -> cmd2.Cmd2ArgumentParser:
|
|
89
|
+
task_stop_description = Text.assemble(Resource.stop_description)
|
|
90
|
+
return cmd2.argparse_custom.DEFAULT_ARGUMENT_PARSER(description=task_stop_description)
|
|
91
|
+
|
|
92
|
+
@cmd2.as_subcommand_to("task", "stop", _build_task_stop_parser(), help=Resource.stop_description)
|
|
93
|
+
def _task_stop(self, _) -> None:
|
|
94
|
+
if self.task_manager.is_thread_running():
|
|
95
|
+
self.task_manager.stop_thread()
|
|
96
|
+
self.poutput(Resource.cli_task_stopped)
|
|
97
|
+
else:
|
|
98
|
+
self.poutput(Resource.cli_task_notRunning)
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def _build_run_parser() -> cmd2.Cmd2ArgumentParser:
|
|
102
|
+
run_description = Text.assemble(Resource.run_description)
|
|
103
|
+
run_parser = cmd2.argparse_custom.DEFAULT_ARGUMENT_PARSER(description=run_description)
|
|
104
|
+
run_parser.add_argument('config', nargs='*', help=Resource.run_configHelp)
|
|
105
|
+
return run_parser
|
|
106
|
+
|
|
107
|
+
@cmd2.with_argparser(_build_run_parser())
|
|
108
|
+
def do_run(self, args: argparse.Namespace) -> None:
|
|
109
|
+
"""Run specified tasks, will block current command line until tasks complete"""
|
|
110
|
+
self.poutput(Resource.cli_run_started)
|
|
111
|
+
try:
|
|
112
|
+
self.task_manager.run(*args.config)
|
|
113
|
+
except KeyboardInterrupt:
|
|
114
|
+
self.task_manager.request_stop()
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def _build_single_parser() -> cmd2.Cmd2ArgumentParser:
|
|
118
|
+
single_description = Text.assemble(Resource.single_description)
|
|
119
|
+
single_parser = cmd2.argparse_custom.DEFAULT_ARGUMENT_PARSER(description=single_description)
|
|
120
|
+
single_parser.add_argument('task', help=Resource.single_taskHelp)
|
|
121
|
+
single_parser.add_argument('--config', help=Resource.single_configHelp)
|
|
122
|
+
return single_parser
|
|
123
|
+
|
|
124
|
+
@cmd2.with_argparser(_build_single_parser())
|
|
125
|
+
def do_single(self, args: argparse.Namespace) -> None:
|
|
126
|
+
"""Run a single specified task, will block current command line until task complete"""
|
|
127
|
+
self.poutput(Resource.cli_run_started)
|
|
128
|
+
try:
|
|
129
|
+
self.task_manager.run_task(args.task, args.config)
|
|
130
|
+
except KeyboardInterrupt:
|
|
131
|
+
self.task_manager.request_stop()
|
|
132
|
+
|
|
133
|
+
# endregion
|
|
134
|
+
|
|
135
|
+
# region 触发器管理
|
|
136
|
+
|
|
137
|
+
@staticmethod
|
|
138
|
+
def _build_trigger_parser() -> argparse.ArgumentParser:
|
|
139
|
+
trigger_description = Text.assemble(Resource.trigger_description)
|
|
140
|
+
trigger_parser = cmd2.argparse_custom.DEFAULT_ARGUMENT_PARSER(description=trigger_description)
|
|
141
|
+
trigger_parser.add_subparsers(metavar="SUBCOMMAND", required=True)
|
|
142
|
+
return trigger_parser
|
|
143
|
+
|
|
144
|
+
@cmd2.with_argparser(_build_trigger_parser(), preserve_quotes=True)
|
|
145
|
+
def do_trigger(self, args: argparse.Namespace) -> None:
|
|
146
|
+
handler = args.cmd2_handler.get()
|
|
147
|
+
handler(args)
|
|
148
|
+
|
|
149
|
+
@staticmethod
|
|
150
|
+
def _build_trigger_run_parser() -> cmd2.Cmd2ArgumentParser:
|
|
151
|
+
trigger_run_description = Text.assemble(Resource.trigger_run_description)
|
|
152
|
+
return cmd2.argparse_custom.DEFAULT_ARGUMENT_PARSER(description=trigger_run_description)
|
|
153
|
+
|
|
154
|
+
@cmd2.as_subcommand_to("trigger", "run", _build_trigger_run_parser(), help=Resource.trigger_run_description)
|
|
155
|
+
def _trigger_run(self, _) -> None:
|
|
156
|
+
if self.trigger_manager.is_thread_running():
|
|
157
|
+
self.poutput(Resource.cli_trigger_alreadyRunning)
|
|
158
|
+
return
|
|
159
|
+
if not self.trigger_manager.has_enabled_triggers():
|
|
160
|
+
self.poutput(Resource.cli_trigger_noEnabledTriggers)
|
|
161
|
+
return
|
|
162
|
+
self.trigger_manager.start_thread()
|
|
163
|
+
self.poutput(Resource.cli_trigger_started)
|
|
164
|
+
|
|
165
|
+
@staticmethod
|
|
166
|
+
def _build_trigger_stop_parser() -> cmd2.Cmd2ArgumentParser:
|
|
167
|
+
trigger_stop_description = Text.assemble(Resource.trigger_stop_description)
|
|
168
|
+
return cmd2.argparse_custom.DEFAULT_ARGUMENT_PARSER(description=trigger_stop_description)
|
|
169
|
+
|
|
170
|
+
@cmd2.as_subcommand_to("trigger", "stop", _build_trigger_stop_parser(), help=Resource.trigger_stop_description)
|
|
171
|
+
def _trigger_stop(self, _) -> None:
|
|
172
|
+
if self.trigger_manager.is_thread_running():
|
|
173
|
+
self.trigger_manager.stop_thread()
|
|
174
|
+
self.poutput(Resource.cli_trigger_stopped)
|
|
175
|
+
else:
|
|
176
|
+
self.poutput(Resource.cli_trigger_notRunning)
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def _build_trigger_enable_parser() -> cmd2.Cmd2ArgumentParser:
|
|
180
|
+
trigger_enable_description = Text.assemble(Resource.trigger_enable_description)
|
|
181
|
+
trigger_enable_parser = cmd2.argparse_custom.DEFAULT_ARGUMENT_PARSER(description=trigger_enable_description)
|
|
182
|
+
trigger_enable_parser.add_argument('name', help=Resource.trigger_enable_nameHelp)
|
|
183
|
+
return trigger_enable_parser
|
|
184
|
+
|
|
185
|
+
@cmd2.as_subcommand_to("trigger", "enable", _build_trigger_enable_parser(), help=Resource.trigger_enable_description)
|
|
186
|
+
def _trigger_enable(self, args: argparse.Namespace) -> None:
|
|
187
|
+
for trigger in self.trigger_manager.triggers:
|
|
188
|
+
if trigger.__class__.__name__.lower() == args.name.lower():
|
|
189
|
+
trigger.set_enable(True)
|
|
190
|
+
logger.info(Resource.cli_trigger_enabled(args.name))
|
|
191
|
+
self.trigger_manager.ensure_running()
|
|
192
|
+
return
|
|
193
|
+
self.poutput(Resource.cli_trigger_notFound(args.name))
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def _build_trigger_disable_parser() -> cmd2.Cmd2ArgumentParser:
|
|
197
|
+
trigger_disable_description = Text.assemble(Resource.trigger_disable_description)
|
|
198
|
+
trigger_disable_parser = cmd2.argparse_custom.DEFAULT_ARGUMENT_PARSER(description=trigger_disable_description)
|
|
199
|
+
trigger_disable_parser.add_argument('name', help=Resource.trigger_disable_nameHelp)
|
|
200
|
+
return trigger_disable_parser
|
|
201
|
+
|
|
202
|
+
@cmd2.as_subcommand_to("trigger", "disable", _build_trigger_disable_parser(), help=Resource.trigger_disable_description)
|
|
203
|
+
def _trigger_disable(self, args: argparse.Namespace) -> None:
|
|
204
|
+
for trigger in self.trigger_manager.triggers:
|
|
205
|
+
if trigger.__class__.__name__.lower() == args.name.lower():
|
|
206
|
+
trigger.set_enable(False)
|
|
207
|
+
logger.info(Resource.cli_trigger_disabled(args.name))
|
|
208
|
+
self.trigger_manager.stop_if_idle()
|
|
209
|
+
return
|
|
210
|
+
self.poutput(Resource.cli_trigger_notFound(args.name))
|
|
211
|
+
|
|
212
|
+
@staticmethod
|
|
213
|
+
def _build_trigger_set_parser() -> cmd2.Cmd2ArgumentParser:
|
|
214
|
+
trigger_set_description = Text.assemble(Resource.trigger_set_description)
|
|
215
|
+
trigger_set_parser = cmd2.argparse_custom.DEFAULT_ARGUMENT_PARSER(description=trigger_set_description)
|
|
216
|
+
trigger_set_parser.add_argument('name', help=Resource.trigger_set_nameHelp)
|
|
217
|
+
trigger_set_parser.add_argument('attr', help=Resource.trigger_set_attrHelp)
|
|
218
|
+
trigger_set_parser.add_argument('value', help=Resource.trigger_set_valueHelp)
|
|
219
|
+
trigger_set_parser.add_argument('--type', choices=['int', 'float', 'str', 'bool'],
|
|
220
|
+
default='str', help=Resource.trigger_set_typeHelp)
|
|
221
|
+
return trigger_set_parser
|
|
222
|
+
|
|
223
|
+
@cmd2.as_subcommand_to("trigger", "set", _build_trigger_set_parser(), help=Resource.trigger_set_description)
|
|
224
|
+
def _trigger_set(self, args: argparse.Namespace) -> None:
|
|
225
|
+
for trigger in self.trigger_manager.triggers:
|
|
226
|
+
if trigger.__class__.__name__.lower() == args.name.lower():
|
|
227
|
+
if not hasattr(trigger, args.attr):
|
|
228
|
+
self.poutput(Resource.cli_trigger_attrNotFound(args.attr, args.name))
|
|
229
|
+
return
|
|
230
|
+
if args.type == 'int':
|
|
231
|
+
setattr(trigger, args.attr, int(args.value))
|
|
232
|
+
elif args.type == 'float':
|
|
233
|
+
setattr(trigger, args.attr, float(args.value))
|
|
234
|
+
elif args.type == 'str':
|
|
235
|
+
setattr(trigger, args.attr, args.value)
|
|
236
|
+
elif args.type == 'bool':
|
|
237
|
+
setattr(trigger, args.attr, args.value.lower() in ['true', '1', 'yes'])
|
|
238
|
+
else:
|
|
239
|
+
self.poutput(Resource.cli_trigger_unknownType(args.type))
|
|
240
|
+
return
|
|
241
|
+
logger.info(Resource.cli_trigger_attrSet(args.name, args.attr, args.value))
|
|
242
|
+
return
|
|
243
|
+
self.poutput(Resource.cli_trigger_notFound(args.name))
|
|
244
|
+
|
|
245
|
+
# endregion
|
|
246
|
+
|
|
247
|
+
# region 其他命令
|
|
248
|
+
def do_init(self, _: str):
|
|
249
|
+
"""Initialize the application: download resources and create default settings/config."""
|
|
250
|
+
import io
|
|
251
|
+
import json
|
|
252
|
+
import os
|
|
253
|
+
import zipfile
|
|
254
|
+
from urllib.error import URLError, HTTPError
|
|
255
|
+
from urllib.request import Request, urlopen
|
|
256
|
+
|
|
257
|
+
from SRACore.models.tasks_config import TasksConfig
|
|
258
|
+
from SRACore.util.const import AppDataDir, ConfigsDir
|
|
259
|
+
|
|
260
|
+
# url = f"https://github.com/Shasnow/StarRailAssistant/releases/download/v{VERSION}/StarRailAssistant_Resources_v{VERSION}.zip"
|
|
261
|
+
url = f"https://download.auto-mas.top/d/StarRailAssistant/StarRailAssistant_Resource_v{VERSION}.zip"
|
|
262
|
+
self.poutput(f"Downloading resources from {url} ...")
|
|
263
|
+
try:
|
|
264
|
+
req = Request(url, headers={"User-Agent": "SRA-cli"})
|
|
265
|
+
with urlopen(req) as resp:
|
|
266
|
+
data = resp.read()
|
|
267
|
+
except (URLError, HTTPError) as e:
|
|
268
|
+
self.poutput(f"Failed to download resources: {e}")
|
|
269
|
+
return
|
|
270
|
+
|
|
271
|
+
self.poutput("Extracting resources ...")
|
|
272
|
+
cwd = os.getcwd()
|
|
273
|
+
with zipfile.ZipFile(io.BytesIO(data)) as zf:
|
|
274
|
+
zf.extractall(cwd)
|
|
275
|
+
self.poutput(f"Resources extracted to {cwd}")
|
|
276
|
+
|
|
277
|
+
# 创建设置文件
|
|
278
|
+
AppDataDir.mkdir(parents=True, exist_ok=True)
|
|
279
|
+
settings_path = AppDataDir / "settings.json"
|
|
280
|
+
if not settings_path.exists():
|
|
281
|
+
settings = AppSettings.from_dict({})
|
|
282
|
+
with open(settings_path, "w", encoding="utf-8") as f:
|
|
283
|
+
json.dump(settings.to_dict(), f, indent=2, ensure_ascii=False)
|
|
284
|
+
self.poutput(f"Created settings file: {settings_path}")
|
|
285
|
+
else:
|
|
286
|
+
self.poutput(f"Settings file already exists: {settings_path}")
|
|
287
|
+
|
|
288
|
+
# 创建默认配置文件
|
|
289
|
+
ConfigsDir.mkdir(parents=True, exist_ok=True)
|
|
290
|
+
config_path = ConfigsDir / "Default.json"
|
|
291
|
+
if not config_path.exists():
|
|
292
|
+
config = TasksConfig.from_dict({"name": "Default"})
|
|
293
|
+
with open(config_path, "w", encoding="utf-8") as f:
|
|
294
|
+
json.dump(config.to_dict(), f, indent=2, ensure_ascii=False)
|
|
295
|
+
self.poutput(f"Created default config: {config_path}")
|
|
296
|
+
else:
|
|
297
|
+
self.poutput(f"Default config already exists: {config_path}")
|
|
298
|
+
|
|
299
|
+
self.poutput("Initialization completed.")
|
|
300
|
+
return True
|
|
301
|
+
|
|
302
|
+
def do_version(self, _: str):
|
|
303
|
+
"""Show version information"""
|
|
304
|
+
self.poutput(f"{VERSION}")
|
|
305
|
+
|
|
306
|
+
def do_quit(self, _: argparse.Namespace) -> bool | None:
|
|
307
|
+
"""Exit this application."""
|
|
308
|
+
self._cleanup()
|
|
309
|
+
# Return True to stop the command loop
|
|
310
|
+
self.last_result = True
|
|
311
|
+
return True
|
|
312
|
+
do_exit = do_quit
|
|
313
|
+
|
|
314
|
+
def do_notify(self, arg: str):
|
|
315
|
+
"""Notification command - support test email/webhook/telegram/serverchan/onebot notification"""
|
|
316
|
+
args = arg.split()
|
|
317
|
+
if not args:
|
|
318
|
+
self.poutput(Resource.cli_invalidArguments('notify'))
|
|
319
|
+
return
|
|
320
|
+
|
|
321
|
+
command = args[0]
|
|
322
|
+
if command == 'test' and len(args) >= 2:
|
|
323
|
+
channel = args[1]
|
|
324
|
+
from SRACore.notification import send_channel_test_notification
|
|
325
|
+
|
|
326
|
+
label, result = send_channel_test_notification(channel)
|
|
327
|
+
if label:
|
|
328
|
+
self.poutput(label + "测试通知发送" + ("成功" if result else "失败"))
|
|
329
|
+
else:
|
|
330
|
+
self.poutput(Resource.cli_invalidArguments("notify"))
|
|
331
|
+
else:
|
|
332
|
+
self.poutput(Resource.cli_invalidArguments('notify'))
|
|
333
|
+
|
|
334
|
+
# endregion
|
|
335
|
+
|
|
336
|
+
# region 生命周期管理
|
|
337
|
+
|
|
338
|
+
def _cleanup(self):
|
|
339
|
+
"""清理资源"""
|
|
340
|
+
self.task_manager.stop_thread(timeout=5.0)
|
|
341
|
+
self.trigger_manager.stop_thread(timeout=5.0)
|
|
342
|
+
self.event_listener.stop()
|
|
343
|
+
|
|
344
|
+
# endregion
|