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.
- 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 +73 -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/__init__.py +1 -0
- kotonebot/client/implements/windows/print_window.py +133 -0
- kotonebot/client/implements/windows/send_message.py +324 -0
- kotonebot/client/implements/{windows.py → windows/windows.py} +175 -176
- kotonebot/client/protocol.py +69 -69
- kotonebot/client/registration.py +24 -24
- kotonebot/client/scaler.py +467 -0
- kotonebot/config/base_config.py +103 -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 +13 -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/interop/win/window.py +89 -0
- 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.7.0.dist-info}/METADATA +84 -82
- kotonebot-0.7.0.dist-info/RECORD +109 -0
- {kotonebot-0.5.0.dist-info → kotonebot-0.7.0.dist-info}/WHEEL +1 -1
- kotonebot-0.7.0.dist-info/entry_points.txt +2 -0
- {kotonebot-0.5.0.dist-info → kotonebot-0.7.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.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 .
|
|
7
|
-
from .
|
|
8
|
-
from .
|
|
9
|
-
from .
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
global
|
|
15
|
-
global
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
from .
|
|
21
|
-
from .
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
'
|
|
41
|
-
]),
|
|
42
|
-
(
|
|
43
|
-
'
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
#
|
|
64
|
-
'
|
|
65
|
-
'
|
|
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
|
]
|