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/loop.py
CHANGED
|
@@ -1,208 +1,222 @@
|
|
|
1
|
-
import time
|
|
2
|
-
from functools import lru_cache, partial
|
|
3
|
-
from typing import Callable, Any, overload, Literal, Generic, TypeVar, cast, get_args, get_origin
|
|
4
|
-
from typing_extensions import deprecated
|
|
5
|
-
|
|
6
|
-
from cv2.typing import MatLike
|
|
7
|
-
|
|
8
|
-
from kotonebot.
|
|
9
|
-
from kotonebot import
|
|
10
|
-
from kotonebot
|
|
11
|
-
from kotonebot.backend.
|
|
12
|
-
from kotonebot.
|
|
13
|
-
from .
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
self.
|
|
20
|
-
self.
|
|
21
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
self.
|
|
81
|
-
self.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
self.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
self.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
self.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
self.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
"""
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
self.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
def
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
1
|
+
import time
|
|
2
|
+
from functools import lru_cache, partial
|
|
3
|
+
from typing import Callable, Any, overload, Literal, Generic, TypeVar, cast, get_args, get_origin
|
|
4
|
+
from typing_extensions import deprecated
|
|
5
|
+
|
|
6
|
+
from cv2.typing import MatLike
|
|
7
|
+
|
|
8
|
+
from kotonebot.config.config import conf
|
|
9
|
+
from kotonebot.util import Interval
|
|
10
|
+
from kotonebot import device, image, ocr
|
|
11
|
+
from kotonebot.backend.core import Image
|
|
12
|
+
from kotonebot.backend.ocr import TextComparator
|
|
13
|
+
from kotonebot.client.protocol import ClickableObjectProtocol
|
|
14
|
+
from .context import vars
|
|
15
|
+
|
|
16
|
+
@deprecated('No longer used.')
|
|
17
|
+
class LoopAction:
|
|
18
|
+
def __init__(self, loop: 'Loop', func: Callable[[], ClickableObjectProtocol | None]):
|
|
19
|
+
self.loop = loop
|
|
20
|
+
self.func = func
|
|
21
|
+
self.result: ClickableObjectProtocol | None = None
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def found(self):
|
|
25
|
+
"""
|
|
26
|
+
是否找到结果。若父 Loop 未在运行中,则返回 False。
|
|
27
|
+
"""
|
|
28
|
+
if not self.loop.running:
|
|
29
|
+
return False
|
|
30
|
+
return bool(self.result)
|
|
31
|
+
|
|
32
|
+
def __bool__(self):
|
|
33
|
+
return self.found
|
|
34
|
+
|
|
35
|
+
def reset(self):
|
|
36
|
+
"""
|
|
37
|
+
重置 LoopAction,以复用此对象。
|
|
38
|
+
"""
|
|
39
|
+
self.result = None
|
|
40
|
+
|
|
41
|
+
def do(self):
|
|
42
|
+
"""
|
|
43
|
+
执行 LoopAction。
|
|
44
|
+
:return: 执行结果。
|
|
45
|
+
"""
|
|
46
|
+
if not self.loop.running:
|
|
47
|
+
return
|
|
48
|
+
if self.loop.found_anything:
|
|
49
|
+
# 本轮循环已执行任意操作,因此不需要再继续检测
|
|
50
|
+
return
|
|
51
|
+
self.result = self.func()
|
|
52
|
+
if self.result:
|
|
53
|
+
self.loop.found_anything = True
|
|
54
|
+
|
|
55
|
+
def click(self, *, at: tuple[int, int] | None = None):
|
|
56
|
+
"""
|
|
57
|
+
点击寻找结果。若结果为空,会跳过执行。
|
|
58
|
+
|
|
59
|
+
:return:
|
|
60
|
+
"""
|
|
61
|
+
if self.result:
|
|
62
|
+
if at is not None:
|
|
63
|
+
device.click(*at)
|
|
64
|
+
else:
|
|
65
|
+
device.click(self.result)
|
|
66
|
+
|
|
67
|
+
def call(self, func: Callable[[ClickableObjectProtocol], Any]):
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Loop:
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
*,
|
|
75
|
+
timeout: float = 300,
|
|
76
|
+
interval: float = 0.3,
|
|
77
|
+
auto_screenshot: bool = True,
|
|
78
|
+
skip_first_wait: bool = True
|
|
79
|
+
):
|
|
80
|
+
self.running = True
|
|
81
|
+
self.found_anything = False
|
|
82
|
+
self.auto_screenshot = auto_screenshot
|
|
83
|
+
"""
|
|
84
|
+
是否在每次循环开始时(Loop.tick() 被调用时)截图。
|
|
85
|
+
"""
|
|
86
|
+
self.__last_loop: float = -1
|
|
87
|
+
self.interval = interval
|
|
88
|
+
"""每次循环后等待的时间。"""
|
|
89
|
+
self.screenshot: MatLike | None = None
|
|
90
|
+
"""上次截图时的图像数据。"""
|
|
91
|
+
self.__skip_first_wait = skip_first_wait
|
|
92
|
+
self.__is_first_tick = True
|
|
93
|
+
|
|
94
|
+
def __iter__(self):
|
|
95
|
+
self.__is_first_tick = True
|
|
96
|
+
vars.flow.check()
|
|
97
|
+
return self
|
|
98
|
+
|
|
99
|
+
def __next__(self):
|
|
100
|
+
if not self.running:
|
|
101
|
+
raise StopIteration
|
|
102
|
+
self.found_anything = False
|
|
103
|
+
self.__last_loop = time.time()
|
|
104
|
+
return self.tick()
|
|
105
|
+
|
|
106
|
+
def tick(self):
|
|
107
|
+
if not (self.__is_first_tick and self.__skip_first_wait):
|
|
108
|
+
time.sleep(self.interval)
|
|
109
|
+
self.__is_first_tick = False
|
|
110
|
+
|
|
111
|
+
if self.auto_screenshot:
|
|
112
|
+
self.screenshot = device.screenshot()
|
|
113
|
+
self.__last_loop = time.time()
|
|
114
|
+
self.found_anything = False
|
|
115
|
+
# 执行全局回调
|
|
116
|
+
callbacks = conf().loop.loop_callbacks
|
|
117
|
+
while True:
|
|
118
|
+
did = False
|
|
119
|
+
for cb in callbacks:
|
|
120
|
+
did = cb(self)
|
|
121
|
+
if did:
|
|
122
|
+
time.sleep(self.interval)
|
|
123
|
+
self.screenshot = device.screenshot()
|
|
124
|
+
break
|
|
125
|
+
if not did:
|
|
126
|
+
break
|
|
127
|
+
|
|
128
|
+
return self
|
|
129
|
+
|
|
130
|
+
def exit(self):
|
|
131
|
+
"""
|
|
132
|
+
结束循环。
|
|
133
|
+
"""
|
|
134
|
+
self.running = False
|
|
135
|
+
|
|
136
|
+
@overload
|
|
137
|
+
@deprecated('Use plain if statement instead.')
|
|
138
|
+
def when(self, condition: Image) -> LoopAction:
|
|
139
|
+
...
|
|
140
|
+
|
|
141
|
+
@overload
|
|
142
|
+
@deprecated('Use plain if statement instead.')
|
|
143
|
+
def when(self, condition: TextComparator) -> LoopAction:
|
|
144
|
+
...
|
|
145
|
+
|
|
146
|
+
@deprecated('Use plain if statement instead.')
|
|
147
|
+
def when(self, condition: Any):
|
|
148
|
+
"""
|
|
149
|
+
判断某个条件是否成立。
|
|
150
|
+
|
|
151
|
+
:param condition:
|
|
152
|
+
:return:
|
|
153
|
+
"""
|
|
154
|
+
if isinstance(condition, Image):
|
|
155
|
+
func = partial(image.find, condition)
|
|
156
|
+
elif isinstance(condition, TextComparator):
|
|
157
|
+
func = partial(ocr.find, condition)
|
|
158
|
+
else:
|
|
159
|
+
raise ValueError('Invalid condition type.')
|
|
160
|
+
la = LoopAction(self, func)
|
|
161
|
+
la.reset()
|
|
162
|
+
la.do()
|
|
163
|
+
return la
|
|
164
|
+
|
|
165
|
+
@deprecated('Use plain if statement instead.')
|
|
166
|
+
def until(self, condition: Any):
|
|
167
|
+
"""
|
|
168
|
+
当满足指定条件时,结束循环。
|
|
169
|
+
|
|
170
|
+
等价于 ``loop.when(...).call(lambda _: loop.exit())``
|
|
171
|
+
"""
|
|
172
|
+
return self.when(condition).call(lambda _: self.exit())
|
|
173
|
+
|
|
174
|
+
@deprecated('Use image.find() and device.click() instead.')
|
|
175
|
+
def click_if(self, condition: Any, *, at: tuple[int, int] | None = None):
|
|
176
|
+
"""
|
|
177
|
+
检测指定对象是否出现,若出现,点击该对象或指定位置。
|
|
178
|
+
|
|
179
|
+
``click_if()`` 等价于 ``loop.when(...).click(...)``。
|
|
180
|
+
|
|
181
|
+
:param condition: 检测目标。
|
|
182
|
+
:param at: 点击位置。若为 None,表示点击找到的目标。
|
|
183
|
+
"""
|
|
184
|
+
return self.when(condition).click(at=at)
|
|
185
|
+
|
|
186
|
+
StateType = TypeVar('StateType')
|
|
187
|
+
class StatedLoop(Loop, Generic[StateType]):
|
|
188
|
+
def __init__(
|
|
189
|
+
self,
|
|
190
|
+
states: list[Any] | None = None,
|
|
191
|
+
initial_state: StateType | None = None,
|
|
192
|
+
*,
|
|
193
|
+
timeout: float = 300,
|
|
194
|
+
interval: float = 0.3,
|
|
195
|
+
auto_screenshot: bool = True
|
|
196
|
+
):
|
|
197
|
+
self.__tmp_states = states
|
|
198
|
+
self.__tmp_initial_state = initial_state
|
|
199
|
+
self.state: StateType
|
|
200
|
+
super().__init__(timeout=timeout, interval=interval, auto_screenshot=auto_screenshot)
|
|
201
|
+
|
|
202
|
+
def __iter__(self):
|
|
203
|
+
# __retrive_state_values() 只能在非 __init__ 中调用
|
|
204
|
+
self.__retrive_state_values()
|
|
205
|
+
return super().__iter__()
|
|
206
|
+
|
|
207
|
+
def __retrive_state_values(self):
|
|
208
|
+
# HACK: __orig_class__ 是 undocumented 属性
|
|
209
|
+
if not hasattr(self, '__orig_class__'):
|
|
210
|
+
# 如果 Foo 不是以参数化泛型的方式实例化的,可能没有 __orig_class__
|
|
211
|
+
if self.state is None:
|
|
212
|
+
raise ValueError('Either specify `states` or use StatedLoop[Literal[...]] syntax.')
|
|
213
|
+
else:
|
|
214
|
+
generic_type_args = get_args(self.__orig_class__) # type: ignore
|
|
215
|
+
if len(generic_type_args) != 1:
|
|
216
|
+
raise ValueError('StatedLoop must have exactly one generic type argument.')
|
|
217
|
+
state_values = get_args(generic_type_args[0])
|
|
218
|
+
if not state_values:
|
|
219
|
+
raise ValueError('StatedLoop must have at least one state value.')
|
|
220
|
+
self.states = cast(tuple[StateType, ...], state_values)
|
|
221
|
+
self.state = self.__tmp_initial_state or self.states[0]
|
|
222
|
+
return state_values
|