pymecli 0.4.4__tar.gz → 0.4.6__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 (46) hide show
  1. {pymecli-0.4.4 → pymecli-0.4.6}/PKG-INFO +2 -1
  2. pymecli-0.4.6/api/v1/clash.py +113 -0
  3. {pymecli-0.4.4 → pymecli-0.4.6}/cli/fast.py +20 -11
  4. {pymecli-0.4.4 → pymecli-0.4.6}/core/clash.py +181 -9
  5. pymecli-0.4.6/core/config.py +40 -0
  6. {pymecli-0.4.4 → pymecli-0.4.6}/core/redis_client.py +1 -1
  7. {pymecli-0.4.4 → pymecli-0.4.6}/data/template.yaml +70 -70
  8. {pymecli-0.4.4 → pymecli-0.4.6}/pyproject.toml +2 -1
  9. {pymecli-0.4.4 → pymecli-0.4.6}/utils/pyredis.py +2 -2
  10. pymecli-0.4.4/api/v1/clash.py +0 -62
  11. pymecli-0.4.4/core/config.py +0 -23
  12. {pymecli-0.4.4 → pymecli-0.4.6}/.gitignore +0 -0
  13. {pymecli-0.4.4 → pymecli-0.4.6}/README.md +0 -0
  14. {pymecli-0.4.4 → pymecli-0.4.6}/api/__init__.py +0 -0
  15. {pymecli-0.4.4 → pymecli-0.4.6}/api/v1/__init__.py +0 -0
  16. {pymecli-0.4.4 → pymecli-0.4.6}/api/v1/redis.py +0 -0
  17. {pymecli-0.4.4 → pymecli-0.4.6}/cli/__init__.py +0 -0
  18. {pymecli-0.4.4 → pymecli-0.4.6}/cli/baidu.py +0 -0
  19. {pymecli-0.4.4 → pymecli-0.4.6}/cli/bitget.py +0 -0
  20. {pymecli-0.4.4 → pymecli-0.4.6}/cli/dst.py +0 -0
  21. {pymecli-0.4.4 → pymecli-0.4.6}/cli/example.py +0 -0
  22. {pymecli-0.4.4 → pymecli-0.4.6}/cli/gate.py +0 -0
  23. {pymecli-0.4.4 → pymecli-0.4.6}/cli/redis_csv.py +0 -0
  24. {pymecli-0.4.4 → pymecli-0.4.6}/cli/util.py +0 -0
  25. {pymecli-0.4.4 → pymecli-0.4.6}/core/__init__.py +0 -0
  26. {pymecli-0.4.4 → pymecli-0.4.6}/crypto/__init__.py +0 -0
  27. {pymecli-0.4.4 → pymecli-0.4.6}/crypto/bitget.py +0 -0
  28. {pymecli-0.4.4 → pymecli-0.4.6}/crypto/gate.py +0 -0
  29. {pymecli-0.4.4 → pymecli-0.4.6}/data/__init__.py +0 -0
  30. {pymecli-0.4.4 → pymecli-0.4.6}/data/dou_dict.py +0 -0
  31. {pymecli-0.4.4 → pymecli-0.4.6}/data/dou_list.py +0 -0
  32. {pymecli-0.4.4 → pymecli-0.4.6}/data/main.py +0 -0
  33. {pymecli-0.4.4 → pymecli-0.4.6}/models/__init__.py +0 -0
  34. {pymecli-0.4.4 → pymecli-0.4.6}/models/douzero_model.py +0 -0
  35. {pymecli-0.4.4 → pymecli-0.4.6}/models/ocr_model.py +0 -0
  36. {pymecli-0.4.4 → pymecli-0.4.6}/models/response.py +0 -0
  37. {pymecli-0.4.4 → pymecli-0.4.6}/utils/__init__.py +0 -0
  38. {pymecli-0.4.4 → pymecli-0.4.6}/utils/elapsed.py +0 -0
  39. {pymecli-0.4.4 → pymecli-0.4.6}/utils/helper.py +0 -0
  40. {pymecli-0.4.4 → pymecli-0.4.6}/utils/logger.py +0 -0
  41. {pymecli-0.4.4 → pymecli-0.4.6}/utils/mysql.py +0 -0
  42. {pymecli-0.4.4 → pymecli-0.4.6}/utils/path.py +0 -0
  43. {pymecli-0.4.4 → pymecli-0.4.6}/utils/pd.py +0 -0
  44. {pymecli-0.4.4 → pymecli-0.4.6}/utils/sleep.py +0 -0
  45. {pymecli-0.4.4 → pymecli-0.4.6}/utils/text.py +0 -0
  46. {pymecli-0.4.4 → pymecli-0.4.6}/utils/toml.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pymecli
