uel-toolcraft 0.1.0__tar.gz

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 (28) hide show
  1. uel_toolcraft-0.1.0/.gitignore +6 -0
  2. uel_toolcraft-0.1.0/LICENSE +21 -0
  3. uel_toolcraft-0.1.0/PKG-INFO +113 -0
  4. uel_toolcraft-0.1.0/README.md +71 -0
  5. uel_toolcraft-0.1.0/main.py +4 -0
  6. uel_toolcraft-0.1.0/pyproject.toml +62 -0
  7. uel_toolcraft-0.1.0/uel_toolcraft/__init__.py +12 -0
  8. uel_toolcraft-0.1.0/uel_toolcraft/__main__.py +19 -0
  9. uel_toolcraft-0.1.0/uel_toolcraft/models/AESCipher.py +134 -0
  10. uel_toolcraft-0.1.0/uel_toolcraft/models/BarkClient.py +120 -0
  11. uel_toolcraft-0.1.0/uel_toolcraft/models/PyQtAsyncHelper.py +91 -0
  12. uel_toolcraft-0.1.0/uel_toolcraft/models/SQLiteHelper.py +335 -0
  13. uel_toolcraft-0.1.0/uel_toolcraft/models/WAPPClient.py +198 -0
  14. uel_toolcraft-0.1.0/uel_toolcraft/models/__init__.py +29 -0
  15. uel_toolcraft-0.1.0/uel_toolcraft/models/logging_config.py +91 -0
  16. uel_toolcraft-0.1.0/uel_toolcraft/models/redis_upstash.py +61 -0
  17. uel_toolcraft-0.1.0/uel_toolcraft/tools/__init__.py +146 -0
  18. uel_toolcraft-0.1.0/uel_toolcraft/tools/config_tools.py +20 -0
  19. uel_toolcraft-0.1.0/uel_toolcraft/tools/ffmpeg_tools.py +261 -0
  20. uel_toolcraft-0.1.0/uel_toolcraft/tools/filesystem_tools.py +257 -0
  21. uel_toolcraft-0.1.0/uel_toolcraft/tools/git_tools.py +207 -0
  22. uel_toolcraft-0.1.0/uel_toolcraft/tools/json_tools.py +69 -0
  23. uel_toolcraft-0.1.0/uel_toolcraft/tools/pickle_tools.py +34 -0
  24. uel_toolcraft-0.1.0/uel_toolcraft/tools/rclone_tools.py +102 -0
  25. uel_toolcraft-0.1.0/uel_toolcraft/tools/supabase_tools.py +30 -0
  26. uel_toolcraft-0.1.0/uel_toolcraft/tools/txt_tools.py +126 -0
  27. uel_toolcraft-0.1.0/uel_toolcraft/tools/ui_tools.py +37 -0
  28. uel_toolcraft-0.1.0/uel_toolcraft/tools/utils.py +35 -0
