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.
Files changed (103) hide show
  1. kotonebot/__init__.py +39 -39
  2. kotonebot/backend/bot.py +312 -312
  3. kotonebot/backend/color.py +525 -525
  4. kotonebot/backend/context/__init__.py +3 -3
  5. kotonebot/backend/context/context.py +1002 -1002
  6. kotonebot/backend/context/task_action.py +183 -183
  7. kotonebot/backend/core.py +86 -129
  8. kotonebot/backend/debug/entry.py +89 -89
  9. kotonebot/backend/debug/mock.py +78 -78
  10. kotonebot/backend/debug/server.py +222 -222
  11. kotonebot/backend/debug/vars.py +351 -351
  12. kotonebot/backend/dispatch.py +227 -227
  13. kotonebot/backend/flow_controller.py +196 -196
  14. kotonebot/backend/image.py +36 -5
  15. kotonebot/backend/loop.py +222 -208
  16. kotonebot/backend/ocr.py +535 -535
  17. kotonebot/backend/preprocessor.py +103 -103
  18. kotonebot/client/__init__.py +9 -9
  19. kotonebot/client/device.py +369 -529
  20. kotonebot/client/fast_screenshot.py +377 -377
  21. kotonebot/client/host/__init__.py +43 -43
  22. kotonebot/client/host/adb_common.py +101 -107
  23. kotonebot/client/host/custom.py +118 -118
  24. kotonebot/client/host/leidian_host.py +196 -196
  25. kotonebot/client/host/mumu12_host.py +353 -353
  26. kotonebot/client/host/protocol.py +214 -214
  27. kotonebot/client/host/windows_common.py +58 -58
  28. kotonebot/client/implements/__init__.py +65 -70
  29. kotonebot/client/implements/adb.py +89 -89
  30. kotonebot/client/implements/nemu_ipc/__init__.py +11 -11
  31. kotonebot/client/implements/nemu_ipc/external_renderer_ipc.py +284 -284
  32. kotonebot/client/implements/nemu_ipc/nemu_ipc.py +327 -327
  33. kotonebot/client/implements/remote_windows.py +188 -188
  34. kotonebot/client/implements/uiautomator2.py +85 -85
  35. kotonebot/client/implements/windows.py +176 -176
  36. kotonebot/client/protocol.py +69 -69
  37. kotonebot/client/registration.py +24 -24
  38. kotonebot/client/scaler.py +467 -0
  39. kotonebot/config/base_config.py +96 -96
  40. kotonebot/config/config.py +61 -0
  41. kotonebot/config/manager.py +36 -36
  42. kotonebot/core/__init__.py +13 -0
  43. kotonebot/core/entities/base.py +182 -0
  44. kotonebot/core/entities/compound.py +75 -0
  45. kotonebot/core/entities/ocr.py +117 -0
  46. kotonebot/core/entities/template_match.py +198 -0
  47. kotonebot/devtools/__init__.py +42 -0
  48. kotonebot/devtools/cli/__init__.py +6 -0
  49. kotonebot/devtools/cli/main.py +53 -0
  50. kotonebot/{tools → devtools}/mirror.py +354 -354
  51. kotonebot/devtools/project/project.py +41 -0
  52. kotonebot/devtools/project/scanner.py +202 -0
  53. kotonebot/devtools/project/schema.py +99 -0
  54. kotonebot/devtools/resgen/__init__.py +42 -0
  55. kotonebot/devtools/resgen/codegen.py +331 -0
  56. kotonebot/devtools/resgen/core.py +94 -0
  57. kotonebot/devtools/resgen/parsers.py +360 -0
  58. kotonebot/devtools/resgen/utils.py +158 -0
  59. kotonebot/devtools/resgen/validation.py +115 -0
  60. kotonebot/devtools/web/dist/assets/bootstrap-icons-BOrJxbIo.woff +0 -0
  61. kotonebot/devtools/web/dist/assets/bootstrap-icons-BtvjY1KL.woff2 +0 -0
  62. kotonebot/devtools/web/dist/assets/ext-language_tools-CD021WJ2.js +2577 -0
  63. kotonebot/devtools/web/dist/assets/index-B_m5f2LF.js +2836 -0
  64. kotonebot/devtools/web/dist/assets/index-BlEDyGGa.css +9 -0
  65. kotonebot/devtools/web/dist/assets/language-client-C9muzqaq.js +128 -0
  66. kotonebot/devtools/web/dist/assets/mode-python-CtHp76XS.js +476 -0
  67. kotonebot/devtools/web/dist/icons/symbol-class.svg +3 -0
  68. kotonebot/devtools/web/dist/icons/symbol-file.svg +3 -0
  69. kotonebot/devtools/web/dist/icons/symbol-method.svg +3 -0
  70. kotonebot/devtools/web/dist/index.html +25 -0
  71. kotonebot/devtools/web/server/__init__.py +0 -0
  72. kotonebot/devtools/web/server/rest_api.py +217 -0
  73. kotonebot/devtools/web/server/server.py +85 -0
  74. kotonebot/errors.py +76 -76
  75. kotonebot/interop/win/__init__.py +11 -9
  76. kotonebot/interop/win/_mouse.py +310 -310
  77. kotonebot/interop/win/message_box.py +313 -313
  78. kotonebot/interop/win/reg.py +37 -37
  79. kotonebot/interop/win/shake_mouse.py +224 -0
  80. kotonebot/interop/win/shortcut.py +43 -43
  81. kotonebot/interop/win/task_dialog.py +513 -513
  82. kotonebot/logging/__init__.py +2 -2
  83. kotonebot/logging/log.py +17 -17
  84. kotonebot/primitives/__init__.py +19 -17
  85. kotonebot/primitives/geometry.py +1067 -862
  86. kotonebot/primitives/visual.py +143 -63
  87. kotonebot/ui/file_host/sensio.py +36 -36
  88. kotonebot/ui/file_host/tmp_send.py +54 -54
  89. kotonebot/ui/pushkit/__init__.py +3 -3
  90. kotonebot/ui/pushkit/image_host.py +88 -88
  91. kotonebot/ui/pushkit/protocol.py +13 -13
  92. kotonebot/ui/pushkit/wxpusher.py +54 -54
  93. kotonebot/ui/user.py +148 -148
  94. kotonebot/util.py +436 -436
  95. {kotonebot-0.5.0.dist-info → kotonebot-0.6.0.dist-info}/METADATA +84 -82
  96. kotonebot-0.6.0.dist-info/RECORD +105 -0
  97. kotonebot-0.6.0.dist-info/entry_points.txt +2 -0
  98. {kotonebot-0.5.0.dist-info → kotonebot-0.6.0.dist-info}/licenses/LICENSE +673 -673
  99. kotonebot/client/implements/adb_raw.py +0 -163
  100. kotonebot-0.5.0.dist-info/RECORD +0 -71
  101. /kotonebot/{tools → devtools/project}/__init__.py +0 -0
  102. {kotonebot-0.5.0.dist-info → kotonebot-0.6.0.dist-info}/WHEEL +0 -0
  103. {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,6 @@
1
+ def main():
2
+ """Entry point for the kbot command."""
3
+ from .main import main as _main
4
+ return _main()
5
+
6
+ __all__ = ["main"]
@@ -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()