3
- Version: 0.4.4
3
+ Version: 0.4.6
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
@@ -16,6 +16,7 @@ Requires-Dist: dotenv>=0.9.9
16
16
  Requires-Dist: fastapi>=0.127.0
17
17
  Requires-Dist: numpy<2.0.0
18
18
  Requires-Dist: pandas>=2.3.3
19
+ Requires-Dist: pydantic-settings>=2.14.2
19
20
  Requires-Dist: pymysql>=1.1.2
20
21
  Requires-Dist: pytz>=2025.2
21
22
  Requires-Dist: pyyaml>=6.0.3
@@ -0,0 +1,113 @@
1
+ import yaml
2
+ from fastapi import APIRouter, Query, Response
3
+
4
+ from core.clash import ClashYamlGenerator, get_generator_dependency
5
+
6
+ router = APIRouter()
7
+
8
+
9
+ class NoAnchorDumper(yaml.Dumper):
10
+ def ignore_aliases(self, data):
11
+ return True
12
+
13
+
14
+ @router.get(
15
+ "/subpw",
16
+ summary="转换订阅(可以转换多个订阅),proxy-providers方式合并多个订阅,白名单模式",
17
+ description="根据提供的URL、User-Agent和名称获取并处理订阅信息,返回YAML格式的Clash配置",
18
+ response_description="返回YAML格式的Clash配置文件",
19
+ )
20
+ async def subProviderWhite(
21
+ urls: str = Query(..., description="订阅URL,逗号分隔"),
22
+ agents: str = Query(
23
+ "clash-verge/v2.4.3",
24
+ description="User-Agent,逗号分隔(根据客户段选择)",
25
+ ),
26
+ names: str = Query(
27
+ "订阅1", description="订阅名称,逗号分隔(可选,在客户端中的显示名)"
28
+ ),
29
+ generator: ClashYamlGenerator = get_generator_dependency(),
30
+ ):
31
+ # 将JSON字符串解析为Python对象
32
+ sub_list = generator.query2sub(urls, agents, names)
33
+ yaml_content, userinfo = generator.genPW(sub_list)
34
+ yaml_string = yaml.dump(
35
+ yaml_content,
36
+ allow_unicode=True,
37
+ default_flow_style=False,
38
+ sort_keys=False,
39
+ # Dumper=NoAnchorDumper,
40
+ )
41
+ return Response(
42
+ headers={"Subscription-Userinfo": userinfo},
43
+ content=yaml_string,
44
+ media_type="text/yaml",
45
+ )
46
+
47
+
48
+ @router.get(
49
+ "/subpb",
50
+ summary="转换订阅(可以转换多个订阅),proxy-providers方式合并多个订阅, 黑名单模式",
51
+ description="根据提供的URL、User-Agent和名称获取并处理订阅信息,返回YAML格式的Clash配置",
52
+ response_description="返回YAML格式的Clash配置文件",
53
+ )
54
+ async def subProviderBlack(
55
+ urls: str = Query(..., description="订阅URL,逗号分隔"),
56
+ agents: str = Query(
57
+ "clash-verge/v2.4.3",
58
+ description="User-Agent,逗号分隔(根据客户段选择)",
59
+ ),
60
+ names: str = Query(
61
+ "订阅1", description="订阅名称,逗号分隔(可选,在客户端中的显示名)"
62
+ ),
63
+ generator: ClashYamlGenerator = get_generator_dependency(),
64
+ ):
65
+ # 将JSON字符串解析为Python对象
66
+ sub_list = generator.query2sub(urls, agents, names)
67
+ yaml_content, userinfo = generator.genPB(sub_list)
68
+ yaml_string = yaml.dump(
69
+ yaml_content,
70
+ allow_unicode=True,
71
+ default_flow_style=False,
72
+ sort_keys=False,
73
+ # Dumper=NoAnchorDumper,
74
+ )
75
+ return Response(
76
+ headers={"Subscription-Userinfo": userinfo},
77
+ content=yaml_string,
78
+ media_type="text/yaml",
79
+ )
80
+
81
+
82
+ @router.get(
83
+ "/subb",
84
+ summary="转换订阅(可以转换多个订阅),拼接proxies的方式合并多个订阅, 黑名单模式",
85
+ description="根据提供的URL、User-Agent和名称获取并处理订阅信息,返回YAML格式的Clash配置",
86
+ response_description="返回YAML格式的Clash配置文件",
87
+ )
88
+ async def subBlack(
89
+ urls: str = Query(..., description="订阅URL,逗号分隔"),
90
+ agents: str = Query(
91
+ "clash-verge/v2.4.3",
92
+ description="User-Agent,逗号分隔(根据客户段选择)",
93
+ ),
94
+ names: str = Query(
95
+ "订阅1", description="订阅名称,逗号分隔(可选,在客户端中的显示名)"
96
+ ),
97
+ generator: ClashYamlGenerator = get_generator_dependency(),
98
+ ):
99
+ # 将JSON字符串解析为Python对象
100
+ sub_list = generator.query2sub(urls, agents, names)
101
+ yaml_content, userinfo = generator.genB(sub_list)
102
+ yaml_string = yaml.dump(
103
+ yaml_content,
104
+ allow_unicode=True,
105
+ default_flow_style=False,
106
+ sort_keys=False,
107
+ # Dumper=NoAnchorDumper,
108
+ )
109
+ return Response(
110
+ headers={"Subscription-Userinfo": userinfo},
111
+ content=yaml_string,
112
+ media_type="text/yaml",
113
+ )
@@ -1,4 +1,3 @@
1
- import os
2
1
  from contextlib import asynccontextmanager
