kotonebot 0.4.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.
- kotonebot/backend/context/context.py +1002 -1002
- kotonebot/backend/core.py +6 -49
- kotonebot/backend/image.py +36 -5
- kotonebot/backend/loop.py +222 -208
- kotonebot/backend/ocr.py +7 -1
- kotonebot/client/device.py +108 -243
- kotonebot/client/host/__init__.py +34 -3
- kotonebot/client/host/adb_common.py +7 -9
- kotonebot/client/host/custom.py +6 -2
- kotonebot/client/host/leidian_host.py +2 -7
- kotonebot/client/host/mumu12_host.py +2 -7
- kotonebot/client/host/protocol.py +4 -3
- kotonebot/client/implements/__init__.py +62 -11
- kotonebot/client/implements/adb.py +5 -1
- kotonebot/client/implements/nemu_ipc/__init__.py +4 -0
- kotonebot/client/implements/uiautomator2.py +6 -2
- kotonebot/client/implements/windows.py +7 -3
- kotonebot/client/registration.py +1 -1
- kotonebot/client/scaler.py +467 -0
- kotonebot/config/base_config.py +1 -1
- kotonebot/config/config.py +61 -0
- 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/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 +7 -2
- kotonebot/interop/win/__init__.py +10 -1
- kotonebot/interop/win/_mouse.py +311 -0
- kotonebot/interop/win/shake_mouse.py +224 -0
- kotonebot/primitives/__init__.py +3 -1
- kotonebot/primitives/geometry.py +817 -40
- kotonebot/primitives/visual.py +81 -1
- kotonebot/ui/pushkit/image_host.py +2 -1
- kotonebot/ui/pushkit/wxpusher.py +2 -1
- {kotonebot-0.4.0.dist-info → kotonebot-0.6.0.dist-info}/METADATA +4 -1
- kotonebot-0.6.0.dist-info/RECORD +105 -0
- kotonebot-0.6.0.dist-info/entry_points.txt +2 -0
- kotonebot/client/implements/adb_raw.py +0 -159
- kotonebot-0.4.0.dist-info/RECORD +0 -70
- /kotonebot/{tools → devtools}/mirror.py +0 -0
- /kotonebot/{tools → devtools/project}/__init__.py +0 -0
- {kotonebot-0.4.0.dist-info → kotonebot-0.6.0.dist-info}/WHEEL +0 -0
- {kotonebot-0.4.0.dist-info → kotonebot-0.6.0.dist-info}/licenses/LICENSE +0 -0
- {kotonebot-0.4.0.dist-info → kotonebot-0.6.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import logging
|
|
3
|
+
import threading
|
|
4
|
+
from typing import List, Callable, Optional
|
|
5
|
+
|
|
6
|
+
from ._mouse import get_pos
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ShakeMouse:
|
|
12
|
+
"""
|
|
13
|
+
全局鼠标晃动检测器。
|
|
14
|
+
|
|
15
|
+
模拟 macOS 的鼠标晃动检测机制。
|
|
16
|
+
使用“累积行程+反转计数”算法,有效区分“快速移动”与“快速晃动”,防止误触。
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
_enabled: bool = False
|
|
20
|
+
_thread: Optional[threading.Thread] = None
|
|
21
|
+
_callbacks: List[Callable[[], None]] = []
|
|
22
|
+
_lock = threading.Lock()
|
|
23
|
+
|
|
24
|
+
# --- 算法参数配置 ---
|
|
25
|
+
|
|
26
|
+
SAMPLE_INTERVAL: float = 0.016
|
|
27
|
+
"""
|
|
28
|
+
采样间隔 (秒)。
|
|
29
|
+
|
|
30
|
+
越小越灵敏,但在低性能机器上可能占用更多 CPU。
|
|
31
|
+
0.016 约为 60Hz。
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
STROKE_THRESHOLD: int = 500
|
|
35
|
+
"""
|
|
36
|
+
有效行程阈值 (像素)。
|
|
37
|
+
|
|
38
|
+
必须在单一方向上至少移动这么多像素,然后反转方向,才会被记为一次“晃动”。
|
|
39
|
+
设置得过小会导致微小抖动触发,设置得过大需要大幅度甩动鼠标。
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
REQUIRED_SHAKES: int = 6
|
|
43
|
+
"""
|
|
44
|
+
触发所需的连续晃动次数。
|
|
45
|
+
|
|
46
|
+
1次晃动定义为:满足行程阈值的一次方向改变。
|
|
47
|
+
比如:左 -> 右 (1), 右 -> 左 (2), 左 -> 右 (3), 右 -> 左 (4)。
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
TIMEOUT_RESET: float = 0.5
|
|
51
|
+
"""
|
|
52
|
+
计数重置时间 (秒)。
|
|
53
|
+
|
|
54
|
+
如果在这个时间内没有发生下一次有效的晃动,计数器归零。
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
COOLDOWN: float = 2.0
|
|
58
|
+
"""
|
|
59
|
+
防抖冷却时间 (秒)。
|
|
60
|
+
|
|
61
|
+
触发成功后,在该时间内不再重复触发。
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def start() -> None:
|
|
67
|
+
"""开启鼠标晃动检测。"""
|
|
68
|
+
with ShakeMouse._lock:
|
|
69
|
+
if ShakeMouse._enabled:
|
|
70
|
+
return
|
|
71
|
+
ShakeMouse._enabled = True
|
|
72
|
+
|
|
73
|
+
ShakeMouse._thread = threading.Thread(target=ShakeMouse._monitor_loop, daemon=True, name="ShakeMouseMonitor")
|
|
74
|
+
ShakeMouse._thread.start()
|
|
75
|
+
logger.info("ShakeMouse detection enabled.")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def stop() -> None:
|
|
80
|
+
"""关闭鼠标晃动检测。"""
|
|
81
|
+
with ShakeMouse._lock:
|
|
82
|
+
ShakeMouse._enabled = False
|
|
83
|
+
|
|
84
|
+
# 线程是 daemon 且 loop 检查 _enabled,不需要 join,让它自然退出即可
|
|
85
|
+
if ShakeMouse._thread and ShakeMouse._thread.is_alive():
|
|
86
|
+
ShakeMouse._thread = None
|
|
87
|
+
logger.info("ShakeMouse detection disabled.")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def add_callback(func: Callable[[], None]) -> None:
|
|
92
|
+
"""添加晃动触发时的回调函数。"""
|
|
93
|
+
ShakeMouse._callbacks.append(func)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@staticmethod
|
|
97
|
+
def clear_callbacks() -> None:
|
|
98
|
+
"""清除所有回调函数。"""
|
|
99
|
+
ShakeMouse._callbacks.clear()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@staticmethod
|
|
103
|
+
def _trigger() -> None:
|
|
104
|
+
"""内部触发逻辑。"""
|
|
105
|
+
logger.debug("Mouse shake detected!")
|
|
106
|
+
for cb in ShakeMouse._callbacks:
|
|
107
|
+
try:
|
|
108
|
+
cb()
|
|
109
|
+
except Exception:
|
|
110
|
+
logger.exception(f"Error in ShakeMouse callback {cb}")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def _monitor_loop() -> None:
|
|
115
|
+
"""检测线程主循环。"""
|
|
116
|
+
|
|
117
|
+
last_pos = get_pos()
|
|
118
|
+
|
|
119
|
+
# 算法状态变量
|
|
120
|
+
# 累计在当前方向上的移动距离
|
|
121
|
+
acc_x = 0
|
|
122
|
+
acc_y = 0
|
|
123
|
+
|
|
124
|
+
# 当前方向标志 (1: 正向, -1: 负向, 0: 静止)
|
|
125
|
+
dir_x = 0
|
|
126
|
+
dir_y = 0
|
|
127
|
+
|
|
128
|
+
# 有效晃动计数
|
|
129
|
+
shake_count = 0
|
|
130
|
+
|
|
131
|
+
# 上一次有效晃动发生的时间
|
|
132
|
+
last_shake_time = time.time()
|
|
133
|
+
|
|
134
|
+
# 上一次触发成功的时间 (用于冷却)
|
|
135
|
+
last_trigger_time = 0
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
while True:
|
|
139
|
+
# 1. 检查开关状态
|
|
140
|
+
if not ShakeMouse._enabled:
|
|
141
|
+
break
|
|
142
|
+
|
|
143
|
+
now = time.time()
|
|
144
|
+
|
|
145
|
+
# 2. 获取当前位置并计算增量
|
|
146
|
+
curr_pos = get_pos()
|
|
147
|
+
dx = curr_pos.x - last_pos.x
|
|
148
|
+
dy = curr_pos.y - last_pos.y
|
|
149
|
+
last_pos = curr_pos
|
|
150
|
+
|
|
151
|
+
# 3. 冷却期检查
|
|
152
|
+
if now - last_trigger_time < ShakeMouse.COOLDOWN:
|
|
153
|
+
time.sleep(ShakeMouse.SAMPLE_INTERVAL)
|
|
154
|
+
# 冷却期内重置累积状态,防止冷却一结束就立即触发
|
|
155
|
+
acc_x, acc_y, shake_count = 0, 0, 0
|
|
156
|
+
continue
|
|
157
|
+
|
|
158
|
+
# 4. 超时重置检查
|
|
159
|
+
# 如果用户晃动了一两下停下来了,一段时间后重置计数
|
|
160
|
+
if shake_count > 0 and (now - last_shake_time > ShakeMouse.TIMEOUT_RESET):
|
|
161
|
+
shake_count = 0
|
|
162
|
+
acc_x = 0
|
|
163
|
+
acc_y = 0
|
|
164
|
+
# logger.debug("Shake timeout reset")
|
|
165
|
+
|
|
166
|
+
# 5. 核心算法:X 轴检测
|
|
167
|
+
# 我们主要检测水平晃动 (X轴) 或 垂直晃动 (Y轴),取两者中显著的一个
|
|
168
|
+
|
|
169
|
+
# --- X 轴逻辑 ---
|
|
170
|
+
if dx != 0:
|
|
171
|
+
current_dir_x = 1 if dx > 0 else -1
|
|
172
|
+
|
|
173
|
+
if current_dir_x != dir_x:
|
|
174
|
+
# 方向发生改变
|
|
175
|
+
if abs(acc_x) > ShakeMouse.STROKE_THRESHOLD:
|
|
176
|
+
# 如果之前的累积行程足够长,记为一次有效晃动
|
|
177
|
+
shake_count += 1
|
|
178
|
+
last_shake_time = now
|
|
179
|
+
# logger.debug(f"Shake count (X): {shake_count}, Acc: {acc_x}")
|
|
180
|
+
|
|
181
|
+
# 重置累积,开始新的方向
|
|
182
|
+
acc_x = dx
|
|
183
|
+
dir_x = current_dir_x
|
|
184
|
+
else:
|
|
185
|
+
# 方向相同,累积行程
|
|
186
|
+
acc_x += dx
|
|
187
|
+
|
|
188
|
+
# --- Y 轴逻辑 (同理) ---
|
|
189
|
+
if dy != 0:
|
|
190
|
+
current_dir_y = 1 if dy > 0 else -1
|
|
191
|
+
|
|
192
|
+
if current_dir_y != dir_y:
|
|
193
|
+
if abs(acc_y) > ShakeMouse.STROKE_THRESHOLD:
|
|
194
|
+
shake_count += 1
|
|
195
|
+
last_shake_time = now
|
|
196
|
+
# logger.debug(f"Shake count (Y): {shake_count}, Acc: {acc_y}")
|
|
197
|
+
|
|
198
|
+
acc_y = dy
|
|
199
|
+
dir_y = current_dir_y
|
|
200
|
+
else:
|
|
201
|
+
acc_y += dy
|
|
202
|
+
|
|
203
|
+
# 6. 触发判定
|
|
204
|
+
if shake_count >= ShakeMouse.REQUIRED_SHAKES:
|
|
205
|
+
last_trigger_time = now
|
|
206
|
+
shake_count = 0
|
|
207
|
+
acc_x = 0
|
|
208
|
+
acc_y = 0
|
|
209
|
+
|
|
210
|
+
# 在独立线程执行触发,不阻塞检测循环太久 (或者回调本身应当快)
|
|
211
|
+
# 这里直接调用,因为 python GIL 限制,只要 callback 不 sleep 太久即可
|
|
212
|
+
ShakeMouse._trigger()
|
|
213
|
+
|
|
214
|
+
# 7. 循环等待
|
|
215
|
+
time.sleep(ShakeMouse.SAMPLE_INTERVAL)
|
|
216
|
+
|
|
217
|
+
if __name__ == "__main__":
|
|
218
|
+
sm = ShakeMouse()
|
|
219
|
+
def on_shake():
|
|
220
|
+
print("Mouse shaken!")
|
|
221
|
+
|
|
222
|
+
sm.add_callback(on_shake)
|
|
223
|
+
sm.start()
|
|
224
|
+
input("Press Enter to stop...\n")
|
kotonebot/primitives/__init__.py
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
from .visual import Image, Template
|
|
1
|
+
from .visual import Image, Template, ImageSlice
|
|
2
2
|
from .geometry import Point, Rect, Size, Vector2D, Vector3D, is_point, is_rect
|
|
3
3
|
from .geometry import RectTuple, PointTuple
|
|
4
4
|
|
|
5
5
|
__all__ = [
|
|
6
6
|
'Image',
|
|
7
7
|
'Template',
|
|
8
|
+
'ImageSlice',
|
|
9
|
+
|
|
8
10
|
'Point',
|
|
9
11
|
'Rect',
|
|
10
12
|
'Size',
|