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
kotonebot/backend/debug/entry.py
CHANGED
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import runpy
|
|
3
|
-
import shutil
|
|
4
|
-
import argparse
|
|
5
|
-
import importlib
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from threading import Thread
|
|
8
|
-
|
|
9
|
-
from . import debug
|
|
10
|
-
from kotonebot import logging
|
|
11
|
-
from kotonebot.backend.context import init_context
|
|
12
|
-
|
|
13
|
-
logger = logging.getLogger(__name__)
|
|
14
|
-
|
|
15
|
-
def _task_thread(task_module: str):
|
|
16
|
-
"""任务线程。"""
|
|
17
|
-
runpy.run_module(task_module, run_name="__main__")
|
|
18
|
-
|
|
19
|
-
def _parse_args():
|
|
20
|
-
"""解析命令行参数。"""
|
|
21
|
-
parser = argparse.ArgumentParser(description='KotoneBot visual debug tool')
|
|
22
|
-
parser.add_argument(
|
|
23
|
-
'-s', '--save',
|
|
24
|
-
help='Save dump image and results to the specified folder',
|
|
25
|
-
type=str,
|
|
26
|
-
metavar='PATH'
|
|
27
|
-
)
|
|
28
|
-
parser.add_argument(
|
|
29
|
-
'-c', '--clear',
|
|
30
|
-
help='Clear the dump folder before running',
|
|
31
|
-
action='store_true'
|
|
32
|
-
)
|
|
33
|
-
parser.add_argument(
|
|
34
|
-
'-t', '--config-type',
|
|
35
|
-
help='The full path of the config data type. e.g. `kotonebot.tasks.common.BaseConfig`',
|
|
36
|
-
type=str,
|
|
37
|
-
metavar='TYPE',
|
|
38
|
-
required=True
|
|
39
|
-
)
|
|
40
|
-
parser.add_argument(
|
|
41
|
-
'input_module',
|
|
42
|
-
help='The module to run'
|
|
43
|
-
)
|
|
44
|
-
return parser.parse_args()
|
|
45
|
-
|
|
46
|
-
def _start_task_thread(module: str):
|
|
47
|
-
"""启动任务线程。"""
|
|
48
|
-
thread = Thread(target=_task_thread, args=(module,))
|
|
49
|
-
thread.start()
|
|
50
|
-
|
|
51
|
-
if __name__ == "__main__":
|
|
52
|
-
args = _parse_args()
|
|
53
|
-
debug.enabled = True
|
|
54
|
-
|
|
55
|
-
# 设置保存路径
|
|
56
|
-
if args.save:
|
|
57
|
-
save_path = Path(args.save)
|
|
58
|
-
debug.auto_save_to_folder = str(save_path)
|
|
59
|
-
if not os.path.exists(save_path):
|
|
60
|
-
os.makedirs(save_path)
|
|
61
|
-
if args.clear:
|
|
62
|
-
if debug.auto_save_to_folder:
|
|
63
|
-
try:
|
|
64
|
-
logger.info(f"Removing {debug.auto_save_to_folder}")
|
|
65
|
-
shutil.rmtree(debug.auto_save_to_folder)
|
|
66
|
-
except PermissionError:
|
|
67
|
-
logger.warning(f"Failed to remove {debug.auto_save_to_folder}. Trying to remove all contents instead.")
|
|
68
|
-
for root, dirs, files in os.walk(debug.auto_save_to_folder):
|
|
69
|
-
for file in files:
|
|
70
|
-
try:
|
|
71
|
-
os.remove(os.path.join(root, file))
|
|
72
|
-
except PermissionError:
|
|
73
|
-
raise
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
# 初始化上下文
|
|
77
|
-
module_name, class_name = args.config_type.rsplit('.', 1)
|
|
78
|
-
class_ = importlib.import_module(module_name).__getattribute__(class_name)
|
|
79
|
-
init_context(config_type=class_)
|
|
80
|
-
|
|
81
|
-
# 启动服务器
|
|
82
|
-
from .server import app
|
|
83
|
-
import uvicorn
|
|
84
|
-
|
|
85
|
-
# 启动任务线程
|
|
86
|
-
_start_task_thread(args.input_module)
|
|
87
|
-
|
|
88
|
-
# 启动服务器
|
|
89
|
-
uvicorn.run(app, host="127.0.0.1", port=8000, log_level='critical' if debug.hide_server_log else None)
|
|
1
|
+
import os
|
|
2
|
+
import runpy
|
|
3
|
+
import shutil
|
|
4
|
+
import argparse
|
|
5
|
+
import importlib
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from threading import Thread
|
|
8
|
+
|
|
9
|
+
from . import debug
|
|
10
|
+
from kotonebot import logging
|
|
11
|
+
from kotonebot.backend.context import init_context
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
def _task_thread(task_module: str):
|
|
16
|
+
"""任务线程。"""
|
|
17
|
+
runpy.run_module(task_module, run_name="__main__")
|
|
18
|
+
|
|
19
|
+
def _parse_args():
|
|
20
|
+
"""解析命令行参数。"""
|
|
21
|
+
parser = argparse.ArgumentParser(description='KotoneBot visual debug tool')
|
|
22
|
+
parser.add_argument(
|
|
23
|
+
'-s', '--save',
|
|
24
|
+
help='Save dump image and results to the specified folder',
|
|
25
|
+
type=str,
|
|
26
|
+
metavar='PATH'
|
|
27
|
+
)
|
|
28
|
+
parser.add_argument(
|
|
29
|
+
'-c', '--clear',
|
|
30
|
+
help='Clear the dump folder before running',
|
|
31
|
+
action='store_true'
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
'-t', '--config-type',
|
|
35
|
+
help='The full path of the config data type. e.g. `kotonebot.tasks.common.BaseConfig`',
|
|
36
|
+
type=str,
|
|
37
|
+
metavar='TYPE',
|
|
38
|
+
required=True
|
|
39
|
+
)
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
'input_module',
|
|
42
|
+
help='The module to run'
|
|
43
|
+
)
|
|
44
|
+
return parser.parse_args()
|
|
45
|
+
|
|
46
|
+
def _start_task_thread(module: str):
|
|
47
|
+
"""启动任务线程。"""
|
|
48
|
+
thread = Thread(target=_task_thread, args=(module,))
|
|
49
|
+
thread.start()
|
|
50
|
+
|
|
51
|
+
if __name__ == "__main__":
|
|
52
|
+
args = _parse_args()
|
|
53
|
+
debug.enabled = True
|
|
54
|
+
|
|
55
|
+
# 设置保存路径
|
|
56
|
+
if args.save:
|
|
57
|
+
save_path = Path(args.save)
|
|
58
|
+
debug.auto_save_to_folder = str(save_path)
|
|
59
|
+
if not os.path.exists(save_path):
|
|
60
|
+
os.makedirs(save_path)
|
|
61
|
+
if args.clear:
|
|
62
|
+
if debug.auto_save_to_folder:
|
|
63
|
+
try:
|
|
64
|
+
logger.info(f"Removing {debug.auto_save_to_folder}")
|
|
65
|
+
shutil.rmtree(debug.auto_save_to_folder)
|
|
66
|
+
except PermissionError:
|
|
67
|
+
logger.warning(f"Failed to remove {debug.auto_save_to_folder}. Trying to remove all contents instead.")
|
|
68
|
+
for root, dirs, files in os.walk(debug.auto_save_to_folder):
|
|
69
|
+
for file in files:
|
|
70
|
+
try:
|
|
71
|
+
os.remove(os.path.join(root, file))
|
|
72
|
+
except PermissionError:
|
|
73
|
+
raise
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# 初始化上下文
|
|
77
|
+
module_name, class_name = args.config_type.rsplit('.', 1)
|
|
78
|
+
class_ = importlib.import_module(module_name).__getattribute__(class_name)
|
|
79
|
+
init_context(config_type=class_)
|
|
80
|
+
|
|
81
|
+
# 启动服务器
|
|
82
|
+
from .server import app
|
|
83
|
+
import uvicorn
|
|
84
|
+
|
|
85
|
+
# 启动任务线程
|
|
86
|
+
_start_task_thread(args.input_module)
|
|
87
|
+
|
|
88
|
+
# 启动服务器
|
|
89
|
+
uvicorn.run(app, host="127.0.0.1", port=8000, log_level='critical' if debug.hide_server_log else None)
|
kotonebot/backend/debug/mock.py
CHANGED
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
import time
|
|
2
|
-
from typing_extensions import override
|
|
3
|
-
|
|
4
|
-
import cv2
|
|
5
|
-
from cv2.typing import MatLike
|
|
6
|
-
|
|
7
|
-
from kotonebot import sleep
|
|
8
|
-
from kotonebot.client.device import Device
|
|
9
|
-
|
|
10
|
-
class Video:
|
|
11
|
-
def __init__(self, path: str, fps: int):
|
|
12
|
-
self.path = path
|
|
13
|
-
self.fps = fps
|
|
14
|
-
self.paused = False
|
|
15
|
-
"""是否暂停"""
|
|
16
|
-
self.__cap = cv2.VideoCapture(path)
|
|
17
|
-
self.__last_frame = None
|
|
18
|
-
self.__last_time = 0
|
|
19
|
-
|
|
20
|
-
def __iter__(self):
|
|
21
|
-
return self
|
|
22
|
-
|
|
23
|
-
def __next__(self):
|
|
24
|
-
if self.paused:
|
|
25
|
-
return self.__last_frame
|
|
26
|
-
ret, frame = self.__cap.read()
|
|
27
|
-
if not ret:
|
|
28
|
-
raise StopIteration
|
|
29
|
-
self.__last_frame = frame
|
|
30
|
-
self.__last_time = time.time()
|
|
31
|
-
if self.__last_time - time.time() < 1 / self.fps:
|
|
32
|
-
sleep(1 / self.fps)
|
|
33
|
-
return frame
|
|
34
|
-
|
|
35
|
-
def pause(self):
|
|
36
|
-
self.paused = True
|
|
37
|
-
|
|
38
|
-
def resume(self):
|
|
39
|
-
self.paused = False
|
|
40
|
-
|
|
41
|
-
class MockDevice(Device):
|
|
42
|
-
def __init__(
|
|
43
|
-
self
|
|
44
|
-
):
|
|
45
|
-
super().__init__()
|
|
46
|
-
self.__video_stream = None
|
|
47
|
-
self.__image = None
|
|
48
|
-
self.__screen_size = None
|
|
49
|
-
|
|
50
|
-
def load_video(self, path: str, fps: int):
|
|
51
|
-
self.__video_stream = Video(path, fps)
|
|
52
|
-
return self.__video_stream
|
|
53
|
-
|
|
54
|
-
def load_image(self, img: str | MatLike):
|
|
55
|
-
if isinstance(img, str):
|
|
56
|
-
self.__image = cv2.imread(img)
|
|
57
|
-
else:
|
|
58
|
-
self.__image = img
|
|
59
|
-
return self.__image
|
|
60
|
-
|
|
61
|
-
def set_screen_size(self, width: int, height: int):
|
|
62
|
-
self.__screen_size = (width, height)
|
|
63
|
-
|
|
64
|
-
@override
|
|
65
|
-
def screenshot(self):
|
|
66
|
-
if self.__image is not None:
|
|
67
|
-
return self.__image
|
|
68
|
-
elif self.__video_stream is not None:
|
|
69
|
-
return next(self.__video_stream)
|
|
70
|
-
else:
|
|
71
|
-
raise RuntimeError('No video stream loaded')
|
|
72
|
-
|
|
73
|
-
@property
|
|
74
|
-
@override
|
|
75
|
-
def screen_size(self):
|
|
76
|
-
if self.__screen_size is not None:
|
|
77
|
-
return self.__screen_size
|
|
78
|
-
else:
|
|
1
|
+
import time
|
|
2
|
+
from typing_extensions import override
|
|
3
|
+
|
|
4
|
+
import cv2
|
|
5
|
+
from cv2.typing import MatLike
|
|
6
|
+
|
|
7
|
+
from kotonebot import sleep
|
|
8
|
+
from kotonebot.client.device import Device
|
|
9
|
+
|
|
10
|
+
class Video:
|
|
11
|
+
def __init__(self, path: str, fps: int):
|
|
12
|
+
self.path = path
|
|
13
|
+
self.fps = fps
|
|
14
|
+
self.paused = False
|
|
15
|
+
"""是否暂停"""
|
|
16
|
+
self.__cap = cv2.VideoCapture(path)
|
|
17
|
+
self.__last_frame = None
|
|
18
|
+
self.__last_time = 0
|
|
19
|
+
|
|
20
|
+
def __iter__(self):
|
|
21
|
+
return self
|
|
22
|
+
|
|
23
|
+
def __next__(self):
|
|
24
|
+
if self.paused:
|
|
25
|
+
return self.__last_frame
|
|
26
|
+
ret, frame = self.__cap.read()
|
|
27
|
+
if not ret:
|
|
28
|
+
raise StopIteration
|
|
29
|
+
self.__last_frame = frame
|
|
30
|
+
self.__last_time = time.time()
|
|
31
|
+
if self.__last_time - time.time() < 1 / self.fps:
|
|
32
|
+
sleep(1 / self.fps)
|
|
33
|
+
return frame
|
|
34
|
+
|
|
35
|
+
def pause(self):
|
|
36
|
+
self.paused = True
|
|
37
|
+
|
|
38
|
+
def resume(self):
|
|
39
|
+
self.paused = False
|
|
40
|
+
|
|
41
|
+
class MockDevice(Device):
|
|
42
|
+
def __init__(
|
|
43
|
+
self
|
|
44
|
+
):
|
|
45
|
+
super().__init__()
|
|
46
|
+
self.__video_stream = None
|
|
47
|
+
self.__image = None
|
|
48
|
+
self.__screen_size = None
|
|
49
|
+
|
|
50
|
+
def load_video(self, path: str, fps: int):
|
|
51
|
+
self.__video_stream = Video(path, fps)
|
|
52
|
+
return self.__video_stream
|
|
53
|
+
|
|
54
|
+
def load_image(self, img: str | MatLike):
|
|
55
|
+
if isinstance(img, str):
|
|
56
|
+
self.__image = cv2.imread(img)
|
|
57
|
+
else:
|
|
58
|
+
self.__image = img
|
|
59
|
+
return self.__image
|
|
60
|
+
|
|
61
|
+
def set_screen_size(self, width: int, height: int):
|
|
62
|
+
self.__screen_size = (width, height)
|
|
63
|
+
|
|
64
|
+
@override
|
|
65
|
+
def screenshot(self):
|
|
66
|
+
if self.__image is not None:
|
|
67
|
+
return self.__image
|
|
68
|
+
elif self.__video_stream is not None:
|
|
69
|
+
return next(self.__video_stream)
|
|
70
|
+
else:
|
|
71
|
+
raise RuntimeError('No video stream loaded')
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
@override
|
|
75
|
+
def screen_size(self):
|
|
76
|
+
if self.__screen_size is not None:
|
|
77
|
+
return self.__screen_size
|
|
78
|
+
else:
|
|
79
79
|
raise RuntimeError('No screen size set')
|