automas-script-maafw-pack-m9a 0.1.1__tar.gz
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.
- automas_script_maafw_pack_m9a-0.1.1/PKG-INFO +16 -0
- automas_script_maafw_pack_m9a-0.1.1/README.md +7 -0
- automas_script_maafw_pack_m9a-0.1.1/pyproject.toml +23 -0
- automas_script_maafw_pack_m9a-0.1.1/setup.cfg +4 -0
- automas_script_maafw_pack_m9a-0.1.1/src/automas_script_maafw_pack_m9a/__init__.py +17 -0
- automas_script_maafw_pack_m9a-0.1.1/src/automas_script_maafw_pack_m9a/assets/m9a.png +0 -0
- automas_script_maafw_pack_m9a-0.1.1/src/automas_script_maafw_pack_m9a/migration.py +398 -0
- automas_script_maafw_pack_m9a-0.1.1/src/automas_script_maafw_pack_m9a/models.py +43 -0
- automas_script_maafw_pack_m9a-0.1.1/src/automas_script_maafw_pack_m9a/plugin.py +105 -0
- automas_script_maafw_pack_m9a-0.1.1/src/automas_script_maafw_pack_m9a/py.typed +1 -0
- automas_script_maafw_pack_m9a-0.1.1/src/automas_script_maafw_pack_m9a/schema.py +75 -0
- automas_script_maafw_pack_m9a-0.1.1/src/automas_script_maafw_pack_m9a/service.py +185 -0
- automas_script_maafw_pack_m9a-0.1.1/src/automas_script_maafw_pack_m9a.egg-info/PKG-INFO +16 -0
- automas_script_maafw_pack_m9a-0.1.1/src/automas_script_maafw_pack_m9a.egg-info/SOURCES.txt +16 -0
- automas_script_maafw_pack_m9a-0.1.1/src/automas_script_maafw_pack_m9a.egg-info/dependency_links.txt +1 -0
- automas_script_maafw_pack_m9a-0.1.1/src/automas_script_maafw_pack_m9a.egg-info/entry_points.txt +2 -0
- automas_script_maafw_pack_m9a-0.1.1/src/automas_script_maafw_pack_m9a.egg-info/requires.txt +2 -0
- automas_script_maafw_pack_m9a-0.1.1/src/automas_script_maafw_pack_m9a.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: automas-script-maafw-pack-m9a
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: M9A project pack declarations for the AUTO-MAS MaaFW plugin
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: automas-script-maafw>=0.1.2
|
|
8
|
+
Requires-Dist: pydantic>=2
|
|
9
|
+
|
|
10
|
+
# automas-script-maafw-pack-m9a
|
|
11
|
+
|
|
12
|
+
M9A project pack declarations for the AUTO-MAS MaaFW plugin.
|
|
13
|
+
|
|
14
|
+
The package provides `maafw.pack.m9a.v1`. It does not declare default project
|
|
15
|
+
sources, controller, resource, preset, task queues, or task templates. Those are
|
|
16
|
+
loaded through the generic MaaFW project/interface flow.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# automas-script-maafw-pack-m9a
|
|
2
|
+
|
|
3
|
+
M9A project pack declarations for the AUTO-MAS MaaFW plugin.
|
|
4
|
+
|
|
5
|
+
The package provides `maafw.pack.m9a.v1`. It does not declare default project
|
|
6
|
+
sources, controller, resource, preset, task queues, or task templates. Those are
|
|
7
|
+
loaded through the generic MaaFW project/interface flow.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "automas-script-maafw-pack-m9a"
|
|
7
|
+
version = "0.1.1"
|
|
8
|
+
description = "M9A project pack declarations for the AUTO-MAS MaaFW plugin"
|
|
9
|
+
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"automas-script-maafw>=0.1.2",
|
|
13
|
+
"pydantic>=2",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.entry-points."auto_mas.plugins"]
|
|
17
|
+
automas_script_maafw_pack_m9a = "automas_script_maafw_pack_m9a.plugin:Plugin"
|
|
18
|
+
|
|
19
|
+
[tool.setuptools.packages.find]
|
|
20
|
+
where = ["src"]
|
|
21
|
+
|
|
22
|
+
[tool.setuptools.package-data]
|
|
23
|
+
automas_script_maafw_pack_m9a = ["py.typed", "assets/*.png"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .models import (
|
|
4
|
+
M9AMigrationDraft,
|
|
5
|
+
M9ANotificationContent,
|
|
6
|
+
M9APackDefinition,
|
|
7
|
+
M9APeriodRule,
|
|
8
|
+
)
|
|
9
|
+
from .service import M9APackService
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"M9AMigrationDraft",
|
|
13
|
+
"M9ANotificationContent",
|
|
14
|
+
"M9APackDefinition",
|
|
15
|
+
"M9APackService",
|
|
16
|
+
"M9APeriodRule",
|
|
17
|
+
]
|
|
Binary file
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from automas_maafw_interface.models import MaaFWInterface, MaaFWOption, MaaFWTask
|
|
7
|
+
from automas_maafw_interface.service import MaaFWInterfaceService
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
_RESERVED_TASK_NAMES = {
|
|
11
|
+
"启动游戏",
|
|
12
|
+
"关闭游戏",
|
|
13
|
+
"切换账号",
|
|
14
|
+
"StartUp",
|
|
15
|
+
"CloseDown",
|
|
16
|
+
"Close1999",
|
|
17
|
+
"SwitchAccount",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
async def migrate_legacy_m9a_config(legacy_script: Any, provider: Any) -> Any:
|
|
22
|
+
"""Convert a persisted M9AConfig into the generated MaaFW plugin shape."""
|
|
23
|
+
|
|
24
|
+
raw_script = await legacy_script.toDict(if_decrypt=False)
|
|
25
|
+
project_path = str(_nested(raw_script, "Info", "Path") or "").strip()
|
|
26
|
+
if not project_path:
|
|
27
|
+
raise ValueError("旧 M9A 配置缺少项目路径,暂不自动迁移")
|
|
28
|
+
|
|
29
|
+
interface = MaaFWInterfaceService().load(project_path)
|
|
30
|
+
controller_name = _default_adb_controller(interface)
|
|
31
|
+
script_payload = _build_script_payload(raw_script, interface, controller_name)
|
|
32
|
+
script_payload["SubConfigsInfo"] = {
|
|
33
|
+
"UserData": await _build_user_collection(
|
|
34
|
+
legacy_script,
|
|
35
|
+
provider.user_config_class,
|
|
36
|
+
interface,
|
|
37
|
+
controller_name,
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
migrated = provider.script_config_class()
|
|
42
|
+
await migrated.load(script_payload)
|
|
43
|
+
return migrated
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _build_script_payload(
|
|
47
|
+
raw_script: dict[str, Any],
|
|
48
|
+
interface: MaaFWInterface,
|
|
49
|
+
controller_name: str,
|
|
50
|
+
) -> dict[str, Any]:
|
|
51
|
+
daily_tasks = (
|
|
52
|
+
_task_names_for_entries(interface, "Psychube")
|
|
53
|
+
if bool(_nested(raw_script, "Run", "IfPsychubeDailyOnce"))
|
|
54
|
+
else []
|
|
55
|
+
)
|
|
56
|
+
monthly_tasks = (
|
|
57
|
+
_task_names_for_entries(interface, "Limbo", "Lucidscape", "SleepDream")
|
|
58
|
+
if bool(_nested(raw_script, "Run", "IfSleepDreamMonthlyOnce"))
|
|
59
|
+
else []
|
|
60
|
+
)
|
|
61
|
+
return {
|
|
62
|
+
"Info": {
|
|
63
|
+
"Name": _nested(raw_script, "Info", "Name"),
|
|
64
|
+
"ProjectLabel": interface.label or interface.name,
|
|
65
|
+
"Path": _nested(raw_script, "Info", "Path"),
|
|
66
|
+
"Controller": controller_name,
|
|
67
|
+
"Resource": "",
|
|
68
|
+
},
|
|
69
|
+
"Emulator": {
|
|
70
|
+
"Id": _nested(raw_script, "Emulator", "Id"),
|
|
71
|
+
"Index": _nested(raw_script, "Emulator", "Index"),
|
|
72
|
+
},
|
|
73
|
+
"Update": {
|
|
74
|
+
"IfAutoUpdate": bool(
|
|
75
|
+
_nested(raw_script, "Run", "IfAutoUpdateAfterQueue")
|
|
76
|
+
),
|
|
77
|
+
},
|
|
78
|
+
"Run": {
|
|
79
|
+
"ProxyTimesLimit": _nested(raw_script, "Run", "ProxyTimesLimit"),
|
|
80
|
+
"RunTimesLimit": _nested(raw_script, "Run", "RunTimesLimit"),
|
|
81
|
+
"RunTimeLimit": _nested(raw_script, "Run", "RunTimeLimit"),
|
|
82
|
+
"DailyOnceTasks": _json(daily_tasks),
|
|
83
|
+
"WeeklyOnceTasks": _json([]),
|
|
84
|
+
"MonthlyOnceTasks": _json(monthly_tasks),
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
async def _build_user_collection(
|
|
90
|
+
legacy_script: Any,
|
|
91
|
+
user_config_class: type,
|
|
92
|
+
interface: MaaFWInterface,
|
|
93
|
+
controller_name: str,
|
|
94
|
+
) -> dict[str, Any]:
|
|
95
|
+
result: dict[str, Any] = {"instances": []}
|
|
96
|
+
for uid, old_user in legacy_script.UserData.items():
|
|
97
|
+
raw_user = await old_user.toDict(if_decrypt=False)
|
|
98
|
+
result["instances"].append(
|
|
99
|
+
{"uid": str(uid), "type": user_config_class.__name__}
|
|
100
|
+
)
|
|
101
|
+
result[str(uid)] = _build_user_payload(
|
|
102
|
+
raw_user,
|
|
103
|
+
interface,
|
|
104
|
+
controller_name,
|
|
105
|
+
)
|
|
106
|
+
return result
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _build_user_payload(
|
|
110
|
+
raw_user: dict[str, Any],
|
|
111
|
+
interface: MaaFWInterface,
|
|
112
|
+
controller_name: str,
|
|
113
|
+
) -> dict[str, Any]:
|
|
114
|
+
task_snapshot = _build_task_snapshot(
|
|
115
|
+
_load_json(_nested(raw_user, "Task", "Queue"), list),
|
|
116
|
+
interface,
|
|
117
|
+
)
|
|
118
|
+
period_records = {
|
|
119
|
+
"daily": _period_records(
|
|
120
|
+
interface,
|
|
121
|
+
str(_nested(raw_user, "Data", "LastPsychubeDate") or "2000-01-01"),
|
|
122
|
+
"Psychube",
|
|
123
|
+
),
|
|
124
|
+
"weekly": {},
|
|
125
|
+
"monthly": {
|
|
126
|
+
**_period_records(
|
|
127
|
+
interface,
|
|
128
|
+
str(_nested(raw_user, "Data", "LastLimboMonth") or "2000-01"),
|
|
129
|
+
"Limbo",
|
|
130
|
+
),
|
|
131
|
+
**_period_records(
|
|
132
|
+
interface,
|
|
133
|
+
str(
|
|
134
|
+
_nested(raw_user, "Data", "LastLucidscapeMonth")
|
|
135
|
+
or "2000-01"
|
|
136
|
+
),
|
|
137
|
+
"Lucidscape",
|
|
138
|
+
),
|
|
139
|
+
},
|
|
140
|
+
}
|
|
141
|
+
resource_name = _match_resource_name(
|
|
142
|
+
interface,
|
|
143
|
+
str(_nested(raw_user, "Info", "Resource") or ""),
|
|
144
|
+
controller_name,
|
|
145
|
+
)
|
|
146
|
+
payload = {
|
|
147
|
+
"Info": {
|
|
148
|
+
**_copy_group(
|
|
149
|
+
raw_user,
|
|
150
|
+
"Info",
|
|
151
|
+
(
|
|
152
|
+
"Name",
|
|
153
|
+
"Status",
|
|
154
|
+
"RemainedDay",
|
|
155
|
+
"IfScriptBeforeTask",
|
|
156
|
+
"ScriptBeforeTask",
|
|
157
|
+
"IfScriptAfterTask",
|
|
158
|
+
"ScriptAfterTask",
|
|
159
|
+
"Notes",
|
|
160
|
+
"Account",
|
|
161
|
+
),
|
|
162
|
+
),
|
|
163
|
+
"Controller": controller_name,
|
|
164
|
+
"Resource": resource_name,
|
|
165
|
+
},
|
|
166
|
+
"Task": {
|
|
167
|
+
"SelectedPreset": "",
|
|
168
|
+
"TaskSnapshot": _json(task_snapshot),
|
|
169
|
+
},
|
|
170
|
+
"Data": {
|
|
171
|
+
**_copy_group(
|
|
172
|
+
raw_user,
|
|
173
|
+
"Data",
|
|
174
|
+
("LastProxyDate", "ProxyTimes", "IfPassCheck"),
|
|
175
|
+
),
|
|
176
|
+
"PeriodTaskRecords": _json(period_records),
|
|
177
|
+
},
|
|
178
|
+
"Notify": _copy_group(
|
|
179
|
+
raw_user,
|
|
180
|
+
"Notify",
|
|
181
|
+
(
|
|
182
|
+
"Enabled",
|
|
183
|
+
"IfSendStatistic",
|
|
184
|
+
"IfSendMail",
|
|
185
|
+
"ToAddress",
|
|
186
|
+
"IfServerChan",
|
|
187
|
+
"ServerChanKey",
|
|
188
|
+
),
|
|
189
|
+
),
|
|
190
|
+
}
|
|
191
|
+
custom_webhooks = (
|
|
192
|
+
raw_user.get("SubConfigsInfo", {}).get("Notify_CustomWebhooks")
|
|
193
|
+
if isinstance(raw_user.get("SubConfigsInfo"), dict)
|
|
194
|
+
else None
|
|
195
|
+
)
|
|
196
|
+
if isinstance(custom_webhooks, dict):
|
|
197
|
+
payload["SubConfigsInfo"] = {
|
|
198
|
+
"Notify_CustomWebhooks": custom_webhooks,
|
|
199
|
+
}
|
|
200
|
+
return payload
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _build_task_snapshot(
|
|
204
|
+
queue: list[Any],
|
|
205
|
+
interface: MaaFWInterface,
|
|
206
|
+
) -> dict[str, Any]:
|
|
207
|
+
task_order: list[str] = []
|
|
208
|
+
task_checked = {task.name: False for task in interface.task}
|
|
209
|
+
task_options: dict[str, dict[str, Any]] = {}
|
|
210
|
+
for item in queue:
|
|
211
|
+
if not isinstance(item, dict):
|
|
212
|
+
continue
|
|
213
|
+
raw_name = str(item.get("name") or item.get("entry") or "").strip()
|
|
214
|
+
if not raw_name or raw_name in _RESERVED_TASK_NAMES:
|
|
215
|
+
continue
|
|
216
|
+
task = _match_task(interface, raw_name, str(item.get("entry") or ""))
|
|
217
|
+
if task is None or task.name in task_order:
|
|
218
|
+
continue
|
|
219
|
+
task_order.append(task.name)
|
|
220
|
+
task_checked[task.name] = True
|
|
221
|
+
task_options[task.name] = _convert_task_options(
|
|
222
|
+
item.get("options"),
|
|
223
|
+
interface,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
task_order.extend(task.name for task in interface.task if task.name not in task_order)
|
|
227
|
+
return {
|
|
228
|
+
"taskOrder": task_order,
|
|
229
|
+
"taskChecked": task_checked,
|
|
230
|
+
"taskOptions": task_options,
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _convert_task_options(
|
|
235
|
+
raw_options: Any,
|
|
236
|
+
interface: MaaFWInterface,
|
|
237
|
+
) -> dict[str, Any]:
|
|
238
|
+
converted: dict[str, Any] = {}
|
|
239
|
+
if not isinstance(raw_options, list):
|
|
240
|
+
return converted
|
|
241
|
+
for raw_option in raw_options:
|
|
242
|
+
if not isinstance(raw_option, dict):
|
|
243
|
+
continue
|
|
244
|
+
option_name = str(raw_option.get("name") or "").strip()
|
|
245
|
+
option = interface.option.get(option_name)
|
|
246
|
+
if not option_name or option is None:
|
|
247
|
+
continue
|
|
248
|
+
value = _convert_option_value(raw_option, option)
|
|
249
|
+
if value is not None:
|
|
250
|
+
converted[option_name] = value
|
|
251
|
+
converted.update(
|
|
252
|
+
_convert_task_options(raw_option.get("sub_options"), interface)
|
|
253
|
+
)
|
|
254
|
+
return converted
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _convert_option_value(raw_option: dict[str, Any], option: MaaFWOption) -> Any:
|
|
258
|
+
if option.type == "input":
|
|
259
|
+
value = raw_option.get("input_values", raw_option.get("data"))
|
|
260
|
+
if isinstance(value, dict):
|
|
261
|
+
return {
|
|
262
|
+
str(key): str(item)
|
|
263
|
+
for key, item in value.items()
|
|
264
|
+
if isinstance(key, str)
|
|
265
|
+
}
|
|
266
|
+
return None
|
|
267
|
+
if option.type == "checkbox":
|
|
268
|
+
selected = raw_option.get("selected_cases")
|
|
269
|
+
if isinstance(selected, list):
|
|
270
|
+
return [str(item) for item in selected]
|
|
271
|
+
return []
|
|
272
|
+
|
|
273
|
+
cases = option.cases or []
|
|
274
|
+
selected = raw_option.get("selected_cases")
|
|
275
|
+
if isinstance(selected, list) and selected:
|
|
276
|
+
return str(selected[0])
|
|
277
|
+
try:
|
|
278
|
+
index = int(raw_option.get("index", 0))
|
|
279
|
+
except (TypeError, ValueError):
|
|
280
|
+
index = 0
|
|
281
|
+
if 0 <= index < len(cases):
|
|
282
|
+
return cases[index].name
|
|
283
|
+
return cases[0].name if cases else None
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _match_task(
|
|
287
|
+
interface: MaaFWInterface,
|
|
288
|
+
name: str,
|
|
289
|
+
entry: str = "",
|
|
290
|
+
) -> MaaFWTask | None:
|
|
291
|
+
for candidate in (name, entry):
|
|
292
|
+
if not candidate:
|
|
293
|
+
continue
|
|
294
|
+
task = next(
|
|
295
|
+
(
|
|
296
|
+
item
|
|
297
|
+
for item in interface.task
|
|
298
|
+
if candidate in {item.name, item.entry, item.label}
|
|
299
|
+
),
|
|
300
|
+
None,
|
|
301
|
+
)
|
|
302
|
+
if task is not None:
|
|
303
|
+
return task
|
|
304
|
+
return None
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def _task_names_for_entries(
|
|
308
|
+
interface: MaaFWInterface,
|
|
309
|
+
*entries: str,
|
|
310
|
+
) -> list[str]:
|
|
311
|
+
wanted = set(entries)
|
|
312
|
+
return [
|
|
313
|
+
task.name
|
|
314
|
+
for task in interface.task
|
|
315
|
+
if task.entry in wanted or task.name in wanted
|
|
316
|
+
]
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _period_records(
|
|
320
|
+
interface: MaaFWInterface,
|
|
321
|
+
period_key: str,
|
|
322
|
+
*entries: str,
|
|
323
|
+
) -> dict[str, str]:
|
|
324
|
+
return {
|
|
325
|
+
task_name: period_key
|
|
326
|
+
for task_name in _task_names_for_entries(interface, *entries)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _default_adb_controller(interface: MaaFWInterface) -> str:
|
|
331
|
+
controller = next(
|
|
332
|
+
(item for item in interface.controller if item.type == "Adb"),
|
|
333
|
+
None,
|
|
334
|
+
)
|
|
335
|
+
return controller.name if controller is not None else ""
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _match_resource_name(
|
|
339
|
+
interface: MaaFWInterface,
|
|
340
|
+
legacy_value: str,
|
|
341
|
+
controller_name: str,
|
|
342
|
+
) -> str:
|
|
343
|
+
if legacy_value:
|
|
344
|
+
resource = next(
|
|
345
|
+
(
|
|
346
|
+
item
|
|
347
|
+
for item in interface.resource
|
|
348
|
+
if legacy_value in {item.name, item.label}
|
|
349
|
+
),
|
|
350
|
+
None,
|
|
351
|
+
)
|
|
352
|
+
if resource is not None:
|
|
353
|
+
return resource.name
|
|
354
|
+
resource = next(
|
|
355
|
+
(
|
|
356
|
+
item
|
|
357
|
+
for item in interface.resource
|
|
358
|
+
if not item.controller or controller_name in item.controller
|
|
359
|
+
),
|
|
360
|
+
None,
|
|
361
|
+
)
|
|
362
|
+
return resource.name if resource is not None else ""
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def _copy_group(
|
|
366
|
+
payload: dict[str, Any],
|
|
367
|
+
group_name: str,
|
|
368
|
+
field_names: tuple[str, ...],
|
|
369
|
+
) -> dict[str, Any]:
|
|
370
|
+
group = payload.get(group_name)
|
|
371
|
+
if not isinstance(group, dict):
|
|
372
|
+
return {}
|
|
373
|
+
return {name: group[name] for name in field_names if name in group}
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def _nested(payload: dict[str, Any], group: str, name: str) -> Any:
|
|
377
|
+
value = payload.get(group)
|
|
378
|
+
return value.get(name) if isinstance(value, dict) else None
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def _load_json(value: Any, expected_type: type) -> Any:
|
|
382
|
+
if isinstance(value, expected_type):
|
|
383
|
+
return value
|
|
384
|
+
if isinstance(value, str):
|
|
385
|
+
try:
|
|
386
|
+
parsed = json.loads(value)
|
|
387
|
+
except json.JSONDecodeError:
|
|
388
|
+
return expected_type()
|
|
389
|
+
if isinstance(parsed, expected_type):
|
|
390
|
+
return parsed
|
|
391
|
+
return expected_type()
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def _json(value: Any) -> str:
|
|
395
|
+
return json.dumps(value, ensure_ascii=False)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
__all__ = ["migrate_legacy_m9a_config"]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class M9APeriodRule(BaseModel):
|
|
9
|
+
task: str
|
|
10
|
+
period: Literal["daily", "weekly", "monthly"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class M9APackDefinition(BaseModel):
|
|
14
|
+
"""M9A project pack 定义,对应文档 6.3 MaaFWProjectPackDefinition。"""
|
|
15
|
+
|
|
16
|
+
key: str = "m9a"
|
|
17
|
+
display_name: str = "M9A"
|
|
18
|
+
project_repo: str | None = None
|
|
19
|
+
interface_path: str = "interface.json"
|
|
20
|
+
supported_controllers: list[str] = Field(default_factory=lambda: ["adb", "win32"])
|
|
21
|
+
default_controller: str = "adb"
|
|
22
|
+
default_resource: str | None = None
|
|
23
|
+
default_preset: str | None = None
|
|
24
|
+
default_task_queue: list[str] | None = None
|
|
25
|
+
period_rules: list[M9APeriodRule] = Field(default_factory=list)
|
|
26
|
+
reserved_task_semantics: dict[str, Any] = Field(default_factory=dict)
|
|
27
|
+
icon: str | None = "automas_script_maafw_pack_m9a:assets/m9a.png"
|
|
28
|
+
notes: str | None = None
|
|
29
|
+
# 额外能力声明(非 6.3 标准字段)
|
|
30
|
+
framework: str = "maafw"
|
|
31
|
+
capabilities: list[str] = Field(default_factory=list)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class M9ANotificationContent(BaseModel):
|
|
35
|
+
title: str
|
|
36
|
+
text: str
|
|
37
|
+
html: str | None = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class M9AMigrationDraft(BaseModel):
|
|
41
|
+
script: dict[str, Any]
|
|
42
|
+
users: list[dict[str, Any]] = Field(default_factory=list)
|
|
43
|
+
warnings: list[str] = Field(default_factory=list)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from app.plugins import ScriptAdapterDefinition, ScriptAdapterPlugin
|
|
6
|
+
from automas_script_maafw.adapter import MaaFWAdapterHooks
|
|
7
|
+
|
|
8
|
+
from .migration import migrate_legacy_m9a_config
|
|
9
|
+
from .schema import M9A_SCRIPT_GROUPS, M9A_USER_GROUPS
|
|
10
|
+
from .service import M9APackService
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from auto_mas_core import PluginContext
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
DEFAULT_INSTANCE = {
|
|
17
|
+
"name": "M9A MaaFW Pack",
|
|
18
|
+
"enabled": True,
|
|
19
|
+
"config": {},
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
schema = {
|
|
23
|
+
"__no_plugin_config__": {
|
|
24
|
+
"type": "boolean",
|
|
25
|
+
"default": True,
|
|
26
|
+
"hidden": True,
|
|
27
|
+
"configurable": False,
|
|
28
|
+
"title": "No plugin-level configuration",
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
SCRIPT_TYPE_BINDINGS = [
|
|
33
|
+
{
|
|
34
|
+
"type_key": "M9A",
|
|
35
|
+
"display_name": "M9A",
|
|
36
|
+
"script_config_class_name": "M9AConfig",
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Plugin(ScriptAdapterPlugin):
|
|
42
|
+
provides = ["maafw.pack.m9a.v1"]
|
|
43
|
+
wants = [
|
|
44
|
+
"maafw.registry.v1",
|
|
45
|
+
"maafw.interface.v1",
|
|
46
|
+
"maafw.project_update.v1",
|
|
47
|
+
"maafw.runner.v1",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
def __init__(self, ctx: "PluginContext") -> None:
|
|
51
|
+
super().__init__(ctx)
|
|
52
|
+
self.ctx = ctx
|
|
53
|
+
self.service = M9APackService()
|
|
54
|
+
|
|
55
|
+
def build_script_adapters(self):
|
|
56
|
+
return [
|
|
57
|
+
ScriptAdapterDefinition(
|
|
58
|
+
type_key="M9A",
|
|
59
|
+
display_name="M9A",
|
|
60
|
+
hooks_factory=MaaFWAdapterHooks,
|
|
61
|
+
script_groups=M9A_SCRIPT_GROUPS,
|
|
62
|
+
user_groups=M9A_USER_GROUPS,
|
|
63
|
+
script_class_name="M9APluginConfig",
|
|
64
|
+
user_class_name="M9APluginUserConfig",
|
|
65
|
+
module="automas_script_maafw.schema",
|
|
66
|
+
related_bindings={"EmulatorConfig": "EmulatorConfig"},
|
|
67
|
+
supported_modes=("AutoProxy",),
|
|
68
|
+
icon="M9A",
|
|
69
|
+
icon_path="automas_script_maafw_pack_m9a:assets/m9a.png",
|
|
70
|
+
editor_kind="plugin:automas_script_maafw_pack_m9a",
|
|
71
|
+
legacy_config_class_name="M9AConfig",
|
|
72
|
+
legacy_user_config_class_name="M9AUserConfig",
|
|
73
|
+
is_builtin=False,
|
|
74
|
+
metadata={
|
|
75
|
+
"framework": "maafw",
|
|
76
|
+
"source": "automas_script_maafw_pack_m9a",
|
|
77
|
+
"project_pack": "m9a",
|
|
78
|
+
"m9a_standalone": True,
|
|
79
|
+
"theme_color": "cyan",
|
|
80
|
+
"legacy_config_migrator": migrate_legacy_m9a_config,
|
|
81
|
+
"script_edit_hint": {
|
|
82
|
+
"text": "遇到配置问题时,可以查看",
|
|
83
|
+
"link_text": "M9A 配置指南",
|
|
84
|
+
"url": "https://doc.auto-mas.top/docs/script-guide/m9a.html",
|
|
85
|
+
"suffix": ",其中整理了项目目录、运行环境和任务配置步骤。",
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
)
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
async def on_start(self) -> None:
|
|
92
|
+
self.ctx.set("maafw.pack.m9a.v1", self.service)
|
|
93
|
+
registry = self.ctx.get("maafw.registry.v1")
|
|
94
|
+
if registry is not None and hasattr(registry, "register_project_pack"):
|
|
95
|
+
registry.register_project_pack(self.service.get_definition())
|
|
96
|
+
await super().on_start()
|
|
97
|
+
self.ctx.logger.info("maafw.pack.m9a.v1 ready")
|
|
98
|
+
|
|
99
|
+
async def on_stop(self, reason: str) -> None:
|
|
100
|
+
registry = self.ctx.get("maafw.registry.v1")
|
|
101
|
+
if registry is not None and hasattr(registry, "unregister_project_pack"):
|
|
102
|
+
registry.unregister_project_pack("m9a")
|
|
103
|
+
self.ctx.set("maafw.pack.m9a.v1", None)
|
|
104
|
+
await super().on_stop(reason)
|
|
105
|
+
self.ctx.logger.info(f"maafw.pack.m9a.v1 stopped, reason={reason}")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""M9A project-pack schema overrides for the shared MaaFW editor."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from dataclasses import replace
|
|
7
|
+
|
|
8
|
+
from app.plugins.fields import PluginFieldGroup
|
|
9
|
+
from automas_script_maafw.schema import SCRIPT_GROUPS, USER_GROUPS, build_source_config
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
schema = {
|
|
13
|
+
"__no_plugin_config__": {
|
|
14
|
+
"type": "boolean",
|
|
15
|
+
"default": True,
|
|
16
|
+
"hidden": True,
|
|
17
|
+
"configurable": False,
|
|
18
|
+
"title": "No plugin-level configuration",
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
M9A_PERIOD_TASK_DEFAULTS = {
|
|
24
|
+
"DailyOnceTasks": ["Psychube"],
|
|
25
|
+
"WeeklyOnceTasks": [],
|
|
26
|
+
"MonthlyOnceTasks": ["SleepDream"],
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _build_m9a_info_group(group: PluginFieldGroup) -> PluginFieldGroup:
|
|
31
|
+
name_field = replace(group.fields[0], default="新M9A脚本")
|
|
32
|
+
path_field = replace(
|
|
33
|
+
group.fields[2],
|
|
34
|
+
label="M9A 项目路径",
|
|
35
|
+
placeholder="选择包含 interface.json 的 M9A 项目目录",
|
|
36
|
+
)
|
|
37
|
+
return replace(
|
|
38
|
+
group,
|
|
39
|
+
fields=(
|
|
40
|
+
name_field,
|
|
41
|
+
group.fields[1],
|
|
42
|
+
path_field,
|
|
43
|
+
*group.fields[3:],
|
|
44
|
+
),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _build_m9a_run_group(group: PluginFieldGroup) -> PluginFieldGroup:
|
|
49
|
+
return replace(
|
|
50
|
+
group,
|
|
51
|
+
fields=tuple(
|
|
52
|
+
replace(field, default=json.dumps(M9A_PERIOD_TASK_DEFAULTS[field.name]))
|
|
53
|
+
if field.name in M9A_PERIOD_TASK_DEFAULTS
|
|
54
|
+
else field
|
|
55
|
+
for field in group.fields
|
|
56
|
+
),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _build_m9a_group(group: PluginFieldGroup) -> PluginFieldGroup:
|
|
61
|
+
if group.key == "Info":
|
|
62
|
+
return _build_m9a_info_group(group)
|
|
63
|
+
if group.key == "Run":
|
|
64
|
+
return _build_m9a_run_group(group)
|
|
65
|
+
return group
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
M9A_SCRIPT_GROUPS: tuple[PluginFieldGroup, ...] = tuple(
|
|
69
|
+
_build_m9a_group(group) for group in SCRIPT_GROUPS
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# User config intentionally reuses the MaaFW surface and storage shape.
|
|
73
|
+
M9A_USER_GROUPS = USER_GROUPS
|
|
74
|
+
|
|
75
|
+
__all__ = ["M9A_SCRIPT_GROUPS", "M9A_USER_GROUPS", "build_source_config"]
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .models import (
|
|
7
|
+
M9AMigrationDraft,
|
|
8
|
+
M9ANotificationContent,
|
|
9
|
+
M9APackDefinition,
|
|
10
|
+
M9APeriodRule,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
PERIOD_RULES = [
|
|
15
|
+
M9APeriodRule(task="Psychube", period="daily"),
|
|
16
|
+
M9APeriodRule(task="SleepDream", period="monthly"),
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
DEFAULT_TASK_QUEUE = [
|
|
20
|
+
"StartUp",
|
|
21
|
+
"Psychube",
|
|
22
|
+
"SleepDream",
|
|
23
|
+
"Award",
|
|
24
|
+
"CloseDown",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
RESERVED_TASK_SEMANTICS = {
|
|
28
|
+
"Psychube": {"period": "daily", "label": "每日心相"},
|
|
29
|
+
"SleepDream": {"period": "monthly", "label": "深眠浅梦"},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class M9APackService:
|
|
34
|
+
"""maafw.pack.m9a.v1 service."""
|
|
35
|
+
|
|
36
|
+
def get_definition(self) -> M9APackDefinition:
|
|
37
|
+
return M9APackDefinition(
|
|
38
|
+
key="m9a",
|
|
39
|
+
display_name="M9A",
|
|
40
|
+
project_repo="MAA1999/M9A",
|
|
41
|
+
interface_path="interface.json",
|
|
42
|
+
supported_controllers=["adb", "win32"],
|
|
43
|
+
default_controller="adb",
|
|
44
|
+
default_resource="resource",
|
|
45
|
+
default_preset="日常任务",
|
|
46
|
+
default_task_queue=DEFAULT_TASK_QUEUE,
|
|
47
|
+
period_rules=PERIOD_RULES,
|
|
48
|
+
reserved_task_semantics=RESERVED_TASK_SEMANTICS,
|
|
49
|
+
icon="automas_script_maafw_pack_m9a:assets/m9a.png",
|
|
50
|
+
notes="M9A 专项插件包 — 重返未来:1999 自动化",
|
|
51
|
+
framework="maafw",
|
|
52
|
+
capabilities=[
|
|
53
|
+
"period_rules",
|
|
54
|
+
"notification_translation",
|
|
55
|
+
"create_only_migration",
|
|
56
|
+
],
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def translate_notification(
|
|
60
|
+
self,
|
|
61
|
+
result: Any,
|
|
62
|
+
*,
|
|
63
|
+
script_name: str = "M9A",
|
|
64
|
+
user_name: str = "",
|
|
65
|
+
started_at: str | None = None,
|
|
66
|
+
ended_at: str | None = None,
|
|
67
|
+
) -> M9ANotificationContent:
|
|
68
|
+
payload = _to_mapping(result)
|
|
69
|
+
success = bool(payload.get("success"))
|
|
70
|
+
completed_tasks = [
|
|
71
|
+
str(item)
|
|
72
|
+
for item in payload.get("completedTasks", [])
|
|
73
|
+
if str(item).strip()
|
|
74
|
+
]
|
|
75
|
+
failed_task = str(payload.get("failedTask") or "").strip()
|
|
76
|
+
error_message = str(payload.get("errorMessage") or "").strip()
|
|
77
|
+
|
|
78
|
+
subject = user_name or script_name or "M9A"
|
|
79
|
+
title = f"{subject} 的 M9A 任务已完成" if success else f"{subject} 的 M9A 任务未完成"
|
|
80
|
+
lines = []
|
|
81
|
+
if started_at or ended_at:
|
|
82
|
+
lines.append(f"开始时间: {started_at or '-'}")
|
|
83
|
+
lines.append(f"结束时间: {ended_at or datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
84
|
+
if completed_tasks:
|
|
85
|
+
lines.append("已完成任务: " + ", ".join(completed_tasks))
|
|
86
|
+
else:
|
|
87
|
+
lines.append("已完成任务: 无")
|
|
88
|
+
if failed_task:
|
|
89
|
+
lines.append(f"失败任务: {failed_task}")
|
|
90
|
+
if error_message:
|
|
91
|
+
lines.append(f"失败原因: {error_message}")
|
|
92
|
+
if not error_message and not success:
|
|
93
|
+
lines.append("失败原因: MaaFW runner 未返回成功状态")
|
|
94
|
+
return M9ANotificationContent(title=title, text="\n".join(lines))
|
|
95
|
+
|
|
96
|
+
def create_migration_draft(
|
|
97
|
+
self,
|
|
98
|
+
old_script_config: dict[str, Any] | None,
|
|
99
|
+
old_user_configs: list[dict[str, Any]] | None = None,
|
|
100
|
+
*,
|
|
101
|
+
script_name: str = "M9A",
|
|
102
|
+
) -> M9AMigrationDraft:
|
|
103
|
+
warnings = [
|
|
104
|
+
"迁移入口只创建新插件配置草稿,不覆盖旧 M9A 配置。",
|
|
105
|
+
"项目来源、controller、resource、preset、任务队列和模板必须通过通用 MaaFW 流程现拉。",
|
|
106
|
+
]
|
|
107
|
+
script_payload = {
|
|
108
|
+
"type": "PluginScriptConfig",
|
|
109
|
+
"Meta": {"PluginTypeKey": "M9A"},
|
|
110
|
+
"Info": {"Name": script_name},
|
|
111
|
+
"PluginData": {
|
|
112
|
+
"Config": {
|
|
113
|
+
"pack": "m9a",
|
|
114
|
+
"legacy": _filter_legacy_script_fields(old_script_config or {}),
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
users: list[dict[str, Any]] = []
|
|
120
|
+
for index, old_user in enumerate(old_user_configs or [], start=1):
|
|
121
|
+
user_name = _extract_user_name(old_user) or f"M9A 用户 {index}"
|
|
122
|
+
users.append(
|
|
123
|
+
{
|
|
124
|
+
"type": "PluginUserConfig",
|
|
125
|
+
"Meta": {"PluginTypeKey": "M9A"},
|
|
126
|
+
"Info": {"Name": user_name},
|
|
127
|
+
"PluginData": {
|
|
128
|
+
"Config": {
|
|
129
|
+
"pack": "m9a",
|
|
130
|
+
"legacy": _filter_legacy_user_fields(old_user),
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
return M9AMigrationDraft(script=script_payload, users=users, warnings=warnings)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _to_mapping(value: Any) -> dict[str, Any]:
|
|
140
|
+
if isinstance(value, dict):
|
|
141
|
+
return value
|
|
142
|
+
if hasattr(value, "model_dump"):
|
|
143
|
+
data = value.model_dump(mode="json")
|
|
144
|
+
return data if isinstance(data, dict) else {}
|
|
145
|
+
if hasattr(value, "__dict__"):
|
|
146
|
+
return {
|
|
147
|
+
key: item
|
|
148
|
+
for key, item in vars(value).items()
|
|
149
|
+
if not key.startswith("_")
|
|
150
|
+
}
|
|
151
|
+
return {}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _filter_legacy_script_fields(value: dict[str, Any]) -> dict[str, Any]:
|
|
155
|
+
return {
|
|
156
|
+
"path": _nested_get(value, "Info", "Path"),
|
|
157
|
+
"autoUpdate": _nested_get(value, "Run", "IfAutoUpdateAfterQueue"),
|
|
158
|
+
"weeklyTasks": _nested_get(value, "Run", "WeeklyOnceTasks"),
|
|
159
|
+
"monthlyTasks": _nested_get(value, "Run", "MonthlyOnceTasks"),
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _filter_legacy_user_fields(value: dict[str, Any]) -> dict[str, Any]:
|
|
164
|
+
return {
|
|
165
|
+
"name": _extract_user_name(value),
|
|
166
|
+
"taskQueue": _nested_get(value, "Task", "Queue"),
|
|
167
|
+
"notifyEnabled": _nested_get(value, "Notify", "Enabled"),
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _nested_get(value: dict[str, Any], section: str, key: str) -> Any:
|
|
172
|
+
section_value = value.get(section)
|
|
173
|
+
if isinstance(section_value, dict):
|
|
174
|
+
return section_value.get(key)
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _extract_user_name(value: dict[str, Any]) -> str:
|
|
179
|
+
info = value.get("Info")
|
|
180
|
+
if isinstance(info, dict):
|
|
181
|
+
name = info.get("Name")
|
|
182
|
+
if isinstance(name, str) and name.strip():
|
|
183
|
+
return name.strip()
|
|
184
|
+
name = value.get("name")
|
|
185
|
+
return name.strip() if isinstance(name, str) else ""
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: automas-script-maafw-pack-m9a
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: M9A project pack declarations for the AUTO-MAS MaaFW plugin
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: automas-script-maafw>=0.1.2
|
|
8
|
+
Requires-Dist: pydantic>=2
|
|
9
|
+
|
|
10
|
+
# automas-script-maafw-pack-m9a
|
|
11
|
+
|
|
12
|
+
M9A project pack declarations for the AUTO-MAS MaaFW plugin.
|
|
13
|
+
|
|
14
|
+
The package provides `maafw.pack.m9a.v1`. It does not declare default project
|
|
15
|
+
sources, controller, resource, preset, task queues, or task templates. Those are
|
|
16
|
+
loaded through the generic MaaFW project/interface flow.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/automas_script_maafw_pack_m9a/__init__.py
|
|
4
|
+
src/automas_script_maafw_pack_m9a/migration.py
|
|
5
|
+
src/automas_script_maafw_pack_m9a/models.py
|
|
6
|
+
src/automas_script_maafw_pack_m9a/plugin.py
|
|
7
|
+
src/automas_script_maafw_pack_m9a/py.typed
|
|
8
|
+
src/automas_script_maafw_pack_m9a/schema.py
|
|
9
|
+
src/automas_script_maafw_pack_m9a/service.py
|
|
10
|
+
src/automas_script_maafw_pack_m9a.egg-info/PKG-INFO
|
|
11
|
+
src/automas_script_maafw_pack_m9a.egg-info/SOURCES.txt
|
|
12
|
+
src/automas_script_maafw_pack_m9a.egg-info/dependency_links.txt
|
|
13
|
+
src/automas_script_maafw_pack_m9a.egg-info/entry_points.txt
|
|
14
|
+
src/automas_script_maafw_pack_m9a.egg-info/requires.txt
|
|
15
|
+
src/automas_script_maafw_pack_m9a.egg-info/top_level.txt
|
|
16
|
+
src/automas_script_maafw_pack_m9a/assets/m9a.png
|
automas_script_maafw_pack_m9a-0.1.1/src/automas_script_maafw_pack_m9a.egg-info/dependency_links.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
automas_script_maafw_pack_m9a
|