kotonebot 0.5.0__py3-none-any.whl → 0.7.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 (107) 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 +73 -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/__init__.py +1 -0
  36. kotonebot/client/implements/windows/print_window.py +133 -0
  37. kotonebot/client/implements/windows/send_message.py +324 -0
  38. kotonebot/client/implements/{windows.py → windows/windows.py} +175 -176
  39. kotonebot/client/protocol.py +69 -69
  40. kotonebot/client/registration.py +24 -24
  41. kotonebot/client/scaler.py +467 -0
  42. kotonebot/config/base_config.py +103 -96
  43. kotonebot/config/config.py +61 -0
  44. kotonebot/config/manager.py +36 -36
  45. kotonebot/core/__init__.py +13 -0
  46. kotonebot/core/entities/base.py +182 -0
  47. kotonebot/core/entities/compound.py +75 -0
  48. kotonebot/core/entities/ocr.py +117 -0
  49. kotonebot/core/entities/template_match.py +198 -0
  50. kotonebot/devtools/__init__.py +42 -0
  51. kotonebot/devtools/cli/__init__.py +6 -0
  52. kotonebot/devtools/cli/main.py +53 -0
  53. kotonebot/{tools → devtools}/mirror.py +354 -354
  54. kotonebot/devtools/project/project.py +41 -0
  55. kotonebot/devtools/project/scanner.py +202 -0
  56. kotonebot/devtools/project/schema.py +99 -0
  57. kotonebot/devtools/resgen/__init__.py +42 -0
  58. kotonebot/devtools/resgen/codegen.py +331 -0
  59. kotonebot/devtools/resgen/core.py +94 -0
  60. kotonebot/devtools/resgen/parsers.py +360 -0
  61. kotonebot/devtools/resgen/utils.py +158 -0
  62. kotonebot/devtools/resgen/validation.py +115 -0
  63. kotonebot/devtools/web/dist/assets/bootstrap-icons-BOrJxbIo.woff +0 -0
  64. kotonebot/devtools/web/dist/assets/bootstrap-icons-BtvjY1KL.woff2 +0 -0
  65. kotonebot/devtools/web/dist/assets/ext-language_tools-CD021WJ2.js +2577 -0
  66. kotonebot/devtools/web/dist/assets/index-B_m5f2LF.js +2836 -0
  67. kotonebot/devtools/web/dist/assets/index-BlEDyGGa.css +9 -0
  68. kotonebot/devtools/web/dist/assets/language-client-C9muzqaq.js +128 -0
  69. kotonebot/devtools/web/dist/assets/mode-python-CtHp76XS.js +476 -0
  70. kotonebot/devtools/web/dist/icons/symbol-class.svg +3 -0
  71. kotonebot/devtools/web/dist/icons/symbol-file.svg +3 -0
  72. kotonebot/devtools/web/dist/icons/symbol-method.svg +3 -0
  73. kotonebot/devtools/web/dist/index.html +25 -0
  74. kotonebot/devtools/web/server/__init__.py +0 -0
  75. kotonebot/devtools/web/server/rest_api.py +217 -0
  76. kotonebot/devtools/web/server/server.py +85 -0
  77. kotonebot/errors.py +76 -76
  78. kotonebot/interop/win/__init__.py +13 -9
  79. kotonebot/interop/win/_mouse.py +310 -310
  80. kotonebot/interop/win/message_box.py +313 -313
  81. kotonebot/interop/win/reg.py +37 -37
  82. kotonebot/interop/win/shake_mouse.py +224 -0
  83. kotonebot/interop/win/shortcut.py +43 -43
  84. kotonebot/interop/win/task_dialog.py +513 -513
  85. kotonebot/interop/win/window.py +89 -0
  86. kotonebot/logging/__init__.py +2 -2
  87. kotonebot/logging/log.py +17 -17
  88. kotonebot/primitives/__init__.py +19 -17
  89. kotonebot/primitives/geometry.py +1067 -862
  90. kotonebot/primitives/visual.py +143 -63
  91. kotonebot/ui/file_host/sensio.py +36 -36
  92. kotonebot/ui/file_host/tmp_send.py +54 -54
  93. kotonebot/ui/pushkit/__init__.py +3 -3
  94. kotonebot/ui/pushkit/image_host.py +88 -88
  95. kotonebot/ui/pushkit/protocol.py +13 -13
  96. kotonebot/ui/pushkit/wxpusher.py +54 -54
  97. kotonebot/ui/user.py +148 -148
  98. kotonebot/util.py +436 -436
  99. {kotonebot-0.5.0.dist-info → kotonebot-0.7.0.dist-info}/METADATA +84 -82
  100. kotonebot-0.7.0.dist-info/RECORD +109 -0
  101. {kotonebot-0.5.0.dist-info → kotonebot-0.7.0.dist-info}/WHEEL +1 -1
  102. kotonebot-0.7.0.dist-info/entry_points.txt +2 -0
  103. {kotonebot-0.5.0.dist-info → kotonebot-0.7.0.dist-info}/licenses/LICENSE +673 -673
  104. kotonebot/client/implements/adb_raw.py +0 -163
  105. kotonebot-0.5.0.dist-info/RECORD +0 -71
  106. /kotonebot/{tools → devtools/project}/__init__.py +0 -0
  107. {kotonebot-0.5.0.dist-info → kotonebot-0.7.0.dist-info}/top_level.txt +0 -0