3
2
 
4
3
  import redis.asyncio as redis
@@ -20,10 +19,10 @@ from models.response import SuccessResponse
20
19
  async def lifespan(app: FastAPI):
21
20
  """管理应用生命周期的上下文管理器"""
22
21
  redis_pool = redis.ConnectionPool(
23
- host=os.getenv("REDIS_HOST", "192.168.123.7"),
24
- port=int(os.getenv("REDIS_PORT", 6379)),
25
- db=int(os.getenv("REDIS_DB", 0)),
26
- password=os.getenv("REDIS_PASSWORD"),
22
+ host=settings.REDIS_HOST,
23
+ port=settings.REDIS_PORT,
24
+ db=settings.REDIS_DB,
25
+ password=settings.REDIS_PASSWORD,
27
26
  max_connections=20, # 根据需要调整最大连接数
28
27
  decode_responses=True,
29
28
  )
@@ -41,9 +40,9 @@ typer_app = typer.Typer()
41
40
 
42
41
 
43
42
  app = FastAPI(
44
- title=settings.NAME,
45
- description=settings.DESCRIPTION,
46
- version=settings.VERSION,
43
+ title=settings.PROJECT_NAME,
44
+ description=settings.PROJECT_DESCRIPTION,
45
+ version=settings.PROJECT_VERSION,
47
46
  lifespan=lifespan,
48
47
  )
49
48
 
@@ -104,7 +103,7 @@ async def general_exception_handler(request, exc):
104
103
 
105
104
  @app.get("/")
106
105
  async def root():
107
- return SuccessResponse(data=f"Welcome to {settings.DESCRIPTION}")
106
+ return SuccessResponse(data=f"Welcome to {settings.PROJECT_DESCRIPTION}")
108
107
 
109
108
 
110
109
  @app.get("/ping", response_class=PlainTextResponse)
@@ -136,13 +135,13 @@ def run_app(
136
135
  help="ssl certfile",
137
136
  ),
138
137
  rule: str = typer.Option(
139
- "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release",
138
+ "https://fastly.jsdelivr.net/gh/Loyalsoldier/clash-rules@release",
140
139
  "--rule",
141
140
  "-r",
142
141
  help="clash Rule base URL",
143
142
  ),
144
143
  my_rule: str = typer.Option(
145
- "https://raw.githubusercontent.com/meme2046/data/refs/heads/main/clash",
144
+ "https://fastly.jsdelivr.net/gh/meme2046/data@main/clash",
146
145
  "--my-rule",
147
146
  "-mr",
148
147
  help="my clash rule base URL(自定义规则)",
@@ -153,7 +152,17 @@ def run_app(
153
152
  "-p",
154
153
  help="服务器代理,传入则通过代理转换Clash订阅,比如:socks5://127.0.0.1:7897",
155
154
  ),
155
+ redis_host: str = typer.Option(
156
+ None,
157
+ "--redis-host",
158
+ "-r",
159
+ help="redis host",
160
+ ),
156
161
  ):
