kotonebot 0.5.0__py3-none-any.whl → 0.6.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.
- kotonebot/__init__.py +39 -39
- kotonebot/backend/bot.py +312 -312
- kotonebot/backend/color.py +525 -525
- kotonebot/backend/context/__init__.py +3 -3
- kotonebot/backend/context/context.py +1002 -1002
- kotonebot/backend/context/task_action.py +183 -183
- kotonebot/backend/core.py +86 -129
- kotonebot/backend/debug/entry.py +89 -89
- kotonebot/backend/debug/mock.py +78 -78
- kotonebot/backend/debug/server.py +222 -222
- kotonebot/backend/debug/vars.py +351 -351
- kotonebot/backend/dispatch.py +227 -227
- kotonebot/backend/flow_controller.py +196 -196
- kotonebot/backend/image.py +36 -5
- kotonebot/backend/loop.py +222 -208
- kotonebot/backend/ocr.py +535 -535
- kotonebot/backend/preprocessor.py +103 -103
- kotonebot/client/__init__.py +9 -9
- kotonebot/client/device.py +369 -529
- kotonebot/client/fast_screenshot.py +377 -377
- kotonebot/client/host/__init__.py +43 -43
- kotonebot/client/host/adb_common.py +101 -107
- kotonebot/client/host/custom.py +118 -118
- kotonebot/client/host/leidian_host.py +196 -196
- kotonebot/client/host/mumu12_host.py +353 -353
- kotonebot/client/host/protocol.py +214 -214
- kotonebot/client/host/windows_common.py +58 -58
- kotonebot/client/implements/__init__.py +65 -70
- kotonebot/client/implements/adb.py +89 -89
- kotonebot/client/implements/nemu_ipc/__init__.py +11 -11
- kotonebot/client/implements/nemu_ipc/external_renderer_ipc.py +284 -284
- kotonebot/client/implements/nemu_ipc/nemu_ipc.py +327 -327
- kotonebot/client/implements/remote_windows.py +188 -188
- kotonebot/client/implements/uiautomator2.py +85 -85
- kotonebot/client/implements/windows.py +176 -176
- kotonebot/client/protocol.py +69 -69
- kotonebot/client/registration.py +24 -24
- kotonebot/client/scaler.py +467 -0
- kotonebot/config/base_config.py +96 -96
- kotonebot/config/config.py +61 -0
- kotonebot/config/manager.py +36 -36
- kotonebot/core/__init__.py +13 -0
- kotonebot/core/entities/base.py +182 -0
- kotonebot/core/entities/compound.py +75 -0
- kotonebot/core/entities/ocr.py +117 -0
- kotonebot/core/entities/template_match.py +198 -0
- kotonebot/devtools/__init__.py +42 -0
- kotonebot/devtools/cli/__init__.py +6 -0
- kotonebot/devtools/cli/main.py +53 -0
- kotonebot/{tools → devtools}/mirror.py +354 -354
- kotonebot/devtools/project/project.py +41 -0
- kotonebot/devtools/project/scanner.py +202 -0
- kotonebot/devtools/project/schema.py +99 -0
- kotonebot/devtools/resgen/__init__.py +42 -0
- kotonebot/devtools/resgen/codegen.py +331 -0
- kotonebot/devtools/resgen/core.py +94 -0
- kotonebot/devtools/resgen/parsers.py +360 -0
- kotonebot/devtools/resgen/utils.py +158 -0
- kotonebot/devtools/resgen/validation.py +115 -0
- kotonebot/devtools/web/dist/assets/bootstrap-icons-BOrJxbIo.woff +0 -0
- kotonebot/devtools/web/dist/assets/bootstrap-icons-BtvjY1KL.woff2 +0 -0
- kotonebot/devtools/web/dist/assets/ext-language_tools-CD021WJ2.js +2577 -0
- kotonebot/devtools/web/dist/assets/index-B_m5f2LF.js +2836 -0
- kotonebot/devtools/web/dist/assets/index-BlEDyGGa.css +9 -0
- kotonebot/devtools/web/dist/assets/language-client-C9muzqaq.js +128 -0
- kotonebot/devtools/web/dist/assets/mode-python-CtHp76XS.js +476 -0
- kotonebot/devtools/web/dist/icons/symbol-class.svg +3 -0
- kotonebot/devtools/web/dist/icons/symbol-file.svg +3 -0
- kotonebot/devtools/web/dist/icons/symbol-method.svg +3 -0
- kotonebot/devtools/web/dist/index.html +25 -0
- kotonebot/devtools/web/server/__init__.py +0 -0
- kotonebot/devtools/web/server/rest_api.py +217 -0
- kotonebot/devtools/web/server/server.py +85 -0
- kotonebot/errors.py +76 -76
- kotonebot/interop/win/__init__.py +11 -9
- kotonebot/interop/win/_mouse.py +310 -310
- kotonebot/interop/win/message_box.py +313 -313
- kotonebot/interop/win/reg.py +37 -37
- kotonebot/interop/win/shake_mouse.py +224 -0
- kotonebot/interop/win/shortcut.py +43 -43
- kotonebot/interop/win/task_dialog.py +513 -513
- kotonebot/logging/__init__.py +2 -2
- kotonebot/logging/log.py +17 -17
- kotonebot/primitives/__init__.py +19 -17
- kotonebot/primitives/geometry.py +1067 -862
- kotonebot/primitives/visual.py +143 -63
- kotonebot/ui/file_host/sensio.py +36 -36
- kotonebot/ui/file_host/tmp_send.py +54 -54
- kotonebot/ui/pushkit/__init__.py +3 -3
- kotonebot/ui/pushkit/image_host.py +88 -88
- kotonebot/ui/pushkit/protocol.py +13 -13
- kotonebot/ui/pushkit/wxpusher.py +54 -54
- kotonebot/ui/user.py +148 -148
- kotonebot/util.py +436 -436
- {kotonebot-0.5.0.dist-info → kotonebot-0.6.0.dist-info}/METADATA +84 -82
- kotonebot-0.6.0.dist-info/RECORD +105 -0
- kotonebot-0.6.0.dist-info/entry_points.txt +2 -0
- {kotonebot-0.5.0.dist-info → kotonebot-0.6.0.dist-info}/licenses/LICENSE +673 -673
- kotonebot/client/implements/adb_raw.py +0 -163
- kotonebot-0.5.0.dist-info/RECORD +0 -71
- /kotonebot/{tools → devtools/project}/__init__.py +0 -0
- {kotonebot-0.5.0.dist-info → kotonebot-0.6.0.dist-info}/WHEEL +0 -0
- {kotonebot-0.5.0.dist-info → kotonebot-0.6.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Generic
|
|
2
|
+
from typing_extensions import Unpack, override
|
|
3
|
+
|
|
4
|
+
from kotonebot.devtools.project.schema import BoolProp, FloatProp, ImageProp, RectProp
|
|
5
|
+
from kotonebot.primitives import Rect, ImageSlice
|
|
6
|
+
from kotonebot.devtools import EditorMetadata
|
|
7
|
+
|
|
8
|
+
from .base import Prefab, FindKwargs, GameObjectType, ClickKwargs as _ClickKwargs, WaitKwargs as _WaitKwargs
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TemplateMatchFindKargs(FindKwargs[GameObjectType], total=False):
|
|
12
|
+
threshold: float | None
|
|
13
|
+
"""匹配阈值
|
|
14
|
+
|
|
15
|
+
如果指定,则覆盖 TemplateMatchPrefab 中定义的 threshold 属性。
|
|
16
|
+
"""
|
|
17
|
+
colored: bool | None
|
|
18
|
+
"""是否匹配颜色
|
|
19
|
+
|
|
20
|
+
如果指定,则覆盖 TemplateMatchPrefab 中定义的 colored 属性。
|
|
21
|
+
"""
|
|
22
|
+
region: Rect | None
|
|
23
|
+
"""搜索区域
|
|
24
|
+
|
|
25
|
+
如果指定,则覆盖 TemplateMatchPrefab 中定义的 region 属性。
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
class ClickKwargs(TemplateMatchFindKargs[GameObjectType], _ClickKwargs[GameObjectType], Generic[GameObjectType], total=False): pass
|
|
29
|
+
class WaitKwargs(TemplateMatchFindKargs[GameObjectType], _WaitKwargs[GameObjectType], Generic[GameObjectType], total=False): pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class TemplateMatchPrefab(Prefab[GameObjectType]):
|
|
33
|
+
"""基于模版匹配的 Prefab"""
|
|
34
|
+
template: ImageSlice
|
|
35
|
+
"""[必填] 用于匹配的模版图像"""
|
|
36
|
+
fixed: bool = False
|
|
37
|
+
"""[可选] 是否固定位置。
|
|
38
|
+
|
|
39
|
+
当 `fixed` 为 True 时,匹配将限定在 `template.slice_rect`(若存在)定义的区域内。
|
|
40
|
+
若 `template` 无 `slice_rect`,会在运行时抛出 ValueError,以提示生成代码或资源定义不完整。
|
|
41
|
+
"""
|
|
42
|
+
region: Rect | None = None
|
|
43
|
+
"""[可选] 限定搜索区域
|
|
44
|
+
|
|
45
|
+
默认为 None(全屏搜索)。
|
|
46
|
+
"""
|
|
47
|
+
threshold: float = 0.8
|
|
48
|
+
"""[可选] 匹配阈值
|
|
49
|
+
|
|
50
|
+
范围 0.0 - 1.0,默认为 0.8。
|
|
51
|
+
"""
|
|
52
|
+
colored: bool = False
|
|
53
|
+
"""[可选] 是否匹配颜色
|
|
54
|
+
|
|
55
|
+
默认为 False(不匹配颜色)。
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
class _Editor(EditorMetadata):
|
|
59
|
+
name = '模版'
|
|
60
|
+
description = '基于模版匹配来寻找对象'
|
|
61
|
+
primary_prop = 'template'
|
|
62
|
+
icon = 'media'
|
|
63
|
+
shortcut = 't'
|
|
64
|
+
props = {
|
|
65
|
+
'template': ImageProp(label='模版图像', description='用于匹配的模版图像', default_value=None),
|
|
66
|
+
'fixed': BoolProp(label='固定位置', description='对象位置是否固定不变,若固定可提升匹配速度', default_value=False),
|
|
67
|
+
'region': RectProp(label='搜索区域', description='限定搜索区域以提升匹配速度', default_value=None),
|
|
68
|
+
'threshold': FloatProp(label='匹配阈值', description='模版匹配的相似度阈值,范围 0.0 - 1.0', min=0.0, max=1.0, default_value=0.8),
|
|
69
|
+
'colored': BoolProp(label='匹配颜色', description='是否在匹配时考虑颜色信息', default_value=False),
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@override
|
|
74
|
+
@classmethod
|
|
75
|
+
def find(cls, **kwargs: Unpack[TemplateMatchFindKargs[GameObjectType]]) -> GameObjectType | None:
|
|
76
|
+
from kotonebot import image
|
|
77
|
+
predicate = kwargs.get('predicate')
|
|
78
|
+
threshold_override = kwargs.get('threshold')
|
|
79
|
+
threshold = cls.threshold if threshold_override is None else threshold_override
|
|
80
|
+
colored_override = kwargs.get('colored')
|
|
81
|
+
colored = cls.colored if colored_override is None else colored_override
|
|
82
|
+
region = kwargs.get('region', cls.region)
|
|
83
|
+
# If prefab is fixed and no explicit region provided, use template.slice_rect
|
|
84
|
+
if region is None and cls.fixed:
|
|
85
|
+
slice_rect = cls.template.slice_rect
|
|
86
|
+
if slice_rect is None:
|
|
87
|
+
raise ValueError(f"Prefab {cls.__name__} is marked fixed but template has no slice_rect")
|
|
88
|
+
region = slice_rect
|
|
89
|
+
result = image.find(
|
|
90
|
+
cls.template.pixels,
|
|
91
|
+
rect=region,
|
|
92
|
+
threshold=threshold,
|
|
93
|
+
colored=colored,
|
|
94
|
+
)
|
|
95
|
+
if result is None:
|
|
96
|
+
return None
|
|
97
|
+
obj_class = cls._get_object_class()
|
|
98
|
+
obj = obj_class()
|
|
99
|
+
obj.rect = result.rect
|
|
100
|
+
obj.prefab = cls
|
|
101
|
+
if predicate is not None and not predicate(obj):
|
|
102
|
+
return None
|
|
103
|
+
return obj
|
|
104
|
+
|
|
105
|
+
@override
|
|
106
|
+
@classmethod
|
|
107
|
+
def find_all(cls, **kwargs: Unpack[TemplateMatchFindKargs[GameObjectType]]) -> list[GameObjectType]:
|
|
108
|
+
from kotonebot import image
|
|
109
|
+
predicate = kwargs.get('predicate')
|
|
110
|
+
threshold_override = kwargs.get('threshold')
|
|
111
|
+
threshold = cls.threshold if threshold_override is None else threshold_override
|
|
112
|
+
colored_override = kwargs.get('colored')
|
|
113
|
+
colored = cls.colored if colored_override is None else colored_override
|
|
114
|
+
region = kwargs.get('region', cls.region)
|
|
115
|
+
if region is None and cls.fixed:
|
|
116
|
+
slice_rect = cls.template.slice_rect
|
|
117
|
+
if slice_rect is None:
|
|
118
|
+
raise ValueError(f"Prefab {cls.__name__} is marked fixed but template has no slice_rect")
|
|
119
|
+
region = slice_rect
|
|
120
|
+
results = image.find_all(
|
|
121
|
+
cls.template.pixels,
|
|
122
|
+
rect=region,
|
|
123
|
+
threshold=threshold,
|
|
124
|
+
colored=colored,
|
|
125
|
+
)
|
|
126
|
+
obj_class = cls._get_object_class()
|
|
127
|
+
objects: list[GameObjectType] = []
|
|
128
|
+
for r in results:
|
|
129
|
+
obj = obj_class()
|
|
130
|
+
obj.rect = r.rect
|
|
131
|
+
obj.prefab = cls
|
|
132
|
+
if predicate is None or predicate(obj):
|
|
133
|
+
objects.append(obj)
|
|
134
|
+
return objects
|
|
135
|
+
|
|
136
|
+
@override
|
|
137
|
+
@classmethod
|
|
138
|
+
def require(cls, **kwargs: Unpack[TemplateMatchFindKargs[GameObjectType]]) -> GameObjectType:
|
|
139
|
+
from kotonebot import image, device
|
|
140
|
+
from kotonebot.backend.image import TemplateNoMatchError
|
|
141
|
+
predicate = kwargs.get('predicate')
|
|
142
|
+
threshold_override = kwargs.get('threshold')
|
|
143
|
+
threshold = cls.threshold if threshold_override is None else threshold_override
|
|
144
|
+
colored_override = kwargs.get('colored')
|
|
145
|
+
colored = cls.colored if colored_override is None else colored_override
|
|
146
|
+
region = kwargs.get('region', cls.region)
|
|
147
|
+
if region is None and cls.fixed:
|
|
148
|
+
slice_rect = cls.template.slice_rect
|
|
149
|
+
if slice_rect is None:
|
|
150
|
+
raise ValueError(f"Prefab {cls.__name__} is marked fixed but template has no slice_rect")
|
|
151
|
+
region = slice_rect
|
|
152
|
+
if predicate is None:
|
|
153
|
+
# 直接使用 expect,未找到会抛出 TemplateNoMatchError
|
|
154
|
+
result = image.expect(
|
|
155
|
+
cls.template.pixels,
|
|
156
|
+
rect=region,
|
|
157
|
+
threshold=threshold,
|
|
158
|
+
colored=colored,
|
|
159
|
+
)
|
|
160
|
+
obj_class = cls._get_object_class()
|
|
161
|
+
obj = obj_class()
|
|
162
|
+
obj.rect = result.rect
|
|
163
|
+
obj.prefab = cls
|
|
164
|
+
return obj
|
|
165
|
+
else:
|
|
166
|
+
# 需要满足 predicate,则遍历所有匹配项
|
|
167
|
+
results = image.find_all(
|
|
168
|
+
cls.template.pixels,
|
|
169
|
+
rect=region,
|
|
170
|
+
threshold=threshold,
|
|
171
|
+
colored=colored,
|
|
172
|
+
)
|
|
173
|
+
obj_class = cls._get_object_class()
|
|
174
|
+
for r in results:
|
|
175
|
+
obj = obj_class()
|
|
176
|
+
obj.rect = r.rect
|
|
177
|
+
if predicate(obj):
|
|
178
|
+
obj.prefab = cls
|
|
179
|
+
return obj
|
|
180
|
+
# 没有任何匹配满足 predicate,抛出未找到异常
|
|
181
|
+
raise TemplateNoMatchError(device.screenshot(), cls.template.pixels)
|
|
182
|
+
|
|
183
|
+
if TYPE_CHECKING:
|
|
184
|
+
# 这些方法只需要重载声明,实际实现由基类提供不变
|
|
185
|
+
@classmethod
|
|
186
|
+
def exists(cls, **kwargs: Unpack[TemplateMatchFindKargs[GameObjectType]]) -> bool: ...
|
|
187
|
+
|
|
188
|
+
@classmethod
|
|
189
|
+
def click(cls, **kwargs: Unpack[ClickKwargs[GameObjectType]]) -> None: ...
|
|
190
|
+
|
|
191
|
+
@classmethod
|
|
192
|
+
def wait(cls, **kwargs: Unpack[WaitKwargs[GameObjectType]]) -> GameObjectType: ...
|
|
193
|
+
|
|
194
|
+
@classmethod
|
|
195
|
+
def try_click(cls, **kwargs: Unpack[ClickKwargs[GameObjectType]]) -> bool: ...
|
|
196
|
+
|
|
197
|
+
@classmethod
|
|
198
|
+
def try_wait(cls, **kwargs: Unpack[WaitKwargs[GameObjectType]]) -> GameObjectType | None: ...
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from .resgen import (
|
|
2
|
+
CodeWriter,
|
|
3
|
+
ResourceNode,
|
|
4
|
+
ClassNode,
|
|
5
|
+
SchemaParser,
|
|
6
|
+
StandardGenerator,
|
|
7
|
+
ParserRegistry,
|
|
8
|
+
KotoneV1Parser,
|
|
9
|
+
BasicSpriteParser,
|
|
10
|
+
to_camel_case,
|
|
11
|
+
unify_path,
|
|
12
|
+
build_class_tree,
|
|
13
|
+
ImageProcessor,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from .project.schema import EditorMetadata
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
# core
|
|
20
|
+
"CodeWriter",
|
|
21
|
+
"ResourceNode",
|
|
22
|
+
"ClassNode",
|
|
23
|
+
"SchemaParser",
|
|
24
|
+
|
|
25
|
+
# generator
|
|
26
|
+
"StandardGenerator",
|
|
27
|
+
|
|
28
|
+
# parsers
|
|
29
|
+
"ParserRegistry",
|
|
30
|
+
"KotoneV1Parser",
|
|
31
|
+
"BasicSpriteParser",
|
|
32
|
+
|
|
33
|
+
# utils
|
|
34
|
+
"to_camel_case",
|
|
35
|
+
"unify_path",
|
|
36
|
+
"build_class_tree",
|
|
37
|
+
"ImageProcessor",
|
|
38
|
+
|
|
39
|
+
# plugin
|
|
40
|
+
"EditorMetadata",
|
|
41
|
+
]
|
|
42
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import argparse
|
|
3
|
+
|
|
4
|
+
from ..web.server.server import start_devtools
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main():
|
|
8
|
+
"""KotoneBot CLI entry point."""
|
|
9
|
+
parser = argparse.ArgumentParser(
|
|
10
|
+
prog="kbot",
|
|
11
|
+
description="KotoneBot command-line interface"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
15
|
+
|
|
16
|
+
# devtools subcommand
|
|
17
|
+
devtools_parser = subparsers.add_parser(
|
|
18
|
+
"devtools",
|
|
19
|
+
help="Start the KotoneBot DevTools web server"
|
|
20
|
+
)
|
|
21
|
+
devtools_parser.add_argument(
|
|
22
|
+
"--port",
|
|
23
|
+
type=int,
|
|
24
|
+
default=1178,
|
|
25
|
+
help="Port to listen on (default: 1178)"
|
|
26
|
+
)
|
|
27
|
+
devtools_parser.add_argument(
|
|
28
|
+
"--host",
|
|
29
|
+
type=str,
|
|
30
|
+
default="127.0.0.1",
|
|
31
|
+
help="Host to listen on (default: 127.0.0.1)"
|
|
32
|
+
)
|
|
33
|
+
devtools_parser.add_argument(
|
|
34
|
+
"--no-browser",
|
|
35
|
+
action="store_true",
|
|
36
|
+
help="Do not automatically open browser"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
args = parser.parse_args()
|
|
40
|
+
|
|
41
|
+
if args.command == "devtools":
|
|
42
|
+
start_devtools(
|
|
43
|
+
host=args.host,
|
|
44
|
+
port=args.port,
|
|
45
|
+
open_browser=not args.no_browser
|
|
46
|
+
)
|
|
47
|
+
else:
|
|
48
|
+
parser.print_help()
|
|
49
|
+
sys.exit(0)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
main()
|