@@ -1,71 +1,66 @@
1
- from typing import TYPE_CHECKING
2
- from kotonebot.util import is_windows, require_windows
3
-
4
- if TYPE_CHECKING:
5
- from .adb import AdbImpl, AdbImplConfig
6
- from .adb_raw import AdbRawImpl
7
- from .uiautomator2 import UiAutomator2Impl
8
- from .windows import WindowsImpl, WindowsImplConfig
9
- from .remote_windows import RemoteWindowsImpl, RemoteWindowsImplConfig, RemoteWindowsServer
10
- from .nemu_ipc import NemuIpcImpl, NemuIpcImplConfig, ExternalRendererIpc
11
-
12
-
13
- def _require_windows():
14
- global WindowsImpl, WindowsImplConfig
15
- global RemoteWindowsImpl, RemoteWindowsImplConfig, RemoteWindowsServer
16
- global NemuIpcImpl, NemuIpcImplConfig, ExternalRendererIpc
17
-
18
- if not is_windows():
19
- require_windows('"windows", "remote_windows" and "nemu_ipc" implementations')
20
- from .windows import WindowsImpl, WindowsImplConfig
21
- from .remote_windows import RemoteWindowsImpl, RemoteWindowsImplConfig, RemoteWindowsServer
22
- from .nemu_ipc import NemuIpcImpl, NemuIpcImplConfig, ExternalRendererIpc
23
-
24
- def _require_adb():
25
- global AdbImpl, AdbImplConfig
26
- global AdbRawImpl
27
-
28
- from .adb import AdbImpl, AdbImplConfig
29
- from .adb_raw import AdbRawImpl
30
-
31
- def _require_uiautomator2():
32
- global UiAutomator2Impl
33
-
34
- from .uiautomator2 import UiAutomator2Impl
35
-
36
- _IMPORT_NAMES = [
37
- (_require_windows, [
38
- 'WindowsImpl', 'WindowsImplConfig',
39
- 'RemoteWindowsImpl', 'RemoteWindowsImplConfig', 'RemoteWindowsServer',
40
- 'NemuIpcImpl', 'NemuIpcImplConfig', 'ExternalRendererIpc'
41
- ]),
42
- (_require_adb, [
43
- 'AdbImpl', 'AdbImplConfig',
44
- 'AdbRawImpl'
45
- ]),
46
- (_require_uiautomator2, [
47
- 'UiAutomator2Impl'
48
- ]),
49
- ]
50
-
51
-
52
- def __getattr__(name: str):
53
- for item in _IMPORT_NAMES:
54
- if name in item[1]:
55
- item[0]()
56
- break
57
- try:
58
- return globals()[name]
59
- except KeyError:
60
- raise AttributeError(name=name)
61
-
62
- __all__ = [
63
- # windows
64
- 'WindowsImpl', 'WindowsImplConfig',
65
- 'RemoteWindowsImpl', 'RemoteWindowsImplConfig', 'RemoteWindowsServer',
66
- 'NemuIpcImpl', 'NemuIpcImplConfig', 'ExternalRendererIpc',
67
- # android
68
- 'AdbImpl', 'AdbImplConfig',
69
- 'AdbRawImpl',
70
- 'UiAutomator2Impl'
1
+ from typing import TYPE_CHECKING
2
+ from kotonebot.util import is_windows, require_windows
3
+
4
+ if TYPE_CHECKING:
5
+ from .adb import AdbImpl, AdbImplConfig
6
+ from .uiautomator2 import UiAutomator2Impl
7
+ from .windows import WindowsImpl, WindowsImplConfig
8
+ from .remote_windows import RemoteWindowsImpl, RemoteWindowsImplConfig, RemoteWindowsServer
9
+ from .nemu_ipc import NemuIpcImpl, NemuIpcImplConfig, ExternalRendererIpc
10
+
11
+
12
+ def _require_windows():
13
+ global WindowsImpl, WindowsImplConfig
14
+ global RemoteWindowsImpl, RemoteWindowsImplConfig, RemoteWindowsServer
15
+ global NemuIpcImpl, NemuIpcImplConfig, ExternalRendererIpc
16
+
17
+ if not is_windows():
18
+ require_windows('"windows", "remote_windows" and "nemu_ipc" implementations')
19
+ from .windows import WindowsImpl, WindowsImplConfig
20
+ from .remote_windows import RemoteWindowsImpl, RemoteWindowsImplConfig, RemoteWindowsServer
21
+ from .nemu_ipc import NemuIpcImpl, NemuIpcImplConfig, ExternalRendererIpc
22
+
23
+ def _require_adb():
24
+ global AdbImpl, AdbImplConfig
25
+
26
+ from .adb import AdbImpl, AdbImplConfig
27
+
28
+ def _require_uiautomator2():
29
+ global UiAutomator2Impl
30
+
31
+ from .uiautomator2 import UiAutomator2Impl
32
+
33
+ _IMPORT_NAMES = [
34
+ (_require_windows, [
35
+ 'WindowsImpl', 'WindowsImplConfig',
36
+ 'RemoteWindowsImpl', 'RemoteWindowsImplConfig', 'RemoteWindowsServer',
37
+ 'NemuIpcImpl', 'NemuIpcImplConfig', 'ExternalRendererIpc'
38
+ ]),
39
+ (_require_adb, [
40
+ 'AdbImpl', 'AdbImplConfig',
41
+ ]),
42
+ (_require_uiautomator2, [
43
+ 'UiAutomator2Impl'
44
+ ]),
45
+ ]
46
+
47
+
48
+ def __getattr__(name: str):
49
+ for item in _IMPORT_NAMES:
50
+ if name in item[1]:
51
+ item[0]()
52
+ break
53
+ try:
54
+ return globals()[name]
55
+ except KeyError:
56
+ raise AttributeError(name=name)
57
+
58
+ __all__ = [
59
+ # windows
60
+ 'WindowsImpl', 'WindowsImplConfig',
61
+ 'RemoteWindowsImpl', 'RemoteWindowsImplConfig', 'RemoteWindowsServer',
62
+ 'NemuIpcImpl', 'NemuIpcImplConfig', 'ExternalRendererIpc',
63
+ # android
64
+ 'AdbImpl', 'AdbImplConfig',
65
+ 'UiAutomator2Impl'
71
66
  ]
@@ -1,89 +1,89 @@
1
- import logging
2
- from typing import cast
3
- from typing_extensions import override
4
-
5
- import cv2
6
- import numpy as np
7
- from cv2.typing import MatLike
8
- try:
9
- from adbutils._device import AdbDevice as AdbUtilsDevice
10
- except ImportError as _e:
11
- from kotonebot.errors import MissingDependencyError
12
- raise MissingDependencyError(_e, 'android')
13
-
14
- from ..device import AndroidDevice
15
- from ..protocol import AndroidCommandable, Touchable, Screenshotable
16
- from ..registration import ImplConfig
17
- from dataclasses import dataclass
18
-
19
- logger = logging.getLogger(__name__)
20
-
21
- # 定义配置模型
22
- @dataclass
23
- class AdbImplConfig(ImplConfig):
24
- addr: str
25
- connect: bool = True
26
- disconnect: bool = True
27
- device_serial: str | None = None
28
- timeout: float = 180
29
-
30
- class AdbImpl(AndroidCommandable, Touchable, Screenshotable):
31
- def __init__(self, adb_connection: AdbUtilsDevice):
32
- self.adb = adb_connection
33
-
34
- @override
35
- def launch_app(self, package_name: str) -> None:
36
- self.adb.shell(f"monkey -p {package_name} 1")
37
-
38
- @override
39
- def current_package(self) -> str | None:
40
- # https://blog.csdn.net/guangdeshishe/article/details/117154406
41
- result_text = self.adb.shell('dumpsys activity top | grep ACTIVITY | tail -n 1')
42
- logger.debug(f"adb returned: {result_text}")
43
- if not isinstance(result_text, str):
44
- logger.error(f"Invalid result_text: {result_text}")
45
- return None
46
- result_text = result_text.strip()
47
- if result_text == '':
48
- logger.error("No current package found")
49
- return None
50
- _, activity, *_ = result_text.split(' ')
51
- package = activity.split('/')[0]
52
- return package
53
-
54
- def adb_shell(self, cmd: str) -> str:
55
- """执行 ADB shell 命令"""
56
- return cast(str, self.adb.shell(cmd))
57
-
58
- @override
59
- def detect_orientation(self):
60
- # 判断方向:https://stackoverflow.com/questions/10040624/check-if-device-is-landscape-via-adb
61
- # 但是上面这种方法不准确
62
- # 因此这里直接通过截图判断方向
63
- img = self.screenshot()
64
- if img.shape[0] > img.shape[1]:
65
- return 'portrait'
66
- return 'landscape'
67
-
68
- @property
69
- def screen_size(self) -> tuple[int, int]:
70
- ret = cast(str, self.adb.shell("wm size")).strip('Physical size: ')
71
- spiltted = tuple(map(int, ret.split("x")))
72
- # 检测当前方向
73
- orientation = self.detect_orientation()
74
- landscape = orientation == 'landscape'
75
- spiltted = tuple(sorted(spiltted, reverse=landscape))
76
- if len(spiltted) != 2:
77
- raise ValueError(f"Invalid screen size: {ret}")
78
- return spiltted
79
-
80
- def screenshot(self) -> MatLike:
81
- return cv2.cvtColor(np.array(self.adb.screenshot()), cv2.COLOR_RGB2BGR)
82
-
83
- def click(self, x: int, y: int) -> None:
84
- self.adb.shell(f"input tap {x} {y}")
85
-
86
- def swipe(self, x1: int, y1: int, x2: int, y2: int, duration: float | None = None) -> None:
87
- if duration is not None:
88
- logger.warning("Swipe duration is not supported with AdbDevice. Ignoring duration.")
89
- self.adb.shell(f"input touchscreen swipe {x1} {y1} {x2} {y2}")
1
+ import logging
2
+ from typing import cast
3
+ from typing_extensions import override
4
+
5
+ import cv2
6
+ import numpy as np
7
+ from cv2.typing import MatLike
8
+ try:
9
+ from adbutils._device import AdbDevice as AdbUtilsDevice
10
+ except ImportError as _e:
11
+ from kotonebot.errors import MissingDependencyError
12
+ raise MissingDependencyError(_e, 'android')
13
+
14
+ from ..device import AndroidDevice
15
+ from ..protocol import AndroidCommandable, Touchable, Screenshotable
16
+ from ..registration import ImplConfig
17
+ from dataclasses import dataclass
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ # 定义配置模型
22
+ @dataclass
23
+ class AdbImplConfig(ImplConfig):
24
+ addr: str
25
+ connect: bool = True
26
+ disconnect: bool = True
27
+ device_serial: str | None = None
28
+ timeout: float = 180
29
+
30
+ class AdbImpl(AndroidCommandable, Touchable, Screenshotable):
31
+ def __init__(self, adb_connection: AdbUtilsDevice):
32
+ self.adb = adb_connection
33
+
34
+ @override
35
+ def launch_app(self, package_name: str) -> None:
36
+ self.adb.shell(f"monkey -p {package_name} 1")
37
+
38
+ @override
39
+ def current_package(self) -> str | None:
40
+ # https://blog.csdn.net/guangdeshishe/article/details/117154406
41
+ result_text = self.adb.shell('dumpsys activity top | grep ACTIVITY | tail -n 1')
42
+ logger.debug(f"adb returned: {result_text}")
43
+ if not isinstance(result_text, str):
44
+ logger.error(f"Invalid result_text: {result_text}")
45
+ return None
46
+ result_text = result_text.strip()
47
+ if result_text == '':
48
+ logger.error("No current package found")
49
+ return None
50
+ _, activity, *_ = result_text.split(' ')
51
+ package = activity.split('/')[0]
52
+ return package
53
+
54
+ def adb_shell(self, cmd: str) -> str:
55
+ """执行 ADB shell 命令"""
56
+ return cast(str, self.adb.shell(cmd))
57
+
58
+ @override
59
+ def detect_orientation(self):
60
+ # 判断方向:https://stackoverflow.com/questions/10040624/check-if-device-is-landscape-via-adb
61
+ # 但是上面这种方法不准确
62
+ # 因此这里直接通过截图判断方向
63
+ img = self.screenshot()
64
+ if img.shape[0] > img.shape[1]:
65
+ return 'portrait'
66
+ return 'landscape'
67
+
68
+ @property
69
+ def screen_size(self) -> tuple[int, int]:
70
+ ret = cast(str, self.adb.shell("wm size")).strip('Physical size: ')
71
+ spiltted = tuple(map(int, ret.split("x")))
72
+ # 检测当前方向
73
+ orientation = self.detect_orientation()
74
+ landscape = orientation == 'landscape'
75
+ spiltted = tuple(sorted(spiltted, reverse=landscape))
76
+ if len(spiltted) != 2:
77
+ raise ValueError(f"Invalid screen size: {ret}")
78
+ return spiltted
79
+
80
+ def screenshot(self) -> MatLike:
81
+ return cv2.cvtColor(np.array(self.adb.screenshot()), cv2.COLOR_RGB2BGR)
82
+
83
+ def click(self, x: int, y: int) -> None:
84
+ self.adb.shell(f"input tap {x} {y}")
85
+
86
+ def swipe(self, x1: int, y1: int, x2: int, y2: int, duration: float | None = None) -> None:
87
+ if duration is not None:
88
+ logger.warning("Swipe duration is not supported with AdbDevice. Ignoring duration.")
89
+ self.adb.shell(f"input touchscreen swipe {x1} {y1} {x2} {y2}")
@@ -1,12 +1,12 @@
1
- # ruff: noqa: E402
2
- from kotonebot.util import require_windows
3
- require_windows('"RemoteWindowsImpl" implementation')
4
-
5
- from .external_renderer_ipc import ExternalRendererIpc
6
- from .nemu_ipc import NemuIpcImpl, NemuIpcImplConfig
7
-
8
- __all__ = [
9
- "ExternalRendererIpc",
10
- "NemuIpcImpl",
11
- "NemuIpcImplConfig",
1
+ # ruff: noqa: E402
2
+ from kotonebot.util import require_windows
3
+ require_windows('"RemoteWindowsImpl" implementation')
4
+
5
+ from .external_renderer_ipc import ExternalRendererIpc
6
+ from .nemu_ipc import NemuIpcImpl, NemuIpcImplConfig
7
+
8
+ __all__ = [
9
+ "ExternalRendererIpc",
10
+ "NemuIpcImpl",
11
+ "NemuIpcImplConfig",
12
12
  ]