162
+ settings.reload()
163
+ if redis_host is not None:
164
+ settings.REDIS_HOST = redis_host
165
+
157
166
  clash_config = ClashConfig(rule, my_rule, proxy)
158
167
  init_generator(clash_config)
159
168
 
@@ -22,7 +22,7 @@ class ClashYamlGenerator:
22
22
  self.request_proxy = config.request_proxy
23
23
 
24
24
  # 白名单模式 Rules 配置方式
25
- def genW(self, sub_list: list[dict]):
25
+ def genPW(self, sub_list: list[dict]):
26
26
  proxies = None
27
27
  if self.request_proxy:
28
28
  proxies = {
@@ -103,13 +103,6 @@ class ClashYamlGenerator:
103
103
  },
104
104
  )
105
105
 
106
- template["rules"].append("IP-CIDR,192.168.0.0/16,DIRECT,no-resolve")
107
- template["rules"].append("IP-CIDR,10.0.0.0/8,DIRECT,no-resolve")
108
- template["rules"].append("IP-CIDR,172.16.0.0/12,DIRECT,no-resolve")
109
- template["rules"].append("IP-CIDR,127.0.0.0/8,DIRECT,no-resolve")
110
-
111
- template["rules"].append("DOMAIN,clash.razord.top,DIRECT")
112
- template["rules"].append("DOMAIN,yacd.haishan.me,DIRECT")
113
106
  # 获取rules
