pymecli 0.6.1__tar.gz → 0.6.2__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.
- {pymecli-0.6.1 → pymecli-0.6.2}/PKG-INFO +6 -3
- {pymecli-0.6.1 → pymecli-0.6.2}/README.md +5 -2
- pymecli-0.6.1/cli/etcd_mysql.py → pymecli-0.6.2/cli/etcd_sync.py +106 -2
- {pymecli-0.6.1 → pymecli-0.6.2}/pyproject.toml +2 -2
- {pymecli-0.6.1 → pymecli-0.6.2}/.gitignore +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/api/__init__.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/api/v1/__init__.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/api/v1/clash.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/api/v1/redis.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/cli/__init__.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/cli/bitget.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/cli/dnspod.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/cli/dst.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/cli/example.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/cli/fast.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/cli/gate.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/cli/okx.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/cli/quark.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/cli/redis_csv.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/cli/tele.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/cli/util.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/core/__init__.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/core/clash.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/core/config.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/core/redis_client.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/crypto/__init__.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/crypto/bitget.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/crypto/gate.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/crypto/okx.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/data/__init__.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/data/dou_dict.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/data/dou_list.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/data/main.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/data/template.yaml +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/models/__init__.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/models/douzero_model.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/models/ocr_model.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/models/response.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/utils/__init__.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/utils/elapsed.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/utils/etcd.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/utils/helper.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/utils/logger.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/utils/mysql.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/utils/notify.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/utils/path.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/utils/pd.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/utils/pyredis.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/utils/sendNotify.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/utils/sleep.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/utils/text.py +0 -0
- {pymecli-0.6.1 → pymecli-0.6.2}/utils/toml.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pymecli
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: My CLI
|
|
5
5
|
Project-URL: Homepage, https://pypi.org/project/pymecli
|
|
6
6
|
Project-URL: Repository, https://github.com/meme2046/pymecli
|
|
@@ -67,10 +67,13 @@ uv run dst cml d:/.backups/dontstarvetogether/modoverrides.lua -m 3486375086
|
|
|
67
67
|
# etcdsync
|
|
68
68
|
|
|
69
69
|
```shell
|
|
70
|
-
etcdsync etcd2mysql /
|
|
70
|
+
etcdsync etcd2mysql /test -e d:/.env/pymecli.env -t kvs
|
|
71
71
|
etcdsync etcd2mysql "" -e d:/.env/pymecli.env -t kvs # 全量
|
|
72
|
-
etcdsync mysql2etcd /
|
|
72
|
+
etcdsync mysql2etcd /test -e d:/.env/pymecli.env -t kvs
|
|
73
73
|
etcdsync mysql2etcd -e d:/.env/pymecli.env -t kvs # 全量
|
|
74
|
+
|
|
75
|
+
etcdsync etcd2json /test -e d:/.env/pymecli.env -f xxx.json
|
|
76
|
+
etcdsync json2etcd /test -e d:/.env/pymecli.env -f xxx.json
|
|
74
77
|
```
|
|
75
78
|
# quark
|
|
76
79
|
```shell
|
|
@@ -28,10 +28,13 @@ uv run dst cml d:/.backups/dontstarvetogether/modoverrides.lua -m 3486375086
|
|
|
28
28
|
# etcdsync
|
|
29
29
|
|
|
30
30
|
```shell
|
|
31
|
-
etcdsync etcd2mysql /
|
|
31
|
+
etcdsync etcd2mysql /test -e d:/.env/pymecli.env -t kvs
|
|
32
32
|
etcdsync etcd2mysql "" -e d:/.env/pymecli.env -t kvs # 全量
|
|
33
|
-
etcdsync mysql2etcd /
|
|
33
|
+
etcdsync mysql2etcd /test -e d:/.env/pymecli.env -t kvs
|
|
34
34
|
etcdsync mysql2etcd -e d:/.env/pymecli.env -t kvs # 全量
|
|
35
|
+
|
|
36
|
+
etcdsync etcd2json /test -e d:/.env/pymecli.env -f xxx.json
|
|
37
|
+
etcdsync json2etcd /test -e d:/.env/pymecli.env -f xxx.json
|
|
35
38
|
```
|
|
36
39
|
# quark
|
|
37
40
|
```shell
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"""
|
|
2
|
-
MySQL <-> Etcd 数据同步 CLI。
|
|
2
|
+
MySQL / JSON <-> Etcd 数据同步 CLI。
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
四个子命令:
|
|
5
5
|
mysql2etcd 将 MySQL kvs 表的 k, v 写入 etcd
|
|
6
6
|
etcd2mysql 将 etcd 指定前缀的 key/value 写入 MySQL kvs 表
|
|
7
|
+
etcd2json 将 etcd 指定前缀的 key/value 导出为 JSON 文件
|
|
8
|
+
json2etcd 将 JSON 文件中的 key/value 写入 etcd
|
|
7
9
|
"""
|
|
8
10
|
|
|
11
|
+
import json
|
|
9
12
|
import logging
|
|
10
13
|
import os
|
|
11
14
|
|
|
@@ -158,5 +161,106 @@ def etcd_to_mysql(
|
|
|
158
161
|
etcd.close()
|
|
159
162
|
|
|
160
163
|
|
|
164
|
+
@app.command("etcd2json")
|
|
165
|
+
def etcd_to_json(
|
|
166
|
+
prefix: str = typer.Argument(
|
|
167
|
+
"",
|
|
168
|
+
help="etcd key 前缀(空字符串表示全量)",
|
|
169
|
+
),
|
|
170
|
+
env_path: str = typer.Option(
|
|
171
|
+
".env",
|
|
172
|
+
"--env",
|
|
173
|
+
"-e",
|
|
174
|
+
help="dotenv 文件路径",
|
|
175
|
+
),
|
|
176
|
+
file_path: str = typer.Option(
|
|
177
|
+
...,
|
|
178
|
+
"--file-path",
|
|
179
|
+
"-f",
|
|
180
|
+
help="输出 JSON 文件路径",
|
|
181
|
+
),
|
|
182
|
+
):
|
|
183
|
+
"""将 etcd 中指定前缀的 key/value 导出为 JSON 文件。
|
|
184
|
+
|
|
185
|
+
JSON 格式为简单的 {key: value} 对象,value 均为字符串。
|
|
186
|
+
"""
|
|
187
|
+
etcd = get_etcd_client(env_path)
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
data: dict[str, str] = {}
|
|
191
|
+
iterator = etcd.get_all() if not prefix else etcd.get_prefix(prefix)
|
|
192
|
+
for value, meta in iterator:
|
|
193
|
+
k = meta.key.decode("utf-8")
|
|
194
|
+
v = value.decode("utf-8") if isinstance(value, bytes) else value
|
|
195
|
+
data[k] = v
|
|
196
|
+
|
|
197
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
198
|
+
json.dump(data, f, ensure_ascii=False, indent=2)
|
|
199
|
+
|
|
200
|
+
logger.info(f"✅ 已导出 {len(data)} 条到 {file_path}")
|
|
201
|
+
except OSError as e:
|
|
202
|
+
logger.error("写入文件失败: %s", e)
|
|
203
|
+
raise typer.Exit(1)
|
|
204
|
+
finally:
|
|
205
|
+
etcd.close()
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@app.command("json2etcd")
|
|
209
|
+
def json_to_etcd(
|
|
210
|
+
prefix: str = typer.Argument(
|
|
211
|
+
"",
|
|
212
|
+
help="写入 etcd 时给所有 key 加上的前缀(空表示不加)",
|
|
213
|
+
),
|
|
214
|
+
env_path: str = typer.Option(
|
|
215
|
+
".env",
|
|
216
|
+
"--env",
|
|
217
|
+
"-e",
|
|
218
|
+
help="dotenv 文件路径",
|
|
219
|
+
),
|
|
220
|
+
file_path: str = typer.Option(
|
|
221
|
+
...,
|
|
222
|
+
"--file-path",
|
|
223
|
+
"-f",
|
|
224
|
+
help="输入 JSON 文件路径",
|
|
225
|
+
),
|
|
226
|
+
):
|
|
227
|
+
"""将 JSON 文件中的 key/value 写入 etcd。
|
|
228
|
+
|
|
229
|
+
JSON 格式应为 {key: value} 对象,value 会被转为字符串。
|
|
230
|
+
--prefix 会加到每个 key 前面(如果 key 本身已带该前缀则不重复加)。
|
|
231
|
+
"""
|
|
232
|
+
etcd = get_etcd_client(env_path)
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
with open(file_path, encoding="utf-8") as f:
|
|
236
|
+
data = json.load(f)
|
|
237
|
+
except OSError as e:
|
|
238
|
+
logger.error("读取文件失败: %s", e)
|
|
239
|
+
raise typer.Exit(1)
|
|
240
|
+
except json.JSONDecodeError as e:
|
|
241
|
+
logger.error("JSON 解析失败: %s", e)
|
|
242
|
+
raise typer.Exit(1)
|
|
243
|
+
|
|
244
|
+
if not isinstance(data, dict):
|
|
245
|
+
logger.error("JSON 顶层必须是对象 (dict)")
|
|
246
|
+
raise typer.Exit(1)
|
|
247
|
+
|
|
248
|
+
try:
|
|
249
|
+
count = 0
|
|
250
|
+
for k, v in data.items():
|
|
251
|
+
# 处理前缀:已带则不重复加
|
|
252
|
+
full_key = k
|
|
253
|
+
if prefix and not k.startswith(prefix):
|
|
254
|
+
full_key = prefix.rstrip("/") + "/" + k.lstrip("/")
|
|
255
|
+
|
|
256
|
+
etcd.put(full_key, str(v))
|
|
257
|
+
logger.debug("put: %s -> %s", full_key, v)
|
|
258
|
+
count += 1
|
|
259
|
+
|
|
260
|
+
logger.info(f"✅ 已写入 {count} 条到 etcd")
|
|
261
|
+
finally:
|
|
262
|
+
etcd.close()
|
|
263
|
+
|
|
264
|
+
|
|
161
265
|
if __name__ == "__main__":
|
|
162
266
|
app()
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
name = "pymecli"
|
|
39
39
|
readme = "README.md"
|
|
40
40
|
requires-python = ">=3.10"
|
|
41
|
-
version = "0.6.
|
|
41
|
+
version = "0.6.2"
|
|
42
42
|
|
|
43
43
|
[project.urls]
|
|
44
44
|
Homepage = "https://pypi.org/project/pymecli"
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
bitget = "cli.bitget:app"
|
|
50
50
|
dnspod = "cli.dnspod:app"
|
|
51
51
|
dst = "cli.dst:app"
|
|
52
|
-
etcdsync = "cli.
|
|
52
|
+
etcdsync = "cli.etcd_sync:app"
|
|
53
53
|
example = "cli.example:app"
|
|
54
54
|
fast = "cli.fast:typer_app"
|
|
55
55
|
gate = "cli.gate:app"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|