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.
Files changed (71) hide show
  1. kotonebot/backend/context/context.py +1002 -1002
  2. kotonebot/backend/core.py +6 -49
  3. kotonebot/backend/image.py +36 -5
  4. kotonebot/backend/loop.py +222 -208
  5. kotonebot/backend/ocr.py +7 -1
  6. kotonebot/client/device.py +108 -243
  7. kotonebot/client/host/__init__.py +34 -3
  8. kotonebot/client/host/adb_common.py +7 -9
  9. kotonebot/client/host/custom.py +6 -2
  10. kotonebot/client/host/leidian_host.py +2 -7
  11. kotonebot/client/host/mumu12_host.py +2 -7
  12. kotonebot/client/host/protocol.py +4 -3
  13. kotonebot/client/implements/__init__.py +62 -11
  14. kotonebot/client/implements/adb.py +5 -1
  15. kotonebot/client/implements/nemu_ipc/__init__.py +4 -0
  16. kotonebot/client/implements/uiautomator2.py +6 -2
  17. kotonebot/client/implements/windows.py +7 -3
  18. kotonebot/client/registration.py +1 -1
  19. kotonebot/client/scaler.py +467 -0
  20. kotonebot/config/base_config.py +1 -1
  21. kotonebot/config/config.py +61 -0
  22. kotonebot/core/__init__.py +13 -0
  23. kotonebot/core/entities/base.py +182 -0
  24. kotonebot/core/entities/compound.py +75 -0
  25. kotonebot/core/entities/ocr.py +117 -0
  26. kotonebot/core/entities/template_match.py +198 -0
  27. kotonebot/devtools/__init__.py +42 -0
  28. kotonebot/devtools/cli/__init__.py +6 -0
  29. kotonebot/devtools/cli/main.py +53 -0
  30. kotonebot/devtools/project/project.py +41 -0
  31. kotonebot/devtools/project/scanner.py +202 -0
  32. kotonebot/devtools/project/schema.py +99 -0
  33. kotonebot/devtools/resgen/__init__.py +42 -0
  34. kotonebot/devtools/resgen/codegen.py +331 -0
  35. kotonebot/devtools/resgen/core.py +94 -0
  36. kotonebot/devtools/resgen/parsers.py +360 -0
  37. kotonebot/devtools/resgen/utils.py +158 -0
  38. kotonebot/devtools/resgen/validation.py +115 -0
  39. kotonebot/devtools/web/dist/assets/bootstrap-icons-BOrJxbIo.woff +0 -0
  40. kotonebot/devtools/web/dist/assets/bootstrap-icons-BtvjY1KL.woff2 +0 -0
  41. kotonebot/devtools/web/dist/assets/ext-language_tools-CD021WJ2.js +2577 -0
  42. kotonebot/devtools/web/dist/assets/index-B_m5f2LF.js +2836 -0
  43. kotonebot/devtools/web/dist/assets/index-BlEDyGGa.css +9 -0
  44. kotonebot/devtools/web/dist/assets/language-client-C9muzqaq.js +128 -0
  45. kotonebot/devtools/web/dist/assets/mode-python-CtHp76XS.js +476 -0
  46. kotonebot/devtools/web/dist/icons/symbol-class.svg +3 -0
  47. kotonebot/devtools/web/dist/icons/symbol-file.svg +3 -0
  48. kotonebot/devtools/web/dist/icons/symbol-method.svg +3 -0
  49. kotonebot/devtools/web/dist/index.html +25 -0
  50. kotonebot/devtools/web/server/__init__.py +0 -0
  51. kotonebot/devtools/web/server/rest_api.py +217 -0
  52. kotonebot/devtools/web/server/server.py +85 -0
  53. kotonebot/errors.py +7 -2
  54. kotonebot/interop/win/__init__.py +10 -1
  55. kotonebot/interop/win/_mouse.py +311 -0
  56. kotonebot/interop/win/shake_mouse.py +224 -0
  57. kotonebot/primitives/__init__.py +3 -1
  58. kotonebot/primitives/geometry.py +817 -40
  59. kotonebot/primitives/visual.py +81 -1
  60. kotonebot/ui/pushkit/image_host.py +2 -1
  61. kotonebot/ui/pushkit/wxpusher.py +2 -1
  62. {kotonebot-0.4.0.dist-info → kotonebot-0.6.0.dist-info}/METADATA +4 -1
  63. kotonebot-0.6.0.dist-info/RECORD +105 -0
  64. kotonebot-0.6.0.dist-info/entry_points.txt +2 -0
  65. kotonebot/client/implements/adb_raw.py +0 -159
  66. kotonebot-0.4.0.dist-info/RECORD +0 -70
  67. /kotonebot/{tools → devtools}/mirror.py +0 -0
  68. /kotonebot/{tools → devtools/project}/__init__.py +0 -0
  69. {kotonebot-0.4.0.dist-info → kotonebot-0.6.0.dist-info}/WHEEL +0 -0
  70. {kotonebot-0.4.0.dist-info → kotonebot-0.6.0.dist-info}/licenses/LICENSE +0 -0
  71. {kotonebot-0.4.0.dist-info → kotonebot-0.6.0.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,11 @@
1
1
  import logging
2
+ import warnings
3
+ from functools import cache
2
4
 
5
+ import cv2
3
6
  from cv2.typing import MatLike
4
7
 
5
- from .geometry import Size
8
+ from .geometry import Size, Rect
6
9
  from kotonebot.util import cv2_imread
7
10
 
8
11
  logger = logging.getLogger(__name__)
@@ -57,6 +60,83 @@ class Image:
57
60
  def size(self) -> Size:
58
61
  return Size(self.pixels.shape[1], self.pixels.shape[0])
59
62
 
63
+ # Compatibility with older API (deprecated)
64
+ def __compat_warn(self, name: str) -> None:
65
+ warnings.warn(
66
+ f'`Image.{name}` is deprecated — use `kotonebot.primitives.Image` API instead.',
67
+ DeprecationWarning,
68
+ stacklevel=3,
69
+ )
70
+
71
+ @property
72
+ def path(self) -> str | None:
73
+ """Deprecated alias for `file_path`."""
74
+ self.__compat_warn('path')
75
+ return self.file_path
76
+
77
+ @path.setter
78
+ def path(self, value: str | None) -> None:
79
+ self.__compat_warn('path')
80
+ self.file_path = value
81
+
82
+ @property
83
+ def data(self) -> MatLike:
84
+ """Deprecated alias for `pixels`."""
85
+ self.__compat_warn('data')
86
+ return self.pixels
87
+
88
+ @property
89
+ def data_with_alpha(self) -> MatLike:
90
+ """Deprecated: return image including alpha channel when available."""
91
+ self.__compat_warn('data_with_alpha')
92
+ # If current pixels already contain alpha, return them
93
+ try:
94
+ if self.__pixels is not None and getattr(self.__pixels, 'shape', None) and len(self.__pixels.shape) >= 3 and self.__pixels.shape[2] == 4:
95
+ return self.__pixels
96
+ except Exception:
97
+ pass
98
+ if not self.file_path:
99
+ raise ValueError('Either pixels or file_path must be provided.')
100
+ arr = cv2_imread(self.file_path, cv2.IMREAD_UNCHANGED)
101
+ return arr
102
+
103
+ @cache
104
+ def binary(self) -> 'Image':
105
+ """Deprecated: return a grayscale copy of the image."""
106
+ self.__compat_warn('binary')
107
+ gray = cv2.cvtColor(self.pixels, cv2.COLOR_BGR2GRAY)
108
+ return Image(pixels=gray, name=self.name)
109
+
110
+ def __repr__(self) -> str:
111
+ class_name = self.__class__.__name__
112
+ if self.file_path is None:
113
+ return f'<{class_name}: memory>'
114
+ else:
115
+ return f'<{class_name}: "{self.name or "untitled"}" at {self.file_path}>'
116
+
117
+
118
+ class ImageSlice(Image):
119
+ def __init__(
120
+ self,
121
+ pixels: MatLike | None = None,
122
+ file_path: str | None = None,
123
+ lazy_load: bool = False,
124
+ name: str | None = None,
125
+ description: str | None = None,
126
+ *,
127
+ slice_rect: Rect | None
128
+ ):
129
+ super().__init__(
130
+ pixels=pixels,
131
+ file_path=file_path,
132
+ lazy_load=lazy_load,
133
+ name=name,
134
+ description=description
135
+ )
136
+ self.slice_rect = slice_rect
137
+ """图像切片的矩形区域。"""
138
+
139
+
60
140
  class Template(Image):
61
141
  """
62
142
  模板图像类。
@@ -4,7 +4,6 @@ from pathlib import Path
4
4
  from typing import Sequence
5
5
 
6
6
  import cv2
7
- import requests
8
7
  import numpy as np
9
8
  from cv2.typing import MatLike
10
9
  from dotenv import load_dotenv
@@ -23,6 +22,8 @@ def _upload_single(image: MatLike | str) -> str:
23
22
 
24
23
  :param image: OpenCV MatLike 或本地图片文件路径
25
24
  """
25
+ import requests
26
+
26
27
  api_url = 'https://freeimage.host/api/1/upload'
27
28
  api_key = os.getenv('FREEIMAGEHOST_KEY')
28
29
 
@@ -1,7 +1,6 @@
1
1
  import os
2
2
  import json
3
3
  from typing import Sequence
4
- import requests
5
4
  from cv2.typing import MatLike
6
5
  from dotenv import dotenv_values
7
6
 
@@ -16,6 +15,8 @@ class Wxpusher(PushkitProtocol):
16
15
  self.uid = uid or config["WXPUSHER_UID"]
17
16
 
18
17
  def push(self, title: str, message: str, *, images: Sequence[str | MatLike] | None = None) -> None:
18
+ import requests
19
+
19
20
  summary = title
20
21
  content = message
21
22
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kotonebot
3
- Version: 0.4.0
3
+ Version: 0.6.0
4
4
  Summary: Kotonebot is game automation library based on computer vision technology, works for Windows and Android.
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -15,6 +15,7 @@ Requires-Dist: typing-extensions~=4.12
15
15
  Requires-Dist: python-dotenv~=1.0
16
16
  Requires-Dist: onnxruntime~=1.14
17
17
  Requires-Dist: numpy
18
+ Requires-Dist: mouse>=0.7.1
18
19
  Provides-Extra: android
19
20
  Requires-Dist: adbutils>=2.8; extra == "android"
20
21
  Requires-Dist: uiautomator2>=3.2; extra == "android"
@@ -32,6 +33,8 @@ Requires-Dist: psutil~=6.1; extra == "dev"
32
33
  Requires-Dist: twine~=6.1; extra == "dev"
33
34
  Requires-Dist: build; extra == "dev"
34
35
  Requires-Dist: snakeviz; extra == "dev"
36
+ Requires-Dist: tomli; python_version < "3.11" and extra == "dev"
37
+ Requires-Dist: dataclass-wizard; extra == "dev"
35
38
  Requires-Dist: jinja2~=3.1; extra == "dev"
36
39
  Requires-Dist: tqdm~=4.67; extra == "dev"
37
40
  Provides-Extra: all
@@ -0,0 +1,105 @@
1
+ kotonebot/__init__.py,sha256=DWS8zZAWH-MQZxk6bDm9uNAfY9UVmtd7Q928suujpiQ,636
2
+ kotonebot/errors.py,sha256=yllcOJxBdqcXadFLGcxIGzLEfoGkx8eaNK-R_SkFFro,2585
3
+ kotonebot/util.py,sha256=ftWhlnZ3qJ80x2wVCHYr2wW299JQkYDOREP1mA-n1vg,14370
4
+ kotonebot/backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ kotonebot/backend/bot.py,sha256=Ph0pfAubr_TV4MmqkyYL0P0hLbXdFEkzlQ0V2B1niZI,11870
6
+ kotonebot/backend/color.py,sha256=KqFISc6puMNbsyB5diu5PcqNjdkLwUeryVsMmeAJG4Q,20125
7
+ kotonebot/backend/core.py,sha256=RjJE586tNGkx8H_6Zo_gUrQsdHbexjKVUhMHdACBLX4,2454
8
+ kotonebot/backend/dispatch.py,sha256=t3eqkOVNNrdaeXSMLxDbReYhVRoAlSRNxb7oO2clRsQ,7423
9
+ kotonebot/backend/flow_controller.py,sha256=sbr6JiuYXErevY_BPrzw7hUCfxLGVrz0W_KNbXIxe9Q,6228
10
+ kotonebot/backend/image.py,sha256=GKXRwNR-fDwciNVluT6Da47BUn-KWZ3ELVdmM1qVI-s,29438
11
+ kotonebot/backend/loop.py,sha256=1Bz9YwK-2IrePKwRam6yg_inlUComK7H6luRFaBmVXk,7286
12
+ kotonebot/backend/ocr.py,sha256=8pkNBwLz0W3QseKMfr_BxDvJAPuRMfh3h4MbHzk3cBU,18379
13
+ kotonebot/backend/preprocessor.py,sha256=YmAbLa-XXES2AchMJtsBpPZwIIGHuYapwpXpw0YSpbA,3423
14
+ kotonebot/backend/context/__init__.py,sha256=0X9jzM0ftGQUgjoXCk98xf1inoCqHqaUwT0wcHn9P5s,168
15
+ kotonebot/backend/context/context.py,sha256=TDi9F8vaNYhCPCZv2A_Vz8guyyp1WRGmtjAufgfWI0I,34782
16
+ kotonebot/backend/context/task_action.py,sha256=LdxyeW-1ie71ludnAN36ltlcAw4AX3Lb9ua7I5xK4yY,6444
17
+ kotonebot/backend/debug/__init__.py,sha256=pcSpwzU2YwGrogOoHmsI035nkA_-kDLfm-lfBxHuQ-c,43
18
+ kotonebot/backend/debug/entry.py,sha256=_sIGi9_LegbaM2DCcDTvGJhrkyUqF6W2Al4nYmkb9rM,2833
19
+ kotonebot/backend/debug/mock.py,sha256=0fTiJeqVTanQv6L3TPbldgbJRBPmunZOjlzKtVicJ_M,2118
20
+ kotonebot/backend/debug/server.py,sha256=9PEpczIrwCnD4c_FAtiojwk27aKkkzTtRySCsdho4Rs,7517
21
+ kotonebot/backend/debug/vars.py,sha256=3wtbkH2WFNXyT2ZNu-8en3S0pAb40h-SefYyScRMLnM,10912
22
+ kotonebot/client/__init__.py,sha256=1eXyGopBFpYoucNTNkTo-7nIeDyasyETfHJ8DWuaNTo,192
23
+ kotonebot/client/device.py,sha256=Wiiy5djmxQk0nQXn69TOy73e6_ubo7idSb_0f4rJ0PI,14699
24
+ kotonebot/client/fast_screenshot.py,sha256=q69AX15VXRuB0U2qFJKfoTOBgG4nVBCUcaN1CX0VsUc,13647
25
+ kotonebot/client/protocol.py,sha256=x05llULFI3MddbvBX_c0sYWlSzCyWa3hped24ktq2Ko,2300
26
+ kotonebot/client/registration.py,sha256=e1mTvuoSqfCZtvZmkmL1GmjAGnfcH_biPDTTDRihuDA,837
27
+ kotonebot/client/scaler.py,sha256=9UHwLYNphIQFgAXnzsp6d6CJ_sq0Z_ZyZ1bsfZa-F6g,17491
28
+ kotonebot/client/host/__init__.py,sha256=qbjja1FoovIer4NMDcAQkDXxbOMSaV6z27VWb7iE5fk,1580
29
+ kotonebot/client/host/adb_common.py,sha256=xorKEiVEohFHdMU259fHZHhpbBI1-WVUMlDZCbYZ3yI,3628
30
+ kotonebot/client/host/custom.py,sha256=zYI4tZl5xGxdUTvF2li7c315252_hijdwMXwGMCXRpQ,4211
31
+ kotonebot/client/host/leidian_host.py,sha256=O8BUx61HDJs3lsdhoJcuYKEYImG9jWM6mqrOec0oSlc,7538
32
+ kotonebot/client/host/mumu12_host.py,sha256=CB-O7z3B-dWGVxiI78eK78hhyqileswa1DYXAftvjU8,14253
33
+ kotonebot/client/host/protocol.py,sha256=fnb9EqlTnSEfPlJ37HMYU6We4_YemX8mQFxcy2gsP8U,8173
34
+ kotonebot/client/host/windows_common.py,sha256=_Hf4CvHKNgiMIVY3ZPr3wF71r6nMErNi84Y_fswUjBc,2431
35
+ kotonebot/client/implements/__init__.py,sha256=OaSvmYTwxasdnYCS0kKrrcmmWPHjfMpYdmZ9siP03m8,2137
36
+ kotonebot/client/implements/adb.py,sha256=0Xs5o014SG9dNDIJYgI2_QyOMcZghuscolfK8eQnA40,3288
37
+ kotonebot/client/implements/remote_windows.py,sha256=SRq97cuXdUVvFIhsazZt4djMeGSLfYS88IUyzmlpaww,6756
38
+ kotonebot/client/implements/uiautomator2.py,sha256=Hal_DDWRCoLjL854xqDRq1X4Gm6O_SCBCUhqM2p1ykw,2722
39
+ kotonebot/client/implements/windows.py,sha256=bnASXFE8xHzY9Ue90r0ppYNnIVezhfMHAA0X9deXTNM,6889
40
+ kotonebot/client/implements/nemu_ipc/__init__.py,sha256=atIZjwnxhKEOndikoHDYhPrkvb7iC3zscBC457MQPz4,321
41
+ kotonebot/client/implements/nemu_ipc/external_renderer_ipc.py,sha256=YsfKf0-qorfAf2YvNuxpLb9af-HJFsu97bnXABshhbA,10643
42
+ kotonebot/client/implements/nemu_ipc/nemu_ipc.py,sha256=LhUUyfB28MDnRg8z2FyGah1hTeOMFiX7w8LZLAAjLF8,12082
43
+ kotonebot/config/__init__.py,sha256=-jATUOdrpUrBRT9RiTRQho2-2zeet50qQggsVMVpqNE,35
44
+ kotonebot/config/base_config.py,sha256=oJMoCEDUU15S7iO8WJg_qqL4YbXSr9qZqlYE82KJSl0,3427
45
+ kotonebot/config/config.py,sha256=0MeumpADN8PneyeqVGutByL-39jZdJioJWL8DM5xtdU,1951
46
+ kotonebot/config/manager.py,sha256=XBtriAU9eo-wv2iKOwyDqu8tzhbKqFCuy0jsAM9T9uU,1061
47
+ kotonebot/core/__init__.py,sha256=r2d-3TLGKvr3H8q3sfDZRtRaq0xPE9gUN4aHnqa7Ofs,410
48
+ kotonebot/core/entities/base.py,sha256=ROvY1LgrAdLISBnpEnJs-NafmZMeuuZcgevGsmn5glE,7116
49
+ kotonebot/core/entities/compound.py,sha256=cl6FJsMMhrvn_5KuLolkzWQKxqyIvfcPYIgkN2Fdeio,2928
50
+ kotonebot/core/entities/ocr.py,sha256=Z_N_sEFcuh0MZ0ZrvIaOxeF3V0_NqF02FDBmv9RqhcM,4633
51
+ kotonebot/core/entities/template_match.py,sha256=qBR-Qsd9rV32YMexZuyz7nhKyWtmeulka1CcDTccOJc,8348
52
+ kotonebot/devtools/__init__.py,sha256=d3S1dd3_ydmXatAeuBLBTfo0Z_w9I-48WzEolM9F8uk,702
53
+ kotonebot/devtools/mirror.py,sha256=jlGpeX_WkFpcZxojxDzT5QqVeIpd_UIDzSMR2K31VJ0,13820
54
+ kotonebot/devtools/cli/__init__.py,sha256=T9OOD1_m9ByRumCtBgtI_4whhY4SdrqzBMBaViugBKU,137
55
+ kotonebot/devtools/cli/main.py,sha256=EFLpCW5JZ-bSPUTUOIsLgZvvMO8lPmNUeB4Qoo2qRXs,1300
56
+ kotonebot/devtools/project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
+ kotonebot/devtools/project/project.py,sha256=cyj4bMf0SweijpYEHlRUkZWm7vabuQLn7W2qz8dTKUA,1314
58
+ kotonebot/devtools/project/scanner.py,sha256=IGQbalqlpCwkB_dRtb4_2C_LwjI2GR_Zfx2zqvse9mk,8043
59
+ kotonebot/devtools/project/schema.py,sha256=HPyCEmIbCXB0mTjoW3VvmAS1FMwWlj7IeoF1xipJ3-k,2723
60
+ kotonebot/devtools/resgen/__init__.py,sha256=apaE2yf1n4Cn9MTxLBZaamfB6M83f6LHRgkoUPsUvCo,692
61
+ kotonebot/devtools/resgen/codegen.py,sha256=HWzx2V2R9J6P6N1tfILO5GR9iXJG_ivJafEWjnA9Cs4,14063
62
+ kotonebot/devtools/resgen/core.py,sha256=mLGqwQ85ucOL2qKFTK29SJTCZrCplAEhSuP7GDMfoss,2607
63
+ kotonebot/devtools/resgen/parsers.py,sha256=cnmaCYZaHovj5_DPzcAbYme3Z87-K01AwQL9Fvc3Qts,15146
64
+ kotonebot/devtools/resgen/utils.py,sha256=13mLQ4ozQEt04GAuK5VFTxtGOk2FM5O7XhdEZ-BMdKU,5593
65
+ kotonebot/devtools/resgen/validation.py,sha256=7yen17JtNU_OoV40fD6xfSx5l054RRFpK4xoJ51lnDA,4284
66
+ kotonebot/devtools/web/dist/index.html,sha256=88mzflP34NfrgFD1lC8n7xP0lkOCTcuXeilQqXiC1EY,736
67
+ kotonebot/devtools/web/dist/assets/bootstrap-icons-BOrJxbIo.woff,sha256=ux3pibg5cPb05U3hzZdMXLpVtzWC2l4bIlptDt8ClIM,176032
68
+ kotonebot/devtools/web/dist/assets/bootstrap-icons-BtvjY1KL.woff2,sha256=R2rfQrQDJQmPz6izarPnaRhrtPbOaiSXU-LhqcIr-Z4,130396
69
+ kotonebot/devtools/web/dist/assets/ext-language_tools-CD021WJ2.js,sha256=reG0R93ZT7Ig_m_7w2fprBrBWW8A4fnj5Ebj9cQ2FIM,107959
70
+ kotonebot/devtools/web/dist/assets/index-B_m5f2LF.js,sha256=xwiem8jQxMujvdhBXKpIOkpPcCjj1l5GcRZjW4c3l78,1380535
71
+ kotonebot/devtools/web/dist/assets/index-BlEDyGGa.css,sha256=LWUTFbG5yb-_Cu5jkz6mgCj2SKCvdQKq0ndbokf1bBU,311806
72
+ kotonebot/devtools/web/dist/assets/language-client-C9muzqaq.js,sha256=X3Kb1EYQOIzMkdxGGSaYt4CBDFZwpBpWshFj8xNu8bI,265765
73
+ kotonebot/devtools/web/dist/assets/mode-python-CtHp76XS.js,sha256=t_YFLderRSd06Q7-1sBcsPPkZN3Cty7BgblBEkV7mcw,16876
74
+ kotonebot/devtools/web/dist/icons/symbol-class.svg,sha256=7FmIdU7IU34qYW521_wX0TligscsmiCttLZdr064JrE,737
75
+ kotonebot/devtools/web/dist/icons/symbol-file.svg,sha256=yOROcG5HGkXDopwQZUjoDH6NGlSBy_kg6gossqkTrvk,331
76
+ kotonebot/devtools/web/dist/icons/symbol-method.svg,sha256=KGS0YF4X4jyzU9PB0NHe6txcEoTZlHMa72kI8dDkDjc,482
77
+ kotonebot/devtools/web/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
+ kotonebot/devtools/web/server/rest_api.py,sha256=Kk_bc3T3Qz_8okxOWd4url3ImXJh-V5x9t3vir3muKA,8249
79
+ kotonebot/devtools/web/server/server.py,sha256=e4M99fDAR3lWGG7k3FBzWDye2O8yTDjYt6A-tXug5wQ,2694
80
+ kotonebot/interop/win/__init__.py,sha256=20_Oxkv9h6tqfw59CFLaCMCbRL-k-82kv-tY6KtivxQ,234
81
+ kotonebot/interop/win/_mouse.py,sha256=SdkgVyxRnZQbR47yQWqmWzSQw3lcG9eNN1hITgiSu2c,10441
82
+ kotonebot/interop/win/message_box.py,sha256=R06GSu936Bx_Wg7ddn6LOvazD9_Gt3mhz4_oUuIoYO0,8635
83
+ kotonebot/interop/win/reg.py,sha256=xw35d1xl8ucITT4bOMFgHmMkAUhak7x3lzegR3g3S48,1347
84
+ kotonebot/interop/win/shake_mouse.py,sha256=SdJ94NyScAgF_IDr7E4DPDE1DVpo_vyFLaAf0SQCgG0,7266
85
+ kotonebot/interop/win/shortcut.py,sha256=f1u6IWvpw6Kxt014wnHz5Z94rVK1qf4kLtRsI9bJYnk,1764
86
+ kotonebot/interop/win/task_dialog.py,sha256=Ezi1CsjFSbnKccvYfIRaJoCGHUsJnnIpHb0gtKR603E,20191
87
+ kotonebot/logging/__init__.py,sha256=r0q4z59yYy_bQnHTwJYsiPGwOGIgEOrcXH1mNs1h_N0,142
88
+ kotonebot/logging/log.py,sha256=PLb6r_hlW1mvqU_kx6_89zaQ1IpamgHWG3sQ0ZnlrCI,555
89
+ kotonebot/primitives/__init__.py,sha256=Gsuo5NSTo81aDi3HDkAj2gFqSTWtY-k2PSJ4fc8w5FA,392
90
+ kotonebot/primitives/geometry.py,sha256=B5uabs6xDVy0vYuTJDphLb1X5913BAzlPZpitKeLRFA,34199
91
+ kotonebot/primitives/visual.py,sha256=K2-kJ36D6jtxhpQps6c0hNPjIt7P5HdVEhgAsjqk78Q,4653
92
+ kotonebot/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
+ kotonebot/ui/user.py,sha256=aP6Va3iHaVA0-AHViNa04a3HDpLLiovZjV5_AMGY4cI,4485
94
+ kotonebot/ui/file_host/sensio.py,sha256=QyH8DpyO3sf2Hz4NuOW9oDt1V3G06XMO7yKfgLwq1oE,914
95
+ kotonebot/ui/file_host/tmp_send.py,sha256=SwzTUGIjZXaP1_H86qahuLwUd8VeHGWW86XKaC__WKI,1802
96
+ kotonebot/ui/pushkit/__init__.py,sha256=xDUctRUL3euvge-yl8IhFYMlxIxQXsjxcyGN5tUwPtE,73
97
+ kotonebot/ui/pushkit/image_host.py,sha256=4ZEptuowUJJ-b9WwXaEfbVes2b-aJBK-JDI4bP8A9VM,2591
98
+ kotonebot/ui/pushkit/protocol.py,sha256=KVZ-xr0sMdiuri7AiYqugpZRRtefBsosXm6zouScUR4,266
99
+ kotonebot/ui/pushkit/wxpusher.py,sha256=Px-7pUvUMsgJLKzJXRJBVv8rPZMnELTYrTBhhllR8tM,1750
100
+ kotonebot-0.6.0.dist-info/licenses/LICENSE,sha256=gcuuhKKc5-dwvyvHsXjlC9oM6N5gZ6umYbC8ewW1Yvg,35821
101
+ kotonebot-0.6.0.dist-info/METADATA,sha256=03B-8qSqwe0SbRoxdRtRYrQjQxtjz3PwRxNFPSz0UOU,3351
102
+ kotonebot-0.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
103
+ kotonebot-0.6.0.dist-info/entry_points.txt,sha256=0TRRHk88fKrlrGGuxTMfa80P618wK3kLspjTrRAVoy0,53
104
+ kotonebot-0.6.0.dist-info/top_level.txt,sha256=QUWAZdbBndoojkrs6RcNytLAn7a0ns4YNF4tLx2Nc4s,10
105
+ kotonebot-0.6.0.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ kbot = kotonebot.devtools.cli:main
@@ -1,159 +0,0 @@
1
- import os
2
- import time
3
- import subprocess
4
- import struct
5
- from threading import Thread, Lock
6
- from functools import cached_property
7
- from typing_extensions import override
8
-
9
- import cv2
10
- import numpy as np
11
- from cv2.typing import MatLike
12
- from adbutils._utils import adb_path
13
-
14
- from .adb import AdbImpl
15
- from adbutils._device import AdbDevice as AdbUtilsDevice
16
- from kotonebot import logging
17
-
18
- logger = logging.getLogger(__name__)
19
-
20
- WAIT_TIMEOUT = 10
21
- MAX_RETRY_COUNT = 5
22
- SCRIPT: str = """#!/bin/sh
23
- while true; do
24
- screencap
25
- sleep 0.3
26
- done
27
- """
28
-
29
- class AdbRawImpl(AdbImpl):
30
- def __init__(self, adb_connection: AdbUtilsDevice):
31
- super().__init__(adb_connection)
32
- self.__worker: Thread | None = None
33
- self.__process: subprocess.Popen | None = None
34
- self.__data: MatLike | None = None
35
- self.__retry_count = 0
36
- self.__lock = Lock()
37
- self.__stopping = False
38
-
39
- def __cleanup_worker(self) -> None:
40
- if self.__process:
41
- try:
42
- self.__process.kill()
43
- except:
44
- pass
45
- self.__process = None
46
- if self.__worker:
47
- try:
48
- self.__worker.join()
49
- except:
50
- pass
51
- self.__worker = None
52
- self.__data = None
53
-
54
- def __start_worker(self) -> None:
55
- self.__stopping = True
56
- self.__cleanup_worker()
57
- self.__stopping = False
58
- self.__worker = Thread(target=self.__worker_thread_with_retry, daemon=True)
59
- self.__worker.start()
60
-
61
- def __worker_thread_with_retry(self) -> None:
62
- try:
63
- self.__worker_thread()
64
- except Exception as e:
65
- logger.error(f"Worker thread failed: {e}")
66
- with self.__lock:
67
- self.__retry_count += 1
68
- raise
69
-
70
- def __worker_thread(self) -> None:
71
- with open('screenshot.sh', 'w', encoding='utf-8', newline='\n') as f:
72
- f.write(SCRIPT)
73
- self.adb.push('screenshot.sh', '/data/local/tmp/screenshot.sh')
74
- self.adb.shell(f'chmod 755 /data/local/tmp/screenshot.sh')
75
- os.remove('screenshot.sh')
76
-
77
- cmd = fr'{adb_path()} -s {self.adb.serial} exec-out "sh /data/local/tmp/screenshot.sh"'
78
- self.__process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
79
-
80
- while not self.__stopping and self.__process.poll() is None:
81
- if self.__process.stdout is None:
82
- logger.error("Failed to get stdout from process")
83
- continue
84
-
85
- # 解析 header
86
- # https://stackoverflow.com/questions/22034959/what-format-does-adb-screencap-sdcard-screenshot-raw-produce-without-p-f
87
- if self.__api_level >= 26:
88
- metadata = self.__process.stdout.read(16)
89
- w, h, p, c = struct.unpack('<IIII', metadata)
90
- # w=width, h=height, p=pixel_format, c=color_space
91
- # 详见:https://android.googlesource.com/platform/frameworks/base/+/26a2b97dbe48ee45e9ae70110714048f2f360f97/cmds/screencap/screencap.cpp#209
92
- else:
93
- metadata = self.__process.stdout.read(12)
94
- w, h, p = struct.unpack('<III', metadata)
95
- if p == 1: # PixelFormat.RGBA_8888
96
- channel = 4
97
- else:
98
- raise ValueError(f"Unsupported pixel format: {p}")
99
- data_size = w * h * channel
100
-
101
- if (data_size < 100 * 100 * 4) or (data_size > 3000 * 3000 * 4):
102
- raise ValueError(f"Invaild data_size: {w}x{h}.")
103
-
104
- # 读取图像数据
105
- # logger.verbose(f"receiving image data: {w}x{h} {data_size} bytes")
106
- image_data = self.__process.stdout.read(data_size)
107
- if not isinstance(image_data, bytes) or len(image_data) != data_size:
108
- logger.error(f"Failed to read image data, expected {data_size} bytes but got {len(image_data) if isinstance(image_data, bytes) else 'non-bytes'}")
109
- raise RuntimeError("Failed to read image data")
110
-
111
- np_data = np.frombuffer(image_data, np.uint8)
112
- np_data = np_data.reshape(h, w, channel)
113
- self.__data = cv2.cvtColor(np_data, cv2.COLOR_RGBA2BGR)
114
-
115
- @cached_property
116
- def __api_level(self) -> int:
117
- try:
118
- output = self.adb.shell("getprop ro.build.version.sdk")
119
- assert isinstance(output, str)
120
- return int(output.strip())
121
- except Exception as e:
122
- logger.error(f"Failed to get API level: {e}")
123
- return 0
124
-
125
- @override
126
- def screenshot(self) -> MatLike:
127
- with self.__lock:
128
- if self.__retry_count >= MAX_RETRY_COUNT:
129
- raise RuntimeError(f"Maximum retry count ({MAX_RETRY_COUNT}) exceeded")
130
-
131
- if not self.__worker or (self.__worker and not self.__worker.is_alive()):
132
- self.__start_worker()
133
-
134
- start_time = time.time()
135
- while self.__data is None:
136
- time.sleep(0.01)
137
- if time.time() - start_time > WAIT_TIMEOUT:
138
- logger.warning("Screenshot timeout, cleaning up and restarting worker...")
139
- with self.__lock:
140
- if self.__retry_count < MAX_RETRY_COUNT:
141
- self.__start_worker()
142
- start_time = time.time() # 重置超时计时器
143
- continue
144
- else:
145
- raise RuntimeError(f"Maximum retry count ({MAX_RETRY_COUNT}) exceeded")
146
-
147
- # 检查 worker 是否还活着
148
- if self.__worker and not self.__worker.is_alive():
149
- with self.__lock:
150
- if self.__retry_count < MAX_RETRY_COUNT:
151
- logger.warning("Worker thread died, restarting...")
152
- self.__start_worker()
153
- else:
154
- raise RuntimeError(f"Maximum retry count ({MAX_RETRY_COUNT}) exceeded")
155
-
156
- logger.verbose(f"adb raw screenshot wait time: {time.time() - start_time:.4f}s")
157
- data = self.__data
158
- self.__data = None
159
- return data
@@ -1,70 +0,0 @@
1
- kotonebot/__init__.py,sha256=DWS8zZAWH-MQZxk6bDm9uNAfY9UVmtd7Q928suujpiQ,636
2
- kotonebot/errors.py,sha256=1mZLFhNNxtRkm66y80rPzOeFjCgR0TgxgCDGo9ByZek,2294
3
- kotonebot/util.py,sha256=ftWhlnZ3qJ80x2wVCHYr2wW299JQkYDOREP1mA-n1vg,14370
4
- kotonebot/backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- kotonebot/backend/bot.py,sha256=Ph0pfAubr_TV4MmqkyYL0P0hLbXdFEkzlQ0V2B1niZI,11870
6
- kotonebot/backend/color.py,sha256=KqFISc6puMNbsyB5diu5PcqNjdkLwUeryVsMmeAJG4Q,20125
7
- kotonebot/backend/core.py,sha256=A0f2i1KO9PXn4ndRDOBbADtB_beVxD1P0g4FYioxUY0,4026
8
- kotonebot/backend/dispatch.py,sha256=t3eqkOVNNrdaeXSMLxDbReYhVRoAlSRNxb7oO2clRsQ,7423
9
- kotonebot/backend/flow_controller.py,sha256=sbr6JiuYXErevY_BPrzw7hUCfxLGVrz0W_KNbXIxe9Q,6228
10
- kotonebot/backend/image.py,sha256=jXYGVXa9K1zBCJBG1Btsf2AG5XI9A0DMy5uYy6KOusk,28111
11
- kotonebot/backend/loop.py,sha256=e4STYQ6sbUp53xcYJXyhz4vOyEzXds1MKR0xebqU228,6638
12
- kotonebot/backend/ocr.py,sha256=IE_LUCdD4i00a02xgLkSRPcYYo3R2MBfZKKQHjOFfgM,18058
13
- kotonebot/backend/preprocessor.py,sha256=YmAbLa-XXES2AchMJtsBpPZwIIGHuYapwpXpw0YSpbA,3423
14
- kotonebot/backend/context/__init__.py,sha256=0X9jzM0ftGQUgjoXCk98xf1inoCqHqaUwT0wcHn9P5s,168
15
- kotonebot/backend/context/context.py,sha256=R91knq7Ry5MSWaFhOj3HIF13u8bomOOsGk59yPOG4aw,33780
16
- kotonebot/backend/context/task_action.py,sha256=LdxyeW-1ie71ludnAN36ltlcAw4AX3Lb9ua7I5xK4yY,6444
17
- kotonebot/backend/debug/__init__.py,sha256=pcSpwzU2YwGrogOoHmsI035nkA_-kDLfm-lfBxHuQ-c,43
18
- kotonebot/backend/debug/entry.py,sha256=_sIGi9_LegbaM2DCcDTvGJhrkyUqF6W2Al4nYmkb9rM,2833
19
- kotonebot/backend/debug/mock.py,sha256=0fTiJeqVTanQv6L3TPbldgbJRBPmunZOjlzKtVicJ_M,2118
20
- kotonebot/backend/debug/server.py,sha256=9PEpczIrwCnD4c_FAtiojwk27aKkkzTtRySCsdho4Rs,7517
21
- kotonebot/backend/debug/vars.py,sha256=3wtbkH2WFNXyT2ZNu-8en3S0pAb40h-SefYyScRMLnM,10912
22
- kotonebot/client/__init__.py,sha256=1eXyGopBFpYoucNTNkTo-7nIeDyasyETfHJ8DWuaNTo,192
23
- kotonebot/client/device.py,sha256=bb49Gbo-zcF1SaeBbRljxkuKGMp5e9umPiguAbV1kok,19405
24
- kotonebot/client/fast_screenshot.py,sha256=q69AX15VXRuB0U2qFJKfoTOBgG4nVBCUcaN1CX0VsUc,13647
25
- kotonebot/client/protocol.py,sha256=x05llULFI3MddbvBX_c0sYWlSzCyWa3hped24ktq2Ko,2300
26
- kotonebot/client/registration.py,sha256=XK424QEWbJKfNdkiDoIJUh_JIy8ryk4I4I3hrWUXCX8,848
27
- kotonebot/client/host/__init__.py,sha256=WsDNAugbr4k1fMCHBdaYZmrifrq6WmSQByp1qOFbV_I,579
28
- kotonebot/client/host/adb_common.py,sha256=_S2LKpsNnRUREUpzO71OfMIB8Dt0pmxKdSbcmpqss-4,3750
29
- kotonebot/client/host/custom.py,sha256=_r2GAtJ20CoEx8JO7wVa4jr6KT8YgO5wzrFHLFtOgZE,4079
30
- kotonebot/client/host/leidian_host.py,sha256=H1yNhKRBdjzyY8_UjqohuJwjj-gVvN1s3dnQfKgFdW8,7729
31
- kotonebot/client/host/mumu12_host.py,sha256=cWlbZiV0HdN2S42L1Nd5Jcv3bw-b1oxjX4_3y5JE-tI,14444
32
- kotonebot/client/host/protocol.py,sha256=x2TbnDELDQpqxSKWUHLT3Pez8Qx6IzL4wyUfgf9mFyk,8051
33
- kotonebot/client/host/windows_common.py,sha256=_Hf4CvHKNgiMIVY3ZPr3wF71r6nMErNi84Y_fswUjBc,2431
34
- kotonebot/client/implements/__init__.py,sha256=_qZNM-2wEr4Q-_2kWisyuc4AUoqpRB_gAC_hr8_RkSY,511
35
- kotonebot/client/implements/adb.py,sha256=OcATiRGj84KMM9mEuQLzLoNyDBaZRJugR-BFrlWjB-c,3145
36
- kotonebot/client/implements/adb_raw.py,sha256=zUTI9QscmdN031tbBbInUN8G-LrgQv-cTc_Yl0zaZQU,6307
37
- kotonebot/client/implements/remote_windows.py,sha256=SRq97cuXdUVvFIhsazZt4djMeGSLfYS88IUyzmlpaww,6756
38
- kotonebot/client/implements/uiautomator2.py,sha256=ER4cNLI_cCpIGKWIXeuaUPmVtz50JuFO5Kx-ZwCGI1s,2575
39
- kotonebot/client/implements/windows.py,sha256=-P5nFiUbCxz9qMM-WZPtw-Q2xYc3Tmym5MNK3gBwoB4,6738
40
- kotonebot/client/implements/nemu_ipc/__init__.py,sha256=vSZzv75bn38Wch86PYs5UDOCLwxxoDGm2v1jrwff_S8,200
41
- kotonebot/client/implements/nemu_ipc/external_renderer_ipc.py,sha256=YsfKf0-qorfAf2YvNuxpLb9af-HJFsu97bnXABshhbA,10643
42
- kotonebot/client/implements/nemu_ipc/nemu_ipc.py,sha256=LhUUyfB28MDnRg8z2FyGah1hTeOMFiX7w8LZLAAjLF8,12082
43
- kotonebot/config/__init__.py,sha256=-jATUOdrpUrBRT9RiTRQho2-2zeet50qQggsVMVpqNE,35
44
- kotonebot/config/base_config.py,sha256=NpJuUmzgjUHelzm55bbVvk9x9MdwuKwfxwpaJFp-9iw,3438
45
- kotonebot/config/manager.py,sha256=XBtriAU9eo-wv2iKOwyDqu8tzhbKqFCuy0jsAM9T9uU,1061
46
- kotonebot/interop/win/__init__.py,sha256=jH8E0iqT1lOs4C_i5yl4bJ7z7iZh2ieGOF1lu8raJ30,111
47
- kotonebot/interop/win/message_box.py,sha256=R06GSu936Bx_Wg7ddn6LOvazD9_Gt3mhz4_oUuIoYO0,8635
48
- kotonebot/interop/win/reg.py,sha256=xw35d1xl8ucITT4bOMFgHmMkAUhak7x3lzegR3g3S48,1347
49
- kotonebot/interop/win/shortcut.py,sha256=f1u6IWvpw6Kxt014wnHz5Z94rVK1qf4kLtRsI9bJYnk,1764
50
- kotonebot/interop/win/task_dialog.py,sha256=Ezi1CsjFSbnKccvYfIRaJoCGHUsJnnIpHb0gtKR603E,20191
51
- kotonebot/logging/__init__.py,sha256=r0q4z59yYy_bQnHTwJYsiPGwOGIgEOrcXH1mNs1h_N0,142
52
- kotonebot/logging/log.py,sha256=PLb6r_hlW1mvqU_kx6_89zaQ1IpamgHWG3sQ0ZnlrCI,555
53
- kotonebot/primitives/__init__.py,sha256=R11AoTQQU6ql86oaVjZJkH2-kkxf5kr9xY8gnqPjLgM,355
54
- kotonebot/primitives/geometry.py,sha256=L2-mqhfSXSNyt9Qvjo5T27WZc9cso2v9oWeRVnz6-1k,7973
55
- kotonebot/primitives/visual.py,sha256=fZbNgaM03wCaR1Rb0QgeAQIfNsBBXPoUp2-E2IYFsLA,2028
56
- kotonebot/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
- kotonebot/tools/mirror.py,sha256=jlGpeX_WkFpcZxojxDzT5QqVeIpd_UIDzSMR2K31VJ0,13820
58
- kotonebot/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
- kotonebot/ui/user.py,sha256=aP6Va3iHaVA0-AHViNa04a3HDpLLiovZjV5_AMGY4cI,4485
60
- kotonebot/ui/file_host/sensio.py,sha256=QyH8DpyO3sf2Hz4NuOW9oDt1V3G06XMO7yKfgLwq1oE,914
61
- kotonebot/ui/file_host/tmp_send.py,sha256=SwzTUGIjZXaP1_H86qahuLwUd8VeHGWW86XKaC__WKI,1802
62
- kotonebot/ui/pushkit/__init__.py,sha256=xDUctRUL3euvge-yl8IhFYMlxIxQXsjxcyGN5tUwPtE,73
63
- kotonebot/ui/pushkit/image_host.py,sha256=nB6BCOA5ZgSGi-ntgqQp49H1UZDk8qC41O_PTLPzZ-E,2581
64
- kotonebot/ui/pushkit/protocol.py,sha256=KVZ-xr0sMdiuri7AiYqugpZRRtefBsosXm6zouScUR4,266
65
- kotonebot/ui/pushkit/wxpusher.py,sha256=U7WKxyf9pVgGvppmBxwMRuBuFkQG3NC3tkdRh7_-IOw,1732
66
- kotonebot-0.4.0.dist-info/licenses/LICENSE,sha256=gcuuhKKc5-dwvyvHsXjlC9oM6N5gZ6umYbC8ewW1Yvg,35821
67
- kotonebot-0.4.0.dist-info/METADATA,sha256=4P-5GBc-v3VEtDlQqx0F3pUJXC7D9p2b0cmPbVZ91NE,3207
68
- kotonebot-0.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
69
- kotonebot-0.4.0.dist-info/top_level.txt,sha256=QUWAZdbBndoojkrs6RcNytLAn7a0ns4YNF4tLx2Nc4s,10
70
- kotonebot-0.4.0.dist-info/RECORD,,
File without changes
File without changes