kotonebot 0.3.1__py3-none-any.whl → 0.5.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 -302
- kotonebot/backend/color.py +525 -525
- kotonebot/backend/context/__init__.py +3 -3
- kotonebot/backend/context/context.py +49 -56
- kotonebot/backend/context/task_action.py +183 -175
- kotonebot/backend/core.py +129 -126
- 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/loop.py +12 -88
- kotonebot/backend/ocr.py +535 -529
- kotonebot/backend/preprocessor.py +103 -103
- kotonebot/client/__init__.py +9 -9
- kotonebot/client/device.py +528 -502
- kotonebot/client/fast_screenshot.py +377 -377
- kotonebot/client/host/__init__.py +43 -12
- kotonebot/client/host/adb_common.py +107 -94
- kotonebot/client/host/custom.py +118 -114
- kotonebot/client/host/leidian_host.py +196 -201
- kotonebot/client/host/mumu12_host.py +353 -358
- kotonebot/client/host/protocol.py +214 -213
- kotonebot/client/host/windows_common.py +58 -55
- kotonebot/client/implements/__init__.py +71 -7
- kotonebot/client/implements/adb.py +89 -85
- kotonebot/client/implements/adb_raw.py +162 -158
- kotonebot/client/implements/nemu_ipc/__init__.py +11 -7
- 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 -192
- kotonebot/client/implements/uiautomator2.py +85 -81
- kotonebot/client/implements/windows.py +176 -168
- kotonebot/client/protocol.py +69 -69
- kotonebot/client/registration.py +24 -24
- kotonebot/config/base_config.py +96 -96
- kotonebot/config/manager.py +36 -36
- kotonebot/errors.py +76 -71
- kotonebot/interop/win/__init__.py +10 -0
- kotonebot/interop/win/_mouse.py +311 -0
- kotonebot/interop/win/message_box.py +313 -313
- kotonebot/interop/win/reg.py +37 -37
- kotonebot/interop/win/shortcut.py +43 -43
- kotonebot/interop/win/task_dialog.py +513 -469
- kotonebot/logging/__init__.py +2 -2
- kotonebot/logging/log.py +17 -17
- kotonebot/primitives/__init__.py +17 -17
- kotonebot/primitives/geometry.py +862 -290
- kotonebot/primitives/visual.py +63 -63
- kotonebot/tools/mirror.py +354 -354
- 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 -87
- kotonebot/ui/pushkit/protocol.py +13 -13
- kotonebot/ui/pushkit/wxpusher.py +54 -53
- kotonebot/ui/user.py +148 -143
- kotonebot/util.py +436 -409
- {kotonebot-0.3.1.dist-info → kotonebot-0.5.0.dist-info}/METADATA +82 -76
- kotonebot-0.5.0.dist-info/RECORD +71 -0
- {kotonebot-0.3.1.dist-info → kotonebot-0.5.0.dist-info}/licenses/LICENSE +673 -673
- kotonebot-0.3.1.dist-info/RECORD +0 -70
- {kotonebot-0.3.1.dist-info → kotonebot-0.5.0.dist-info}/WHEEL +0 -0
- {kotonebot-0.3.1.dist-info → kotonebot-0.5.0.dist-info}/top_level.txt +0 -0
kotonebot/ui/file_host/sensio.py
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import requests
|
|
2
|
-
import os
|
|
3
|
-
|
|
4
|
-
def upload(file_path: str) -> str:
|
|
5
|
-
"""
|
|
6
|
-
上传文件到 paste.sensio.no
|
|
7
|
-
|
|
8
|
-
Args:
|
|
9
|
-
file_path: 要上传的文件路径
|
|
10
|
-
|
|
11
|
-
Returns:
|
|
12
|
-
str: 上传后的 URL
|
|
13
|
-
"""
|
|
14
|
-
url = 'https://paste.sensio.no/'
|
|
15
|
-
headers = {
|
|
16
|
-
'accept': 'text/plain',
|
|
17
|
-
'User-Agent': 'KAA',
|
|
18
|
-
'x-uuid': ''
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
files = {
|
|
22
|
-
'file': (os.path.basename(file_path), open(file_path, 'rb'))
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
response = requests.post(url, files=files, headers=headers, allow_redirects=False)
|
|
26
|
-
|
|
27
|
-
if response.status_code != 200:
|
|
28
|
-
raise Exception(f"Upload failed with status code {response.status_code}")
|
|
29
|
-
|
|
30
|
-
return response.text.strip()
|
|
31
|
-
|
|
32
|
-
if __name__ == "__main__":
|
|
33
|
-
test_file = "version"
|
|
34
|
-
if os.path.exists(test_file):
|
|
35
|
-
result = upload(test_file)
|
|
36
|
-
print(f"Upload result: {result}")
|
|
1
|
+
import requests
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
def upload(file_path: str) -> str:
|
|
5
|
+
"""
|
|
6
|
+
上传文件到 paste.sensio.no
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
file_path: 要上传的文件路径
|
|
10
|
+
|
|
11
|
+
Returns:
|
|
12
|
+
str: 上传后的 URL
|
|
13
|
+
"""
|
|
14
|
+
url = 'https://paste.sensio.no/'
|
|
15
|
+
headers = {
|
|
16
|
+
'accept': 'text/plain',
|
|
17
|
+
'User-Agent': 'KAA',
|
|
18
|
+
'x-uuid': ''
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
files = {
|
|
22
|
+
'file': (os.path.basename(file_path), open(file_path, 'rb'))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
response = requests.post(url, files=files, headers=headers, allow_redirects=False)
|
|
26
|
+
|
|
27
|
+
if response.status_code != 200:
|
|
28
|
+
raise Exception(f"Upload failed with status code {response.status_code}")
|
|
29
|
+
|
|
30
|
+
return response.text.strip()
|
|
31
|
+
|
|
32
|
+
if __name__ == "__main__":
|
|
33
|
+
test_file = "version"
|
|
34
|
+
if os.path.exists(test_file):
|
|
35
|
+
result = upload(test_file)
|
|
36
|
+
print(f"Upload result: {result}")
|
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
import requests
|
|
2
|
-
import os
|
|
3
|
-
|
|
4
|
-
def upload(file_path: str) -> str:
|
|
5
|
-
url = 'https://tmpsend.com/upload'
|
|
6
|
-
headers = {
|
|
7
|
-
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
|
|
8
|
-
'Referer': 'https://tmpsend.com/',
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
file_name = os.path.basename(file_path)
|
|
12
|
-
file_size = os.path.getsize(file_path)
|
|
13
|
-
|
|
14
|
-
# 第一次请求:添加文件信息
|
|
15
|
-
files = {
|
|
16
|
-
'action': (None, 'add'),
|
|
17
|
-
'name': (None, file_name),
|
|
18
|
-
'size': (None, str(file_size)),
|
|
19
|
-
'file': (file_name, open(file_path, 'rb'))
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
response = requests.post(url, headers=headers, files=files)
|
|
23
|
-
if response.status_code != 200:
|
|
24
|
-
raise Exception(f"Upload failed with status code {response.status_code}")
|
|
25
|
-
|
|
26
|
-
result = response.json()
|
|
27
|
-
if result.get('hasError'):
|
|
28
|
-
raise Exception(result.get('error'))
|
|
29
|
-
|
|
30
|
-
file_id = result.get('id')
|
|
31
|
-
if not file_id:
|
|
32
|
-
raise Exception("Failed to get file ID")
|
|
33
|
-
|
|
34
|
-
# 第二次请求:上传实际文件
|
|
35
|
-
upload_files = {
|
|
36
|
-
'action': (None, 'upload'),
|
|
37
|
-
'id': (None, file_id),
|
|
38
|
-
'name': (None, file_name),
|
|
39
|
-
'size': (None, str(file_size)),
|
|
40
|
-
'start': (None, '0'),
|
|
41
|
-
'end': (None, str(file_size)),
|
|
42
|
-
'data': (file_name, open(file_path, 'rb'), 'application/octet-stream')
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
upload_response = requests.post(url, headers=headers, files=upload_files)
|
|
46
|
-
if upload_response.status_code != 200:
|
|
47
|
-
raise Exception(f"File upload failed with status code {upload_response.status_code}")
|
|
48
|
-
|
|
49
|
-
return 'https://tmpsend.com/' + file_id
|
|
50
|
-
|
|
51
|
-
if __name__ == "__main__":
|
|
52
|
-
file_path = r"主题1.thmx"
|
|
53
|
-
print(upload(file_path))
|
|
54
|
-
|
|
1
|
+
import requests
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
def upload(file_path: str) -> str:
|
|
5
|
+
url = 'https://tmpsend.com/upload'
|
|
6
|
+
headers = {
|
|
7
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
|
|
8
|
+
'Referer': 'https://tmpsend.com/',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
file_name = os.path.basename(file_path)
|
|
12
|
+
file_size = os.path.getsize(file_path)
|
|
13
|
+
|
|
14
|
+
# 第一次请求:添加文件信息
|
|
15
|
+
files = {
|
|
16
|
+
'action': (None, 'add'),
|
|
17
|
+
'name': (None, file_name),
|
|
18
|
+
'size': (None, str(file_size)),
|
|
19
|
+
'file': (file_name, open(file_path, 'rb'))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
response = requests.post(url, headers=headers, files=files)
|
|
23
|
+
if response.status_code != 200:
|
|
24
|
+
raise Exception(f"Upload failed with status code {response.status_code}")
|
|
25
|
+
|
|
26
|
+
result = response.json()
|
|
27
|
+
if result.get('hasError'):
|
|
28
|
+
raise Exception(result.get('error'))
|
|
29
|
+
|
|
30
|
+
file_id = result.get('id')
|
|
31
|
+
if not file_id:
|
|
32
|
+
raise Exception("Failed to get file ID")
|
|
33
|
+
|
|
34
|
+
# 第二次请求:上传实际文件
|
|
35
|
+
upload_files = {
|
|
36
|
+
'action': (None, 'upload'),
|
|
37
|
+
'id': (None, file_id),
|
|
38
|
+
'name': (None, file_name),
|
|
39
|
+
'size': (None, str(file_size)),
|
|
40
|
+
'start': (None, '0'),
|
|
41
|
+
'end': (None, str(file_size)),
|
|
42
|
+
'data': (file_name, open(file_path, 'rb'), 'application/octet-stream')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
upload_response = requests.post(url, headers=headers, files=upload_files)
|
|
46
|
+
if upload_response.status_code != 200:
|
|
47
|
+
raise Exception(f"File upload failed with status code {upload_response.status_code}")
|
|
48
|
+
|
|
49
|
+
return 'https://tmpsend.com/' + file_id
|
|
50
|
+
|
|
51
|
+
if __name__ == "__main__":
|
|
52
|
+
file_path = r"主题1.thmx"
|
|
53
|
+
print(upload(file_path))
|
|
54
|
+
|
kotonebot/ui/pushkit/__init__.py
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
from .protocol import PushkitProtocol
|
|
2
|
-
from .wxpusher import Wxpusher
|
|
3
|
-
|
|
1
|
+
from .protocol import PushkitProtocol
|
|
2
|
+
from .wxpusher import Wxpusher
|
|
3
|
+
|
|
@@ -1,87 +1,88 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import tempfile
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from typing import Sequence
|
|
5
|
-
|
|
6
|
-
import cv2
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
from
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
1
|
+
import os
|
|
2
|
+
import tempfile
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Sequence
|
|
5
|
+
|
|
6
|
+
import cv2
|
|
7
|
+
import numpy as np
|
|
8
|
+
from cv2.typing import MatLike
|
|
9
|
+
from dotenv import load_dotenv
|
|
10
|
+
|
|
11
|
+
load_dotenv()
|
|
12
|
+
|
|
13
|
+
def _save_temp_image(image: MatLike) -> Path:
|
|
14
|
+
"""将OpenCV图片保存为临时文件"""
|
|
15
|
+
temp_file = Path(tempfile.mktemp(suffix='.jpg'))
|
|
16
|
+
cv2.imwrite(str(temp_file), image)
|
|
17
|
+
return temp_file
|
|
18
|
+
|
|
19
|
+
def _upload_single(image: MatLike | str) -> str:
|
|
20
|
+
"""
|
|
21
|
+
上传单张图片到freeimage.host
|
|
22
|
+
|
|
23
|
+
:param image: OpenCV MatLike 或本地图片文件路径
|
|
24
|
+
"""
|
|
25
|
+
import requests
|
|
26
|
+
|
|
27
|
+
api_url = 'https://freeimage.host/api/1/upload'
|
|
28
|
+
api_key = os.getenv('FREEIMAGEHOST_KEY')
|
|
29
|
+
|
|
30
|
+
if not api_key:
|
|
31
|
+
raise RuntimeError('Environment variable FREEIMAGEHOST_KEY is not set')
|
|
32
|
+
|
|
33
|
+
# 处理输入
|
|
34
|
+
temp_file = None
|
|
35
|
+
if isinstance(image, str):
|
|
36
|
+
# 本地文件路径
|
|
37
|
+
files = {'source': open(image, 'rb')}
|
|
38
|
+
else:
|
|
39
|
+
# 保存OpenCV图片为临时文件
|
|
40
|
+
temp_file = _save_temp_image(image)
|
|
41
|
+
files = {'source': open(temp_file, 'rb')}
|
|
42
|
+
|
|
43
|
+
data = {
|
|
44
|
+
'key': api_key,
|
|
45
|
+
'action': 'upload',
|
|
46
|
+
'format': 'json'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
# 发送POST请求
|
|
51
|
+
response = requests.post(api_url, data=data, files=files)
|
|
52
|
+
|
|
53
|
+
if response.status_code != 200:
|
|
54
|
+
raise RuntimeError(f'Upload failed: HTTP {response.status_code}')
|
|
55
|
+
|
|
56
|
+
result = response.json()
|
|
57
|
+
|
|
58
|
+
if result['status_code'] != 200:
|
|
59
|
+
raise RuntimeError(f'Upload failed: API {result["status_txt"]}')
|
|
60
|
+
|
|
61
|
+
return result['image']['url']
|
|
62
|
+
|
|
63
|
+
finally:
|
|
64
|
+
# 清理临时文件
|
|
65
|
+
files['source'].close()
|
|
66
|
+
if temp_file and temp_file.exists():
|
|
67
|
+
temp_file.unlink()
|
|
68
|
+
|
|
69
|
+
def upload(images: MatLike | str | Sequence[MatLike | str]) -> list[str]:
|
|
70
|
+
"""上传一张或多张图片到freeimage.host
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
images: 单张图片或图片列表。每个图片可以是OpenCV图片对象或本地图片文件路径
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
上传后的图片URL列表
|
|
77
|
+
"""
|
|
78
|
+
if isinstance(images, (str, np.ndarray)):
|
|
79
|
+
_images = [images]
|
|
80
|
+
elif isinstance(images, Sequence):
|
|
81
|
+
_images = [img for img in images]
|
|
82
|
+
else:
|
|
83
|
+
raise ValueError("Invalid input type")
|
|
84
|
+
|
|
85
|
+
return [_upload_single(img) for img in _images]
|
|
86
|
+
|
|
87
|
+
if __name__ == "__main__":
|
|
88
|
+
print(upload(cv2.imread("res/sprites/jp/common/button_close.png")))
|
kotonebot/ui/pushkit/protocol.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
from typing import Protocol
|
|
2
|
-
|
|
3
|
-
from cv2.typing import MatLike
|
|
4
|
-
|
|
5
|
-
class PushkitProtocol(Protocol):
|
|
6
|
-
def push(
|
|
7
|
-
self,
|
|
8
|
-
title: str,
|
|
9
|
-
message: str,
|
|
10
|
-
*,
|
|
11
|
-
images: list[str | MatLike] | None = None,
|
|
12
|
-
) -> None:
|
|
13
|
-
...
|
|
1
|
+
from typing import Protocol
|
|
2
|
+
|
|
3
|
+
from cv2.typing import MatLike
|
|
4
|
+
|
|
5
|
+
class PushkitProtocol(Protocol):
|
|
6
|
+
def push(
|
|
7
|
+
self,
|
|
8
|
+
title: str,
|
|
9
|
+
message: str,
|
|
10
|
+
*,
|
|
11
|
+
images: list[str | MatLike] | None = None,
|
|
12
|
+
) -> None:
|
|
13
|
+
...
|
kotonebot/ui/pushkit/wxpusher.py
CHANGED
|
@@ -1,53 +1,54 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import json
|
|
3
|
-
from typing import Sequence
|
|
4
|
-
import
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
from .
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
self.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
wxpusher
|
|
53
|
-
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
from typing import Sequence
|
|
4
|
+
from cv2.typing import MatLike
|
|
5
|
+
from dotenv import dotenv_values
|
|
6
|
+
|
|
7
|
+
from .image_host import upload
|
|
8
|
+
from .protocol import PushkitProtocol
|
|
9
|
+
|
|
10
|
+
config = dotenv_values(".env")
|
|
11
|
+
|
|
12
|
+
class Wxpusher(PushkitProtocol):
|
|
13
|
+
def __init__(self, app_token: str | None = None, uid: str | None = None):
|
|
14
|
+
self.app_token = app_token or config["WXPUSHER_APP_TOKEN"]
|
|
15
|
+
self.uid = uid or config["WXPUSHER_UID"]
|
|
16
|
+
|
|
17
|
+
def push(self, title: str, message: str, *, images: Sequence[str | MatLike] | None = None) -> None:
|
|
18
|
+
import requests
|
|
19
|
+
|
|
20
|
+
summary = title
|
|
21
|
+
content = message
|
|
22
|
+
|
|
23
|
+
if images:
|
|
24
|
+
image_urls = upload(images)
|
|
25
|
+
img_md = "\n".join([f"" for img_url in image_urls])
|
|
26
|
+
content = content + "\n" + img_md
|
|
27
|
+
|
|
28
|
+
data = {
|
|
29
|
+
"appToken": self.app_token,
|
|
30
|
+
"uid": self.uid,
|
|
31
|
+
"summary": summary,
|
|
32
|
+
"content": content,
|
|
33
|
+
"contentType": 3, # 1: 文本 2: HTML 3: Markdown
|
|
34
|
+
"uids": [self.uid],
|
|
35
|
+
"verifyPay": False,
|
|
36
|
+
"verifyPayType": 0
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
response = requests.post(
|
|
40
|
+
"http://wxpusher.zjiecode.com/api/send/message",
|
|
41
|
+
json=data
|
|
42
|
+
)
|
|
43
|
+
result = response.json()
|
|
44
|
+
|
|
45
|
+
if result["code"] != 1000 or not result["success"]:
|
|
46
|
+
raise RuntimeError(f"推送失败: {result['msg']}")
|
|
47
|
+
|
|
48
|
+
# TODO: 极简推送 https://wxpusher.zjiecode.com/docs/#/?id=spt
|
|
49
|
+
|
|
50
|
+
if __name__ == "__main__":
|
|
51
|
+
import cv2
|
|
52
|
+
wxpusher = Wxpusher()
|
|
53
|
+
wxpusher.push("测试图片", "测试图片", images=[cv2.imread("res/sprites/jp/common/button_close.png")])
|
|
54
|
+
|