114
107
  rule_list = [
115
108
  [f"{self.my_rule_base_url}/direct.yaml", "DIRECT"],
@@ -251,7 +244,7 @@ class ClashYamlGenerator:
251
244
  return template, userinfo
252
245
 
253
246
  # 黑名单模式 Rules 配置方式
254
- def genB(self, sub_list: list[dict]):
247
+ def genPB(self, sub_list: list[dict]):
255
248
  proxies = None
256
249
  if self.request_proxy:
257
250
  proxies = {
@@ -442,6 +435,185 @@ class ClashYamlGenerator:
442
435
 
443
436
  return template, userinfo
444
437
 
438
+ def genB(self, sub_list: list[dict]):
439
+ proxies = None
440
+ if self.request_proxy:
441
+ proxies = {
442
+ "http": self.request_proxy,
443
+ "https": self.request_proxy,
444
+ }
445
+
446
+ with open(str(module_dir / "data/template.yaml"), "r", encoding="utf-8") as f:
447
+ template = yaml.safe_load(f)
448
+
449
+ template["proxy-groups"].extend(
450
+ [
451
+ {
452
+ "name": "全局选择",
453
+ "type": "select",
454
+ "proxies": ["自动选择", "手动选择", "轮询"]
455
+ + [item["name"] for item in sub_list],
456
+ },
457
+ {
458
+ "name": "自动选择",
459
+ "type": "url-test",
460
+ "include-all": True,
461
+ },
462
+ {
463
+ "name": "手动选择",
464
+ "type": "select",
465
+ "include-all": True,
466
+ },
467
+ {
468
+ "name": "轮询",
469
+ "type": "load-balance",
470
+ "url": "https://api.bitget.com/api/v2/public/time",
471
+ "strategy": "round-robin",
472
+ "include-all": True,
473
+ },
474
+ ]
475
+ )
476
+
477
+ userinfo = ""
478
+ template["proxies"] = []
479
+ for item in sub_list:
480
+ headers = {"User-Agent": item["user_agent"]} if item["user_agent"] else {}
481
+
482
+ if not item["url"]:
483
+ raise ValueError("Invalid subscription URL.")
484
+ response = requests.get(item["url"], headers=headers, proxies=proxies)
485
+ response.raise_for_status()
486
+ if not userinfo:
487
+ userinfo = response.headers["Subscription-Userinfo"]
488
+ remote_config = yaml.safe_load(response.text)
489
+
490
+ # 检查 remote_config 是否为 None
491
+ if remote_config is None:
492
+ raise ValueError("Invalid subscription content: empty or invalid YAML.")
493
+
494
+ ps = remote_config.get("proxies", [])
495
+ if not ps:
496
+ raise ValueError("No proxies found in subscription.")
497
+
498
+ template["proxies"].extend(ps)
499
+
500
+ template["proxy-groups"].append(
501
+ {
502
+ "name": item["name"],
503
+ "type": "url-test",
504
+ "proxies": [item["name"] for item in ps],
505
+ },
506
+ )
507
+
508
+ template["rules"].append("IP-CIDR,192.168.0.0/16,DIRECT,no-resolve")
509
+ template["rules"].append("IP-CIDR,10.0.0.0/8,DIRECT,no-resolve")
510
+ template["rules"].append("IP-CIDR,172.16.0.0/12,DIRECT,no-resolve")
511
+ template["rules"].append("IP-CIDR,127.0.0.0/8,DIRECT,no-resolve")
512
+ # ⬇️饥荒服务器相关端口
513
+ template["rules"].append("DST-PORT,10999,DIRECT")
514
+ template["rules"].append("DST-PORT,10998,DIRECT")
515
+ template["rules"].append("DST-PORT,27016,DIRECT")
516
+ template["rules"].append("DST-PORT,27017,DIRECT")
517
+ template["rules"].append("DST-PORT,8766,DIRECT")
518
+ template["rules"].append("DST-PORT,8767,DIRECT")
519
+
520
+ template["rules"].append("DOMAIN,clash.razord.top,DIRECT")
521
+ template["rules"].append("DOMAIN,yacd.haishan.me,DIRECT")
522
+ # template["rules"].append("DOMAIN,accounts.klei.com,全局选择")
523
+
524
+ # 获取rules
525
+ rule_list = [
526
+ [f"{self.my_rule_base_url}/direct.yaml", "DIRECT"],
527
+ [f"{self.my_rule_base_url}/proxy.yaml", "全局选择"],
528
+ [
529
+ f"{self.my_rule_base_url}/round.yaml",
530
+ "轮询",
531
+ ],
532
+ [
533
+ f"{self.my_rule_base_url}/reject.yaml",
534
+ "REJECT",
535
+ ],
536
+ ]
537
+
538
+ for item in rule_list:
539
+ response = requests.get(item[0], proxies=proxies)
540
+ response.raise_for_status()
541
+ remote = yaml.safe_load(response.text)
542
+ template["rule-providers"][os.path.basename(item[0])] = {
543
+ "type": "inline",
544
+ "behavior": "classical",
545
+ "payload": remote["payload"],
546
+ }
547
+
548
+ template["rules"].append(f"RULE-SET,{os.path.basename(item[0])},{item[1]}")
549
+
550
+ template["rule-providers"].update(
551
+ {
552
+ "applications": {
553
+ "type": "http",
554
+ "format": "yaml",
555
+ "behavior": "classical",
556
+ "url": f"{self.rule_base_url}/applications.txt",
557
+ "path": "./ruleset/applications.yaml",
558
+ "interval": 86400,
559
+ },
560
+ "private": {
561
+ "type": "http",
562
+ "format": "yaml",
563
+ "behavior": "domain",
564
+ "url": f"{self.rule_base_url}/private.txt",
565
+ "path": "./ruleset/private.yaml",
566
+ "interval": 86400,
567
+ },
568
+ "tld-not-cn": {
569
+ "type": "http",
570
+ "format": "yaml",
571
+ "behavior": "domain",
572
+ "url": f"{self.rule_base_url}/tld-not-cn.txt",
573
+ "path": "./ruleset/tld-not-cn.yaml",
574
+ "interval": 86400,
575
+ },
576
+ "telegramcidr": {
577
+ "type": "http",
578
+ "format": "yaml",
579
+ "behavior": "ipcidr",
580
+ "url": f"{self.rule_base_url}/telegramcidr.txt",
581
+ "path": "./ruleset/telegramcidr.yaml",
582
+ "interval": 86400,
583
+ },
584
+ "gfw": {
585
+ "type": "http",
586
+ "format": "yaml",
587
+ "behavior": "domain",
588
+ "url": f"{self.rule_base_url}/gfw.txt",
589
+ "path": "./ruleset/gfw.yaml",
590
+ "interval": 86400,
591
+ },
592
+ # "reject": {
593
+ # "type": "http",
594
+ # "format": "yaml",
595
+ # "behavior": "domain",
596
+ # "url": f"{self.rule_base_url}/reject.txt",
597
+ # "path": "./ruleset/reject.yaml",
598
+ # "interval": 86400,
599
+ # },
600
+ },
601
+ )
602
+
603
+ template["rules"].extend(
604
+ [
605
+ "RULE-SET,applications,DIRECT",
606
+ "RULE-SET,private,DIRECT",
607
+ # "RULE-SET,reject,REJECT",
608
+ "RULE-SET,tld-not-cn,全局选择",
609
+ "RULE-SET,gfw,全局选择",
610
+ "RULE-SET,telegramcidr,全局选择",
611
+ "MATCH,DIRECT",
612
+ ]
613
+ )
614
+
615
+ return template, userinfo
616
+
445
617
  def query2sub(self, urls: str, agents: str, names: str):
446
618
  url_list = urls.split(",") if urls and urls.strip() else []
447
619
  agents_list = agents.split(",") if agents else []
@@ -0,0 +1,40 @@
1
+ import importlib.metadata
2
+
3
+ from pydantic_settings import BaseSettings
4
+
5
+ metadata = importlib.metadata.metadata("pymecli")
6
+ # module_dir = Path(__file__).resolve().parent.parent
7
+
8
+ # project = read_toml(str(module_dir / "./pyproject.toml"))["project"]
9
+
10
+
11
+ class Settings(BaseSettings):
12
+ # API配置
13
+ API_V1_STR: str = "/api/v1"
14
+ PROJECT_NAME: str = metadata["Name"]
15
+
16
+ REDIS_HOST: str = "192.168.124.7"
17
+ REDIS_PORT: int = 6379
18
+ REDIS_DB: int = 0
19
+ REDIS_PASSWORD: str = ""
20
+
21
+ PROJECT_DESCRIPTION: str = (
22
+ f"{metadata['Summary']}, FastAPI提供: clash订阅转换、baidu.gushitong api"
23
+ )
24
+ PROJECT_VERSION: str = metadata["Version"]
25
+
26
+ class Config:
27
+ env_prefix = "PYME_CLI_" # 添加环境变量前缀
28
+ case_sensitive = True
29
+
30
+ def reload(self):
31
+ new_settings = Settings()
32
+ for field in Settings.model_fields:
33
+ setattr(self, field, getattr(new_settings, field))
34
+
35
+
36
+ settings = Settings()
37
+
38
+ print(f"project: {settings.PROJECT_NAME}")
39
+ print(f"version: {settings.PROJECT_VERSION}")
40
+ print(f"description: {settings.PROJECT_DESCRIPTION}")
@@ -8,7 +8,7 @@ import redis
8
8
  class RedisClient:
9
9
  def __init__(
10
10
  self,
11
- host=os.getenv("REDIS_HOST", "192.168.123.7"),
11
+ host=os.getenv("REDIS_HOST", "192.168.124.7"),
12
12
  port=int(os.getenv("REDIS_PORT", 6379)),
13
13
  db=int(os.getenv("REDIS_DB", 0)),
14
14
  password=os.getenv("REDIS_PASSWORD"),
@@ -1,70 +1,70 @@
1
- mixed-port: 7897
2
- allow-lan: false
3
- mode: rule
4
- log-level: warning
5
- external-controller: "127.0.0.1:9097"
6
- client-fingerprint: chrome
7
- unified-delay: true
8
- ipv6: false
9
- profile:
10
- store-selected: true
11
- tun:
12
- mtu: 1500
13
- dns:
14
- enable: true
15
- listen: ":53"
16
- enhanced-mode: "fake-ip"
17
- fake-ip-range: "198.18.0.1/16"
18
- fake-ip-filter-mode: "blacklist"
19
- prefer-h3: false
20
- respect-rules: false
21
- use-hosts: false
22
- use-system-hosts: false
23
- ipv6: true
24
- fake-ip-filter:
25
- - "*.lan"
26
- - "*.local"
27
- - "*.arpa"
28
- - "time.*.com"
29
- - "ntp.*.com"
30
- - "time.*.com"
31
- - "+.market.xiaomi.com"
32
- - "localhost.ptlogin2.qq.com"
33
- - "*.msftncsi.com"
34
- - "www.msftconnecttest.com"
35
- default-nameserver:
36
- - "system"
37
- - "223.6.6.6"
38
- - "8.8.8.8"
39
- - "2400:3200::1"
40
- - "2001:4860:4860::8888"
41
- nameserver:
42
- - "8.8.8.8"
43
- - "https://doh.pub/dns-query"
44
- - "https://dns.alidns.com/dns-query"
45
- direct-nameserver-follow-policy: false
46
- fallback-filter:
47
- geoip: true
48
- geoip-code: "CN"
49
- ipcidr:
50
- - "240.0.0.0/4"
51
- - "0.0.0.0/32"
52
- domain:
53
- - "+.google.com"
54
- - "+.facebook.com"
55
- - "+.youtube.com"
56
- fallback: []
57
- proxy-server-nameserver:
58
- - "https://doh.pub/dns-query"
59
- - "https://dns.alidns.com/dns-query"
60
- - "tls://223.5.5.5"
61
- direct-nameserver: []
62
- external-controller-cors:
63
- allow-private-network: true
64
- allow-origins:
65
- - "*"
66
- proxies: []
67
- proxy-providers: {}
68
- proxy-groups: []
69
- rule-providers: {}
70
- rules: []
1
+ mixed-port: 7897
2
+ allow-lan: false
3
+ mode: rule
4
+ log-level: warning
5
+ external-controller: "127.0.0.1:9097"
6
+ client-fingerprint: chrome
7
+ unified-delay: true
8
+ ipv6: false
9
+ profile:
10
+ store-selected: true
11
+ tun:
12
+ mtu: 1500
13
+ dns:
14
+ enable: true
15
+ listen: ":53"
16
+ enhanced-mode: "fake-ip"
17
+ fake-ip-range: "198.18.0.1/16"
18
+ fake-ip-filter-mode: "blacklist"
19
+ prefer-h3: false
20
+ respect-rules: false
21
+ use-hosts: false
22
+ use-system-hosts: false
23
+ ipv6: true
24
+ fake-ip-filter:
25
+ - "*.lan"
26
+ - "*.local"
27
+ - "*.arpa"
28
+ - "time.*.com"
29
+ - "ntp.*.com"
30
+ - "time.*.com"
31
+ - "+.market.xiaomi.com"
32
+ - "localhost.ptlogin2.qq.com"
33
+ - "*.msftncsi.com"
34
+ - "www.msftconnecttest.com"
35
+ default-nameserver:
36
+ - "system"
37
+ - "223.6.6.6"
38
+ - "8.8.8.8"
39
+ - "2400:3200::1"
40
+ - "2001:4860:4860::8888"
41
+ nameserver:
42
+ - "8.8.8.8"
43
+ - "https://doh.pub/dns-query"
44
+ - "https://dns.alidns.com/dns-query"
45
+ direct-nameserver-follow-policy: false
46
+ fallback-filter:
47
+ geoip: true
48
+ geoip-code: "CN"
49
+ ipcidr:
50
+ - "240.0.0.0/4"
51
+ - "0.0.0.0/32"
52
+ domain:
53
+ - "+.google.com"
54
+ - "+.facebook.com"
55
+ - "+.youtube.com"
56
+ fallback: []
57
+ proxy-server-nameserver:
58
+ - "https://doh.pub/dns-query"
59
+ - "https://dns.alidns.com/dns-query"
60
+ - "tls://223.5.5.5"
61
+ direct-nameserver: []
62
+ external-controller-cors:
63
+ allow-private-network: true
64
+ allow-origins:
65
+ - "*"
66
+ proxies: []
67
+ proxy-providers: {}
68
+ proxy-groups: []
69
+ rule-providers: {}
70
+ rules: []
@@ -26,13 +26,14 @@
26
26
  "typer>=0.20.1",
27
27
  "uvicorn>=0.40.0",
28
28
  "webdriver-manager>=4.0.2",
29
+ "pydantic-settings>=2.14.2",
29
30
  ]
30
31
  description = "My CLI"
31
32
  license = { text = "MIT" }
32
33
  name = "pymecli"
33
34
  readme = "README.md"
34
35
  requires-python = ">=3.10"
35
- version = "0.4.4"
36
+ version = "0.4.6"
36
37
 
37
38
  [project.urls]
38
39
  Homepage = "https://pypi.org/project/pymecli"
@@ -5,7 +5,7 @@ import redis.asyncio as redis
5
5
 
6
6
 
7
7
  def get_redis_client(
8
- host=os.getenv("REDIS_HOST", "192.168.123.7"),
8
+ host=os.getenv("REDIS_HOST", "192.168.124.7"),
9
9
  port=int(os.getenv("REDIS_PORT", 6379)),
10
10
  db=int(os.getenv("REDIS_DB", 0)),
11
11
  password=os.getenv("REDIS_PASSWORD"),
@@ -21,7 +21,7 @@ def get_redis_client(
21
21
 
22
22
 
23
23
  def get_redis_client_sync(
24
- host=os.getenv("REDIS_HOST", "192.168.123.7"),
24
+ host=os.getenv("REDIS_HOST", "192.168.124.7"),
25
25
  port=int(os.getenv("REDIS_PORT", 6379)),
26
26
  db=int(os.getenv("REDIS_DB", 0)),
27
27
  password=os.getenv("REDIS_PASSWORD"),
@@ -1,62 +0,0 @@
1
- import yaml
2
- from fastapi import APIRouter, Query, Response
3
-
4
- from core.clash import ClashYamlGenerator, get_generator_dependency
5
-
6
- router = APIRouter()
7
-
8
-
9
- @router.get(
10
- "/subwhite",
11
- summary="转换订阅(可以转换多个订阅)",
12
- description="根据提供的URL、User-Agent和名称获取并处理订阅信息,返回YAML格式的Clash配置",
13
- response_description="返回YAML格式的Clash配置文件",
14
- )
15
- async def subWhite(
16
- urls: str = Query(..., description="订阅URL,逗号分隔"),
17
- agents: str = Query(
18
- "clash-verge/v2.4.3",
19
- description="User-Agent,逗号分隔(根据客户段选择)",
20
- ),
21
- names: str = Query(
22
- "订阅1", description="订阅名称,逗号分隔(可选,在客户端中的显示名)"
23
- ),
24
- generator: ClashYamlGenerator = get_generator_dependency(),
25
- ):
26
- # 将JSON字符串解析为Python对象
27
- sub_list = generator.query2sub(urls, agents, names)
28
- yaml_content, userinfo = generator.genW(sub_list)
29
- yaml_string = yaml.dump(yaml_content, allow_unicode=True, default_flow_style=False)
30
- return Response(
31
- headers={"Subscription-Userinfo": userinfo},
32
- content=yaml_string,
33
- media_type="text/yaml",
34
- )
35
-
36
-
37
- @router.get(
38
- "/subblack",
39
- summary="转换订阅(可以转换多个订阅)",
40
- description="根据提供的URL、User-Agent和名称获取并处理订阅信息,返回YAML格式的Clash配置",
41
- response_description="返回YAML格式的Clash配置文件",
42
- )
43
- async def subBlack(
44
- urls: str = Query(..., description="订阅URL,逗号分隔"),
45
- agents: str = Query(
46
- "clash-verge/v2.4.3",
47
- description="User-Agent,逗号分隔(根据客户段选择)",
48
- ),
49
- names: str = Query(
50
- "订阅1", description="订阅名称,逗号分隔(可选,在客户端中的显示名)"
51
- ),
52
- generator: ClashYamlGenerator = get_generator_dependency(),
53
- ):
54
- # 将JSON字符串解析为Python对象
55
- sub_list = generator.query2sub(urls, agents, names)
56
- yaml_content, userinfo = generator.genB(sub_list)
57
- yaml_string = yaml.dump(yaml_content, allow_unicode=True, default_flow_style=False)
58
- return Response(
59
- headers={"Subscription-Userinfo": userinfo},
60
- content=yaml_string,
61
- media_type="text/yaml",
62
- )
@@ -1,23 +0,0 @@
1
- import importlib.metadata
2
-
3
- metadata = importlib.metadata.metadata("pymecli")
4
- # module_dir = Path(__file__).resolve().parent.parent
5
-
6
- # project = read_toml(str(module_dir / "./pyproject.toml"))["project"]
7
-
8
-
9
- class Settings:
10
- # API配置
11
- API_V1_STR: str = "/api/v1"
12
- NAME: str = metadata["Name"]
13
- DESCRIPTION: str = (
14
- f"{metadata['Summary']}, FastAPI提供: clash订阅转换、baidu.gushitong api"
15
- )
16
- VERSION: str = metadata["Version"]
17
-
18
- print(f"project: {NAME}")
19
- print(f"version: {VERSION}")
20
- print(f"description: {DESCRIPTION}")
21
-
22
-
23
- settings = Settings()
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