@@ -0,0 +1,6 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ .venv/
6
+ build/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Samuel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,113 @@
1
+ Metadata-Version: 2.4
2
+ Name: uel-toolcraft
3
+ Version: 0.1.0
4
+ Summary: A Unified Essential Library toolcraft (uel-toolcraft) for personal usage.
5
+ Author: Samuel
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: models,tools,utilities
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: json5>=0.9.0
20
+ Requires-Dist: pycryptodome>=3.15.0
21
+ Requires-Dist: requests>=2.28.0
22
+ Provides-Extra: all
23
+ Requires-Dist: ffmpeg-python>=0.2.0; extra == 'all'
24
+ Requires-Dist: gitpython>=3.1.0; extra == 'all'
25
+ Requires-Dist: pyqt6>=6.5.0; extra == 'all'
26
+ Requires-Dist: rclone-python>=0.1.0; extra == 'all'
27
+ Requires-Dist: supabase>=2.0.0; extra == 'all'
28
+ Requires-Dist: upstash-redis>=0.1.0; extra == 'all'
29
+ Provides-Extra: ffmpeg
30
+ Requires-Dist: ffmpeg-python>=0.2.0; extra == 'ffmpeg'
31
+ Provides-Extra: git
32
+ Requires-Dist: gitpython>=3.1.0; extra == 'git'
33
+ Provides-Extra: pyqt
34
+ Requires-Dist: pyqt6>=6.5.0; extra == 'pyqt'
35
+ Provides-Extra: rclone
36
+ Requires-Dist: rclone-python>=0.1.0; extra == 'rclone'
37
+ Provides-Extra: redis
38
+ Requires-Dist: upstash-redis>=0.1.0; extra == 'redis'
39
+ Provides-Extra: supabase
40
+ Requires-Dist: supabase>=2.0.0; extra == 'supabase'
41
+ Description-Content-Type: text/markdown
42
+
43
+ # uel-toolcraft
44
+
45
+ A Unified Essential Library toolcraft for personal usage.
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ pip install uel-toolcraft
51
+ ```
52
+
53
+ Install with optional dependencies:
54
+
55
+ ```bash
56
+ # All optional dependencies
57
+ pip install uel-toolcraft[all]
58
+
59
+ # Specific extras
60
+ pip install uel-toolcraft[pyqt,ffmpeg,git]
61
+ ```
62
+
63
+ Local development install:
64
+
65
+ ```bash
66
+ git clone https://github.com/iShengren/uel-toolcraft.git
67
+ cd uel-toolcraft
68
+ uv pip install -e ".[all]"
69
+ ```
70
+
71
+ ## Usage
72
+
73
+ ### Models
74
+
75
+ ```python
76
+ from uel_toolcraft.models import AESCipher, SQLiteHelper, BarkClient
77
+
78
+ cipher = AESCipher("my-secret-key")
79
+ db = SQLiteHelper("mydb.sqlite")
80
+ bark = BarkClient("https://bark.example.com", "device_key")
81
+ ```
82
+
83
+ ### Tools
84
+
85
+ ```python
86
+ from uel_toolcraft.tools import read_json_file, write_json_file, md5sum
87
+
88
+ data = read_json_file("config.json")
89
+ checksum = md5sum("large_file.bin")
90
+ write_json_file({"key": "value"}, "output.json")
91
+ ```
92
+
93
+ ### CLI
94
+
95
+ ```bash
96
+ uel-toolcraft --version
97
+ python -m uel_toolcraft --version
98
+ ```
99
+
100
+ ## Optional Dependencies
101
+
102
+ | Extra | Packages | Used by |
103
+ |---|---|---|
104
+ | `pyqt` | PyQt6 | `PyQtAsyncHelper`, `ui_tools` |
105
+ | `ffmpeg` | ffmpeg-python | `ffmpeg_tools` |
106
+ | `rclone` | rclone-python | `rclone_tools` |
107
+ | `supabase` | supabase | `supabase_tools` |
108
+ | `git` | GitPython | `git_tools` |
109
+ | `redis` | upstash-redis | `redis_upstash` |
110
+
111
+ ## License
112
+
113
+ MIT
@@ -0,0 +1,71 @@
1
+ # uel-toolcraft
2
+
3
+ A Unified Essential Library toolcraft for personal usage.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install uel-toolcraft
9
+ ```
10
+
11
+ Install with optional dependencies:
12
+
13
+ ```bash
14
+ # All optional dependencies
15
+ pip install uel-toolcraft[all]
16
+
17
+ # Specific extras
18
+ pip install uel-toolcraft[pyqt,ffmpeg,git]
19
+ ```
20
+
21
+ Local development install:
22
+
23
+ ```bash
24
+ git clone https://github.com/iShengren/uel-toolcraft.git
25
+ cd uel-toolcraft
26
+ uv pip install -e ".[all]"
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### Models
32
+
33
+ ```python
34
+ from uel_toolcraft.models import AESCipher, SQLiteHelper, BarkClient
35
+
36
+ cipher = AESCipher("my-secret-key")
37
+ db = SQLiteHelper("mydb.sqlite")
38
+ bark = BarkClient("https://bark.example.com", "device_key")
39
+ ```
40
+
41
+ ### Tools
42
+
43
+ ```python
44
+ from uel_toolcraft.tools import read_json_file, write_json_file, md5sum
45
+
46
+ data = read_json_file("config.json")
47
+ checksum = md5sum("large_file.bin")
48
+ write_json_file({"key": "value"}, "output.json")
49
+ ```
50
+
51
+ ### CLI
52
+
53
+ ```bash
54
+ uel-toolcraft --version
55
+ python -m uel_toolcraft --version
56
+ ```
57
+
58
+ ## Optional Dependencies
59
+
60
+ | Extra | Packages | Used by |
61
+ |---|---|---|
62
+ | `pyqt` | PyQt6 | `PyQtAsyncHelper`, `ui_tools` |
63
+ | `ffmpeg` | ffmpeg-python | `ffmpeg_tools` |
64
+ | `rclone` | rclone-python | `rclone_tools` |
65
+ | `supabase` | supabase | `supabase_tools` |
66
+ | `git` | GitPython | `git_tools` |
67
+ | `redis` | upstash-redis | `redis_upstash` |
68
+
69
+ ## License
70
+
71
+ MIT
@@ -0,0 +1,4 @@
1
+ from uel_toolcraft.__main__ import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,62 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "uel-toolcraft"
7
+ version = "0.1.0"
8
+ description = "A Unified Essential Library toolcraft (uel-toolcraft) for personal usage."
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ {name = "Samuel"},
14
+ ]
15
+ keywords = ["tools", "utilities", "models"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
26
+ ]
27
+
28
+ dependencies = [
29
+ "json5>=0.9.0",
30
+ "pycryptodome>=3.15.0",
31
+ "requests>=2.28.0",
32
+ ]
33
+
34
+ [project.optional-dependencies]
35
+ pyqt = ["PyQt6>=6.5.0"]
36
+ ffmpeg = ["ffmpeg-python>=0.2.0"]
37
+ rclone = ["rclone-python>=0.1.0"]
38
+ supabase = ["supabase>=2.0.0"]
39
+ git = ["GitPython>=3.1.0"]
40
+ redis = ["upstash-redis>=0.1.0"]
41
+ all = [
42
+ "PyQt6>=6.5.0",
43
+ "ffmpeg-python>=0.2.0",
44
+ "rclone-python>=0.1.0",
45
+ "supabase>=2.0.0",
46
+ "GitPython>=3.1.0",
47
+ "upstash-redis>=0.1.0",
48
+ ]
49
+
50
+ [project.scripts]
51
+ uel-toolcraft = "main:main"
52
+
53
+ [tool.hatch.build.targets.wheel]
54
+ packages = ["uel_toolcraft"]
55
+
56
+ [tool.hatch.build.targets.sdist]
57
+ include = [
58
+ "/uel_toolcraft",
59
+ "/main.py",
60
+ "/README.md",
61
+ "/LICENSE",
62
+ ]
@@ -0,0 +1,12 @@
1
+ """
2
+ uel-toolcraft — A Unified Essential Library toolcraft for personal usage.
3
+
4
+ Models: AES encryption, Bark push, logging, PyQt async, Redis, SQLite, WeCom.
5
+ Tools: config, ffmpeg, filesystem, git, json, pickle, rclone, supabase, text, UI, utils.
6
+ """
7
+
8
+ try:
9
+ from importlib.metadata import version as _get_version
10
+ __version__ = _get_version("uel-toolcraft")
11
+ except Exception:
12
+ __version__ = "0.1.0"
@@ -0,0 +1,19 @@
1
+ """Entry point for `python -m uel_toolcraft`."""
2
+
3
+ import argparse
4
+ from importlib.metadata import version as _get_version
5
+
6
+
7
+ def main():
8
+ parser = argparse.ArgumentParser(
9
+ description="uel-toolcraft — A Unified Essential Library toolcraft"
10
+ )
11
+ parser.add_argument(
12
+ "--version", action="version",
13
+ version=f"uel-toolcraft {_get_version('uel-toolcraft')}"
14
+ )
15
+ parser.parse_args()
16
+
17
+
18
+ if __name__ == "__main__":
19
+ main()
@@ -0,0 +1,134 @@
1
+ import base64
2
+ import logging
3
+ from .logging_config import LogConfig
4
+ from Crypto.Cipher import AES
5
+ from Crypto.Util.Padding import pad, unpad
6
+ from Crypto.Random import get_random_bytes
7
+
8
+ # 获取日志记录器
9
+ logger: logging.Logger = logging.getLogger(__name__)
10
+
11
+
12
+ class AESCipher:
13
+ def __init__(self, key, iv=None, mode: str = 'CBC', padding_style: str = 'pkcs7'):
14
+ """
15
+ 初始化AES加密器
16
+
17
+ :param key: 密钥 bytes (16/24/32字节分别对应AES-128/192/256)
18
+ :param iv: 初始化向量 (CBC/GCM模式需要,16字节)
19
+ :param mode: 加密模式 'CBC'/'ECB'/'GCM'
20
+ """
21
+ self.key = key if type(key) is bytes else key.encode('utf-8') # 将字符串转换为字节
22
+ self.mode_name = mode.upper()
23
+ if self.mode_name == 'CBC':
24
+ self.mode = AES.MODE_CBC
25
+ if iv is None:
26
+ raise ValueError("IV is required for CBC mode")
27
+ self.iv = iv
28
+ elif self.mode_name == 'ECB':
29
+ self.mode = AES.MODE_ECB
30
+ self.iv = None
31
+ elif self.mode_name == 'GCM':
32
+ self.mode = AES.MODE_GCM
33
+ if iv is None:
34
+ raise ValueError("IV is required for GCM mode")
35
+ self.iv = iv
36
+ else:
37
+ raise ValueError(f"Unsupported AES mode: {mode}")
38
+ if (self.iv is not None) and (type(self.iv) is str):
39
+ self.iv = self.iv.encode('utf-8') # 将字符串转换为字节
40
+ self.padding_style = padding_style # 默认填充方式
41
+
42
+ def encrypt(self, plaintext: str) -> str:
43
+ """
44
+ 加密数据并返回 base64 编码字符串
45
+
46
+ :param plaintext: 原文字符串
47
+ :return: base64加密结果
48
+ """
49
+ plaintext_bytes = plaintext.encode('utf-8')
50
+
51
+ if self.mode_name in ['CBC', 'ECB']:
52
+ padded_data = pad(plaintext_bytes, AES.block_size, style=self.padding_style) # padding 填充
53
+ # 创建 AES 加密器
54
+ if self.iv:
55
+ cipher = AES.new(self.key, self.mode, iv=self.iv) # 忽略意外类型 "(Any, Literal[11], Any)"
56
+ else:
57
+ cipher = AES.new(self.key, self.mode)
58
+ encrypted = cipher.encrypt(padded_data) # 加密数据
59
+ elif self.mode_name == 'GCM':
60
+ # # GCM 模式不需要填充
61
+ # padded_data = pad(plaintext_bytes, AES.block_size, style=self.padding_style) # padding 填充
62
+ cipher = AES.new(self.key, self.mode, nonce=self.iv)
63
+ encrypted, tag = cipher.encrypt_and_digest(plaintext_bytes) # 加密数据
64
+ encrypted += tag # 将tag附加到密文末尾
65
+ else:
66
+ raise ValueError("Unsupported encryption mode.")
67
+
68
+ # 将加密结果转换为base64编码
69
+ base64_encrypted = base64.b64encode(encrypted).decode('utf-8')
70
+
71
+ return base64_encrypted
72
+
73
+ def decrypt(self, encrypted_base64: str) -> str:
74
+ """
75
+ 解密 base64 数据并返回原文字符串
76
+
77
+ :param encrypted_base64: base64编码的加密数据
78
+ :return: 解密后的原文
79
+ """
80
+ encrypted_bytes = base64.b64decode(encrypted_base64)
81
+
82
+ if self.mode_name in ['CBC', 'ECB']:
83
+ if self.iv:
84
+ cipher = AES.new(self.key, self.mode, iv=self.iv) # 忽略意外类型 "(Any, Literal[11], Any)"
85
+ else:
86
+ cipher = AES.new(self.key, self.mode)
87
+ decrypted_padded = cipher.decrypt(encrypted_bytes)
88
+ plaintext = unpad(decrypted_padded, AES.block_size, self.padding_style).decode('utf-8') # 去除填充
89
+ elif self.mode_name == 'GCM':
90
+ cipher = AES.new(self.key, self.mode, nonce=self.iv) # 忽略意外类型
91
+ tag = encrypted_bytes[-AES.block_size:]
92
+ encrypted_data = encrypted_bytes[:-AES.block_size]
93
+ plaintext = cipher.decrypt_and_verify(encrypted_data, tag).decode('utf-8')
94
+ else:
95
+ raise ValueError("Unsupported decryption mode.")
96
+
97
+ return plaintext
98
+
99
+ @staticmethod
100
+ def generate_key(bits: int = 128, output_format: str = "str") -> bytes|str:
101
+ """
102
+ 生成随机AES密钥
103
+
104
+ :param bits: 密钥长度(128/192/256)
105
+ :param output_format: 输出格式("bytes" 或 "str")
106
+ :return: 随机密钥 bytes 或 str
107
+ """
108
+ if bits not in [128, 192, 256]:
109
+ raise ValueError("Key length must be 128, 192, or 256 bits.")
110
+ # 生成随机密钥
111
+ random_bytes = get_random_bytes(bits // 8)
112
+ if output_format == "str":
113
+ # 将字节转换为字符串, 取前 bits // 8 字节,并使用 URL-safe base64 编码
114
+ return base64.urlsafe_b64encode(random_bytes).decode('utf-8')[:(bits // 8)]
115
+ else:
116
+ return random_bytes
117
+
118
+
119
+ if __name__ == "__main__":
120
+ LogConfig(level=logging.DEBUG) # 初始化日志配置
121
+
122
+ # 示例用法
123
+ key_ = AESCipher.generate_key(128, output_format="str") # 生成128位密钥
124
+ iv_ = get_random_bytes(16) # 生成16字节IV
125
+ aes_cipher_ = AESCipher(key_, iv_, mode='CBC', padding_style='pkcs7')
126
+ plaintext_ = '{"body": "This is a aes cipher test message", "title": "AES Cipher Test"}'
127
+ encrypted_ = aes_cipher_.encrypt(plaintext_)
128
+ decrypted_ = aes_cipher_.decrypt(encrypted_)
129
+
130
+ print(f"Key: {key_}")
131
+ print(f"IV: {iv_}")
132
+ print(f"Plaintext: {plaintext_}")
133
+ print(f"Encrypted: {encrypted_}")
134
+ print(f"Decrypted: {decrypted_}")
@@ -0,0 +1,120 @@
1
+ import requests
2
+ import json
3
+ from .AESCipher import AESCipher
4
+ import logging
5
+ from .logging_config import LogConfig
6
+
7
+ # 获取日志记录器
8
+ logger: logging.Logger = logging.getLogger(__name__)
9
+
10
+
11
+ class BarkClient:
12
+ def __init__(self, server_url: str, device_key: str | list, debug: bool = False):
13
+ """
14
+ 初始化 BarkClient
15
+ :param server_url: Bark服务器地址,例如 http://127.0.0.1:8080
16
+ :param device_key: 设备的推送key,例如 "your_bark_device_token" 或 ["device_key1", "device_key2"]
17
+ :param debug: 是否开启调试模式,默认为 False
18
+ """
19
+ self.debug = debug # 是否开启调试模式
20
+ self.server_url = server_url.rstrip('/')
21
+ self.device_key = device_key
22
+ self.push_url = f"{self.server_url}/push"
23
+ self.headers = {
24
+ "Content-Type": "application/json; charset=utf-8",
25
+ }
26
+ self.supported_parameters = [
27
+ "title", "subtitle", "body", "device_key", "device_keys", "level", "volume", "badge", "call", "autoCopy", "copy", "sound", "icon", "group", "ciphertext", "isArchive", "url", "action"
28
+ ]
29
+
30
+ # 根据 device_key 的类型设置 payload 中的 device_key 键名称
31
+ if isinstance(self.device_key, list):
32
+ # 如果 device_key 是列表,parameters 中的 device_key 应该是 device_keys
33
+ self.device_key_payload_name = "device_keys"
34
+ if self.debug:
35
+ logger.debug(f"✅ [BarkClient] device_key is a list, using 'device_keys' in payload.")
36
+ elif isinstance(self.device_key, str):
37
+ # 如果 device_key 是字符串,parameters 中的 device_key 应该是 device_key
38
+ self.device_key_payload_name = "device_key"
39
+ if self.debug:
40
+ logger.debug(f"✅ [BarkClient] device_key is a string, using 'device_key' in payload.")
41
+ else:
42
+ logger.error("❌ [BarkClient] device_key must be a string or a list of strings.")
43
+ raise ValueError("device_key must be a string or a list of strings.")
44
+
45
+ def send(self, body: str, title: str = "", **extra_fields):
46
+ """
47
+ 发送推送,支持额外动态字段
48
+ :param body: 消息正文
49
+ :param title: 消息标题
50
+ :param extra_fields: 其他自定义参数(sound, badge, icon, group, url等, 帮助文档: "https://bark.day.app/#/tutorial")
51
+ """
52
+ payload = {
53
+ "body": body,
54
+ self.device_key_payload_name: self.device_key,
55
+ "title": title
56
+ }
57
+
58
+ # 是否加密
59
+ encrypt = False
60
+
61
+ # 动态添加额外参数
62
+ for field_name, value in extra_fields.items():
63
+ if (field_name in self.supported_parameters) and (value is not None):
64
+ payload[field_name] = value
65
+ elif (field_name in ["encrypt", "aes"]) and (value is not None):
66
+ # 如果参数是 encrypt,则设置 encrypt 变量
67
+ encrypt = value
68
+
69
+ try:
70
+ if encrypt:
71
+ # 如果需要加密,则使用 AES 加密
72
+ key = extra_fields.get("key", None) # 默认密钥
73
+ if key is None:
74
+ logger.error("❌ [BarkClient] Key is required for encryption.")
75
+ raise ValueError("Key is required for encryption.")
76
+ # 从 extra_fields 中获取 IV, 如果没有提供则生成一个默认的 IV
77
+ iv = extra_fields.get("iv") or AESCipher.generate_key(128) # 默认 IV
78
+ mode = extra_fields.get("mode", "CBC") # 默认模式为 CBC
79
+ aes_cipher = AESCipher(key=key, iv=iv, mode=mode)
80
+ payload_str = json.dumps(payload)
81
+ encrypted_payload = aes_cipher.encrypt(payload_str)
82
+ # 构建含有加密文本的 payload
83
+ payload = {
84
+ self.device_key_payload_name: self.device_key,
85
+ "ciphertext": encrypted_payload,
86
+ "iv": iv,
87
+ }
88
+
89
+ # 发送请求
90
+ send_response = requests.post(
91
+ url=self.push_url,
92
+ headers=self.headers,
93
+ data=json.dumps(payload),
94
+ timeout=10
95
+ )
96
+
97
+ if self.debug:
98
+ # 检查响应状态码
99
+ logger.debug(f"✅ [BarkClient] Response Status Code: {send_response.status_code}")
100
+ logger.debug(f"✅ [BarkClient] Response Body: {send_response.content.decode('utf-8')}")
101
+
102
+ return send_response
103
+ except requests.exceptions.RequestException as e:
104
+ logger.error(f"❌ [BarkClient] Request failed: {e}")
105
+ return None
106
+
107
+
108
+ if __name__ == "__main__":
109
+ LogConfig(level=logging.DEBUG) # 初始化日志配置
110
+ bark = BarkClient(server_url="https://api.day.app/", device_key="your_bark_device_token", debug=True)
111
+ response = bark.send(
112
+ body="This is a test message.",
113
+ title="Test",
114
+ group="Test Group",
115
+ debug=False,
116
+ encrypt=False,
117
+ mode="CBC",
118
+ key="aes_16_byte_key",
119
+ iv="aes_16_byte_iv" # CBC 模式需要 16 字节 IV, 如果不指定则自动生成 16 字节 IV
120
+ )
@@ -0,0 +1,91 @@
1
+ import asyncio
2
+ from threading import Thread
3
+ from PyQt6.QtCore import QObject, pyqtSignal, QCoreApplication
4
+
5
+
6
+ class AsyncRunner:
7
+ """
8
+ 全局异步执行器,允许在非主线程中运行异步任务。
9
+ 使用 asyncio.run_coroutine_threadsafe 方法将协程提交到事件循环。
10
+ 通过线程安全的方式与 PyQt 信号系统交互。
11
+ 该类在初始化时创建一个新的事件循环,并在独立线程中运行该循环。
12
+ 可以通过 run_async 方法提交异步任务。
13
+ 该类适用于需要在 PyQt 应用程序中执行异步操作的场景。
14
+ 例如,可以在按钮点击事件中调用 run_async 方法来执行异步任务。
15
+ 该类的设计使得异步任务可以在后台线程中运行,而不会阻塞主线程的 UI 更新。
16
+ 通过使用 PyQt 的信号机制,可以在异步任务完成后安全地更新 UI。
17
+ 该类的使用方式如下:
18
+ 1. 创建 AsyncRunner 实例:`runner = AsyncRunner()`
19
+ 2. 定义异步任务:`async def my_async_task(): ...`
20
+ 3. 提交异步任务:`runner.run_async(my_async_task())`
21
+ 4. 在异步任务中使用 `self.signals.result_ready.emit(result)`
22
+ 来发送信号到主线程。
23
+ 5. 在主线程中连接信号:`self.signals.result_ready.connect(self.display_result)`
24
+ 6. 在 `display_result` 方法中处理结果并更新 UI。
25
+ 该类的设计使得异步任务可以在 PyQt 应用程序中安全地执行,同时保持 UI 的响应性。
26
+ """
27
+ def __init__(self):
28
+ self.loop = asyncio.new_event_loop()
29
+ self.thread = Thread(target=self._start_loop, daemon=True)
30
+ self.thread.start()
31
+
32
+ def _start_loop(self):
33
+ asyncio.set_event_loop(self.loop)
34
+ self.loop.run_forever()
35
+
36
+ def run_async(self, coro):
37
+ return asyncio.run_coroutine_threadsafe(coro, self.loop)
38
+
39
+
40
+ class SignalEmitter(QObject):
41
+ """
42
+ 信号发射器类,用于在 PyQt 应用程序中发出信号。
43
+ 该类继承自 QObject,并定义了一个信号 result_ready。
44
+ 该信号可以在异步任务完成后发出结果,以便在主线程中处理。
45
+ 使用该类可以实现线程安全的信号发射,确保在 PyQt 应用程序中正确地更新 UI。
46
+ 该类的使用方式如下:
47
+ 1. 创建 SignalEmitter 实例:`self.signals = SignalEmitter()`
48
+ 2. 在异步任务中发出信号:`self.signals.result_ready.emit(result)`
49
+ 3. 在主线程中连接信号:`self.signals.result_ready.connect(self.display_result)`
50
+ 4. 在 `display_result` 方法中处理结果并更新 UI。
51
+ 该类的设计使得异步任务可以在后台线程中运行,而不会阻塞主线程的 UI 更新。
52
+ 通过使用 PyQt 的信号机制,可以在异步任务完成后安全地更新 UI。
53
+
54
+ """
55
+ result_ready = pyqtSignal(str)
56
+
57
+
58
+ class SignalEmitter2(QObject):
59
+ """
60
+ 信号发射器类,用于在 PyQt 应用程序中发出信号。
61
+ 该类继承自 QObject,并定义了一个信号 result_ready。
62
+ 该信号可以在异步任务完成后发出结果,以便在主线程中处理。
63
+ 使用该类可以实现线程安全的信号发射,确保在 PyQt 应用程序中正确地更新 UI。
64
+ 该类的使用方式如下:
65
+ 1. 创建 SignalEmitter 实例:`self.signals = SignalEmitter()`
66
+ 2. 在异步任务中发出信号:`self.signals.result_ready.emit(result)`
67
+ 3. 在主线程中连接信号:`self.signals.result_ready.connect(self.display_result)`
68
+ 4. 在 `display_result` 方法中处理结果并更新 UI。
69
+ 该类的设计使得异步任务可以在后台线程中运行,而不会阻塞主线程的 UI 更新。
70
+ 通过使用 PyQt 的信号机制,可以在异步任务完成后安全地更新 UI。
71
+
72
+ """
73
+ result_ready = pyqtSignal(str, str)
74
+
75
+
76
+ class SignalEmitter3(QObject):
77
+ """
78
+ 信号发射器类,用于在 PyQt 应用程序中发出信号。
79
+ 该类继承自 QObject,并定义了一个信号 result_ready。
80
+ 该信号可以在异步任务完成后发出结果,以便在主线程中处理。
81
+ 使用该类可以实现线程安全的信号发射,确保在 PyQt 应用程序中正确地更新 UI。
82
+ 该类的使用方式如下:
83
+ 1. 创建 SignalEmitter 实例:`self.signals = SignalEmitter()`
84
+ 2. 在异步任务中发出信号:`self.signals.result_ready.emit(result)`
85
+ 3. 在主线程中连接信号:`self.signals.result_ready.connect(self.display_result)`
86
+ 4. 在 `display_result` 方法中处理结果并更新 UI。
87
+ 该类的设计使得异步任务可以在后台线程中运行,而不会阻塞主线程的 UI 更新。
88
+ 通过使用 PyQt 的信号机制,可以在异步任务完成后安全地更新 UI。
89
+
90
+ """
91
+ result_ready = pyqtSignal(str, str, str)