pymecli 0.5.2__tar.gz → 0.5.3__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.5.2 → pymecli-0.5.3}/PKG-INFO +1 -1
- pymecli-0.5.3/core/clash.py +368 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/data/template.yaml +25 -1
- {pymecli-0.5.2 → pymecli-0.5.3}/pyproject.toml +1 -1
- pymecli-0.5.2/core/clash.py +0 -664
- {pymecli-0.5.2 → pymecli-0.5.3}/.gitignore +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/README.md +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/api/__init__.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/api/v1/__init__.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/api/v1/clash.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/api/v1/redis.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/cli/__init__.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/cli/bitget.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/cli/dst.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/cli/example.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/cli/fast.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/cli/gate.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/cli/redis_csv.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/cli/util.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/core/__init__.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/core/config.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/core/redis_client.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/crypto/__init__.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/crypto/bitget.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/crypto/gate.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/data/__init__.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/data/dou_dict.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/data/dou_list.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/data/main.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/models/__init__.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/models/douzero_model.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/models/ocr_model.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/models/response.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/utils/__init__.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/utils/elapsed.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/utils/helper.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/utils/logger.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/utils/mysql.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/utils/path.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/utils/pd.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/utils/pyredis.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/utils/sleep.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/utils/text.py +0 -0
- {pymecli-0.5.2 → pymecli-0.5.3}/utils/toml.py +0 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import requests
|
|
5
|
+
import yaml
|
|
6
|
+
from fastapi import Depends
|
|
7
|
+
|
|
8
|
+
module_dir = Path(__file__).resolve().parent.parent
|
|
9
|
+
|
|
10
|
+
TEMPLATE_URL = "https://raw.githubusercontent.com/meme2046/data/main/clash/template.yaml"
|
|
11
|
+
|
|
12
|
+
# 白名单模式最终规则
|
|
13
|
+
WHITELIST_RULES = [
|
|
14
|
+
"RULE-SET,applications,DIRECT",
|
|
15
|
+
"RULE-SET,private,DIRECT",
|
|
16
|
+
"RULE-SET,icloud,DIRECT",
|
|
17
|
+
"RULE-SET,apple,DIRECT",
|
|
18
|
+
"RULE-SET,google,全局选择",
|
|
19
|
+
"RULE-SET,proxy,全局选择",
|
|
20
|
+
"RULE-SET,direct,DIRECT",
|
|
21
|
+
"RULE-SET,lancidr,DIRECT",
|
|
22
|
+
"RULE-SET,cncidr,DIRECT",
|
|
23
|
+
"RULE-SET,telegramcidr,全局选择",
|
|
24
|
+
"GEOIP,LAN,DIRECT",
|
|
25
|
+
"GEOIP,CN,DIRECT",
|
|
26
|
+
"MATCH,全局选择",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
# 黑名单模式最终规则
|
|
30
|
+
BLACKLIST_RULES = [
|
|
31
|
+
"RULE-SET,applications,DIRECT",
|
|
32
|
+
"RULE-SET,private,DIRECT",
|
|
33
|
+
"RULE-SET,tld-not-cn,全局选择",
|
|
34
|
+
"RULE-SET,gfw,全局选择",
|
|
35
|
+
"RULE-SET,telegramcidr,全局选择",
|
|
36
|
+
"MATCH,DIRECT",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ClashConfig:
|
|
41
|
+
def __init__(self, rule_base_url: str, my_rule_base_url: str, request_proxy: str):
|
|
42
|
+
self.rule_base_url = rule_base_url.rstrip("/")
|
|
43
|
+
self.my_rule_base_url = my_rule_base_url.rstrip("/")
|
|
44
|
+
self.request_proxy = request_proxy
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ClashYamlGenerator:
|
|
48
|
+
def __init__(self, config: ClashConfig):
|
|
49
|
+
self.rule_base_url = config.rule_base_url
|
|
50
|
+
self.my_rule_base_url = config.my_rule_base_url
|
|
51
|
+
self.request_proxy = config.request_proxy
|
|
52
|
+
|
|
53
|
+
# ---------- 通用 helpers ----------
|
|
54
|
+
|
|
55
|
+
def _get_proxies(self):
|
|
56
|
+
if not self.request_proxy:
|
|
57
|
+
return None
|
|
58
|
+
return {"http": self.request_proxy, "https": self.request_proxy}
|
|
59
|
+
|
|
60
|
+
def _load_template(self, proxies=None):
|
|
61
|
+
"""从远程获取 template.yaml,失败时回退到本地文件"""
|
|
62
|
+
try:
|
|
63
|
+
response = requests.get(TEMPLATE_URL, proxies=proxies)
|
|
64
|
+
response.raise_for_status()
|
|
65
|
+
template = yaml.safe_load(response.text)
|
|
66
|
+
if template:
|
|
67
|
+
return template
|
|
68
|
+
except Exception:
|
|
69
|
+
pass
|
|
70
|
+
with open(str(module_dir / "data/template.yaml"), "r", encoding="utf-8") as f:
|
|
71
|
+
return yaml.safe_load(f)
|
|
72
|
+
|
|
73
|
+
def _fetch_subscriptions(self, sub_list, proxies):
|
|
74
|
+
"""抓取并校验所有订阅,返回 [(item, ps), ...] 和 userinfo"""
|
|
75
|
+
userinfo = ""
|
|
76
|
+
results = []
|
|
77
|
+
for item in sub_list:
|
|
78
|
+
headers = {"User-Agent": item["user_agent"]} if item["user_agent"] else {}
|
|
79
|
+
|
|
80
|
+
if not item["url"]:
|
|
81
|
+
raise ValueError("Invalid subscription URL.")
|
|
82
|
+
response = requests.get(item["url"], headers=headers, proxies=proxies)
|
|
83
|
+
response.raise_for_status()
|
|
84
|
+
if not userinfo:
|
|
85
|
+
userinfo = response.headers["Subscription-Userinfo"]
|
|
86
|
+
remote_config = yaml.safe_load(response.text)
|
|
87
|
+
|
|
88
|
+
# 检查 remote_config 是否为 None
|
|
89
|
+
if remote_config is None:
|
|
90
|
+
raise ValueError("Invalid subscription content: empty or invalid YAML.")
|
|
91
|
+
|
|
92
|
+
ps = remote_config.get("proxies", [])
|
|
93
|
+
if not ps:
|
|
94
|
+
raise ValueError("No proxies found in subscription.")
|
|
95
|
+
|
|
96
|
+
results.append((item, ps))
|
|
97
|
+
return results, userinfo
|
|
98
|
+
|
|
99
|
+
def _add_inline_rule_providers(self, template, proxies):
|
|
100
|
+
"""从 my_rule_base_url 拉取 inline 规则集并写入 template"""
|
|
101
|
+
rule_list = [
|
|
102
|
+
[f"{self.my_rule_base_url}/direct.yaml", "DIRECT"],
|
|
103
|
+
[f"{self.my_rule_base_url}/proxy.yaml", "全局选择"],
|
|
104
|
+
[f"{self.my_rule_base_url}/round.yaml", "轮询"],
|
|
105
|
+
[f"{self.my_rule_base_url}/reject.yaml", "REJECT"],
|
|
106
|
+
]
|
|
107
|
+
for url, target in rule_list:
|
|
108
|
+
response = requests.get(url, proxies=proxies)
|
|
109
|
+
response.raise_for_status()
|
|
110
|
+
remote = yaml.safe_load(response.text)
|
|
111
|
+
name = os.path.basename(url)
|
|
112
|
+
template["rule-providers"][name] = {
|
|
113
|
+
"type": "inline",
|
|
114
|
+
"behavior": "classical",
|
|
115
|
+
"payload": remote["payload"],
|
|
116
|
+
}
|
|
117
|
+
template["rules"].append(f"RULE-SET,{name},{target}")
|
|
118
|
+
|
|
119
|
+
def _http_rule_provider(self, name, behavior):
|
|
120
|
+
"""生成单个 http 类型 rule-provider"""
|
|
121
|
+
return {
|
|
122
|
+
"type": "http",
|
|
123
|
+
"format": "yaml",
|
|
124
|
+
"behavior": behavior,
|
|
125
|
+
"url": f"{self.rule_base_url}/{name}.txt",
|
|
126
|
+
"path": f"./ruleset/{name}.yaml",
|
|
127
|
+
"interval": 86400,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
def _whitelist_http_rule_providers(self):
|
|
131
|
+
return {
|
|
132
|
+
"applications": self._http_rule_provider("applications", "classical"),
|
|
133
|
+
"private": self._http_rule_provider("private", "domain"),
|
|
134
|
+
"icloud": self._http_rule_provider("icloud", "domain"),
|
|
135
|
+
"apple": self._http_rule_provider("apple", "domain"),
|
|
136
|
+
"google": self._http_rule_provider("google", "domain"),
|
|
137
|
+
"proxy": self._http_rule_provider("proxy", "domain"),
|
|
138
|
+
"direct": self._http_rule_provider("direct", "domain"),
|
|
139
|
+
"lancidr": self._http_rule_provider("lancidr", "ipcidr"),
|
|
140
|
+
"cncidr": self._http_rule_provider("cncidr", "ipcidr"),
|
|
141
|
+
"telegramcidr": self._http_rule_provider("telegramcidr", "ipcidr"),
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
def _blacklist_http_rule_providers(self):
|
|
145
|
+
return {
|
|
146
|
+
"applications": self._http_rule_provider("applications", "classical"),
|
|
147
|
+
"private": self._http_rule_provider("private", "domain"),
|
|
148
|
+
"tld-not-cn": self._http_rule_provider("tld-not-cn", "domain"),
|
|
149
|
+
"telegramcidr": self._http_rule_provider("telegramcidr", "ipcidr"),
|
|
150
|
+
"gfw": self._http_rule_provider("gfw", "domain"),
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
def _provider_proxy_groups(self, sub_list):
|
|
154
|
+
"""provider 风格的 proxy-groups(genPW / genPB)"""
|
|
155
|
+
use_list = [f"provider.{item['name']}" for item in sub_list]
|
|
156
|
+
return [
|
|
157
|
+
{
|
|
158
|
+
"name": "全局选择",
|
|
159
|
+
"type": "select",
|
|
160
|
+
"proxies": ["自动选择", "手动选择", "轮询"]
|
|
161
|
+
+ [item["name"] for item in sub_list],
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"name": "自动选择",
|
|
165
|
+
"type": "url-test",
|
|
166
|
+
"url": "https://www.gstatic.com/generate_204",
|
|
167
|
+
"interval": 300,
|
|
168
|
+
"tolerance": 11,
|
|
169
|
+
"lazy": True,
|
|
170
|
+
"use": use_list,
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"name": "手动选择",
|
|
174
|
+
"type": "select",
|
|
175
|
+
"use": use_list,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"name": "轮询",
|
|
179
|
+
"type": "load-balance",
|
|
180
|
+
"url": "https://api.bitget.com/api/v2/public/time",
|
|
181
|
+
"interval": 300,
|
|
182
|
+
"lazy": True,
|
|
183
|
+
"strategy": "round-robin",
|
|
184
|
+
"use": use_list,
|
|
185
|
+
},
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
def _inline_proxy_groups(self, sub_list):
|
|
189
|
+
"""include-all 风格的 proxy-groups(genB)"""
|
|
190
|
+
return [
|
|
191
|
+
{
|
|
192
|
+
"name": "全局选择",
|
|
193
|
+
"type": "select",
|
|
194
|
+
"proxies": ["自动选择", "手动选择", "轮询"]
|
|
195
|
+
+ [item["name"] for item in sub_list],
|
|
196
|
+
},
|
|
197
|
+
{"name": "自动选择", "type": "url-test", "include-all": True},
|
|
198
|
+
{"name": "手动选择", "type": "select", "include-all": True},
|
|
199
|
+
{
|
|
200
|
+
"name": "轮询",
|
|
201
|
+
"type": "load-balance",
|
|
202
|
+
"url": "https://api.bitget.com/api/v2/public/time",
|
|
203
|
+
"strategy": "round-robin",
|
|
204
|
+
"include-all": True,
|
|
205
|
+
},
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
def _add_provider_sub(self, template, item, ps):
|
|
209
|
+
"""单个订阅写入 proxy-providers + url-test group(genPW / genPB)"""
|
|
210
|
+
template["proxy-providers"][f"provider.{item['name']}"] = {
|
|
211
|
+
"type": "inline",
|
|
212
|
+
"payload": ps,
|
|
213
|
+
}
|
|
214
|
+
template["proxy-groups"].append(
|
|
215
|
+
{
|
|
216
|
+
"name": item["name"],
|
|
217
|
+
"type": "url-test",
|
|
218
|
+
"url": "https://www.gstatic.com/generate_204",
|
|
219
|
+
"interval": 300,
|
|
220
|
+
"tolerance": 11,
|
|
221
|
+
"lazy": True,
|
|
222
|
+
"use": [f"provider.{item['name']}"],
|
|
223
|
+
}
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def _add_inline_sub(self, template, item, ps):
|
|
227
|
+
"""单个订阅直接写入 proxies + url-test group(genB)"""
|
|
228
|
+
template["proxies"].extend(ps)
|
|
229
|
+
template["proxy-groups"].append(
|
|
230
|
+
{
|
|
231
|
+
"name": item["name"],
|
|
232
|
+
"type": "url-test",
|
|
233
|
+
"proxies": [p["name"] for p in ps],
|
|
234
|
+
}
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
def _add_base_rules(self, template, with_dst_port=False):
|
|
238
|
+
"""IP-CIDR / DOMAIN 基础规则(genPB / genB),genB 含饥荒端口"""
|
|
239
|
+
template["rules"].extend(
|
|
240
|
+
[
|
|
241
|
+
"IP-CIDR,192.168.0.0/16,DIRECT,no-resolve",
|
|
242
|
+
"IP-CIDR,10.0.0.0/8,DIRECT,no-resolve",
|
|
243
|
+
"IP-CIDR,172.16.0.0/12,DIRECT,no-resolve",
|
|
244
|
+
"IP-CIDR,127.0.0.0/8,DIRECT,no-resolve",
|
|
245
|
+
]
|
|
246
|
+
)
|
|
247
|
+
if with_dst_port:
|
|
248
|
+
template["rules"].extend(
|
|
249
|
+
[
|
|
250
|
+
"DST-PORT,10999,DIRECT",
|
|
251
|
+
"DST-PORT,10998,DIRECT",
|
|
252
|
+
"DST-PORT,27016,DIRECT",
|
|
253
|
+
"DST-PORT,27017,DIRECT",
|
|
254
|
+
"DST-PORT,8766,DIRECT",
|
|
255
|
+
"DST-PORT,8767,DIRECT",
|
|
256
|
+
]
|
|
257
|
+
)
|
|
258
|
+
template["rules"].extend(
|
|
259
|
+
[
|
|
260
|
+
"DOMAIN,clash.razord.top,DIRECT",
|
|
261
|
+
"DOMAIN,yacd.haishan.me,DIRECT",
|
|
262
|
+
# "DOMAIN,accounts.klei.com,全局选择",
|
|
263
|
+
]
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
# ---------- 三种生成模式 ----------
|
|
267
|
+
|
|
268
|
+
# 白名单模式 Rules 配置方式
|
|
269
|
+
def genPW(self, sub_list: list[dict]):
|
|
270
|
+
proxies = self._get_proxies()
|
|
271
|
+
template = self._load_template(proxies)
|
|
272
|
+
|
|
273
|
+
template["proxy-groups"].extend(self._provider_proxy_groups(sub_list))
|
|
274
|
+
|
|
275
|
+
subs, userinfo = self._fetch_subscriptions(sub_list, proxies)
|
|
276
|
+
for item, ps in subs:
|
|
277
|
+
self._add_provider_sub(template, item, ps)
|
|
278
|
+
|
|
279
|
+
self._add_inline_rule_providers(template, proxies)
|
|
280
|
+
template["rule-providers"].update(self._whitelist_http_rule_providers())
|
|
281
|
+
template["rules"].extend(WHITELIST_RULES)
|
|
282
|
+
|
|
283
|
+
return template, userinfo
|
|
284
|
+
|
|
285
|
+
# 黑名单模式 Rules 配置方式
|
|
286
|
+
def genPB(self, sub_list: list[dict]):
|
|
287
|
+
proxies = self._get_proxies()
|
|
288
|
+
template = self._load_template(proxies)
|
|
289
|
+
|
|
290
|
+
template["proxy-groups"].extend(self._provider_proxy_groups(sub_list))
|
|
291
|
+
|
|
292
|
+
subs, userinfo = self._fetch_subscriptions(sub_list, proxies)
|
|
293
|
+
for item, ps in subs:
|
|
294
|
+
self._add_provider_sub(template, item, ps)
|
|
295
|
+
|
|
296
|
+
self._add_base_rules(template, with_dst_port=False)
|
|
297
|
+
self._add_inline_rule_providers(template, proxies)
|
|
298
|
+
template["rule-providers"].update(self._blacklist_http_rule_providers())
|
|
299
|
+
template["rules"].extend(BLACKLIST_RULES)
|
|
300
|
+
|
|
301
|
+
return template, userinfo
|
|
302
|
+
|
|
303
|
+
def genB(self, sub_list: list[dict]):
|
|
304
|
+
proxies = self._get_proxies()
|
|
305
|
+
template = self._load_template(proxies)
|
|
306
|
+
|
|
307
|
+
template["proxy-groups"].extend(self._inline_proxy_groups(sub_list))
|
|
308
|
+
|
|
309
|
+
template["proxies"] = []
|
|
310
|
+
subs, userinfo = self._fetch_subscriptions(sub_list, proxies)
|
|
311
|
+
for item, ps in subs:
|
|
312
|
+
self._add_inline_sub(template, item, ps)
|
|
313
|
+
|
|
314
|
+
self._add_base_rules(template, with_dst_port=True)
|
|
315
|
+
self._add_inline_rule_providers(template, proxies)
|
|
316
|
+
template["rule-providers"].update(self._blacklist_http_rule_providers())
|
|
317
|
+
template["rules"].extend(BLACKLIST_RULES)
|
|
318
|
+
|
|
319
|
+
return template, userinfo
|
|
320
|
+
|
|
321
|
+
def query2sub(self, urls: str, agents: str, names: str):
|
|
322
|
+
url_list = urls.split(",") if urls and urls.strip() else []
|
|
323
|
+
agents_list = agents.split(",") if agents else []
|
|
324
|
+
name_list = names.split(",") if names else []
|
|
325
|
+
|
|
326
|
+
max_length = max(len(url_list), len(agents_list), len(name_list))
|
|
327
|
+
|
|
328
|
+
while len(url_list) < max_length:
|
|
329
|
+
url_list.append("")
|
|
330
|
+
while len(agents_list) < max_length:
|
|
331
|
+
agents_list.append("")
|
|
332
|
+
while len(name_list) < max_length:
|
|
333
|
+
name_list.append("")
|
|
334
|
+
|
|
335
|
+
sub_list = []
|
|
336
|
+
|
|
337
|
+
for i in range(max_length):
|
|
338
|
+
if not url_list[i]:
|
|
339
|
+
raise ValueError(f"Invalid subscription URL. #{i + 1}")
|
|
340
|
+
|
|
341
|
+
sub_list.append(
|
|
342
|
+
{
|
|
343
|
+
"url": url_list[i],
|
|
344
|
+
"user_agent": agents_list[i] if agents_list[i] else "",
|
|
345
|
+
"name": name_list[i] if name_list[i] else f"订阅{i}",
|
|
346
|
+
}
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
return sub_list
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
generator_instance = None
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def init_generator(config: ClashConfig):
|
|
356
|
+
global generator_instance
|
|
357
|
+
generator_instance = ClashYamlGenerator(config)
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def get_generator():
|
|
361
|
+
global generator_instance
|
|
362
|
+
if generator_instance is None:
|
|
363
|
+
raise RuntimeError("ClashYamlGenerator not initialized")
|
|
364
|
+
return generator_instance
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def get_generator_dependency():
|
|
368
|
+
return Depends(get_generator)
|
|
@@ -9,7 +9,7 @@ tproxy-port: 0
|
|
|
9
9
|
allow-lan: true
|
|
10
10
|
unified-delay: true
|
|
11
11
|
tcp-concurrent: false
|
|
12
|
-
log-level:
|
|
12
|
+
log-level: warning
|
|
13
13
|
find-process-mode: strict
|
|
14
14
|
bind-address: "*"
|
|
15
15
|
lan-allowed-ips:
|
|
@@ -43,6 +43,10 @@ dns:
|
|
|
43
43
|
- time.*.com
|
|
44
44
|
- ntp.*.com
|
|
45
45
|
- +.market.xiaomi.com
|
|
46
|
+
- api.memeniu.xyz
|
|
47
|
+
- meme.us.kg
|
|
48
|
+
- +.steamserver.net
|
|
49
|
+
- +.recaptcha.net
|
|
46
50
|
use-hosts: false
|
|
47
51
|
use-system-hosts: false
|
|
48
52
|
respect-rules: true
|
|
@@ -103,6 +107,26 @@ dns:
|
|
|
103
107
|
+.fastly.net:
|
|
104
108
|
- https://cloudflare-dns.com/dns-query
|
|
105
109
|
- https://dns.google/dns-query
|
|
110
|
+
api.memeniu.xyz:
|
|
111
|
+
- https://[2402:4e00::]/dns-query
|
|
112
|
+
- https://[2400:3200::1]/dns-query
|
|
113
|
+
- https://dns.alidns.com/dns-query
|
|
114
|
+
- https://doh.pub/dns-query
|
|
115
|
+
meme.us.kg:
|
|
116
|
+
- https://[2402:4e00::]/dns-query
|
|
117
|
+
- https://[2400:3200::1]/dns-query
|
|
118
|
+
- https://dns.alidns.com/dns-query
|
|
119
|
+
- https://doh.pub/dns-query
|
|
120
|
+
+.steamserver.net:
|
|
121
|
+
- https://[2402:4e00::]/dns-query
|
|
122
|
+
- https://[2400:3200::1]/dns-query
|
|
123
|
+
- https://dns.alidns.com/dns-query
|
|
124
|
+
- https://doh.pub/dns-query
|
|
125
|
+
+.recaptcha.net:
|
|
126
|
+
- https://[2402:4e00::]/dns-query
|
|
127
|
+
- https://[2400:3200::1]/dns-query
|
|
128
|
+
- https://dns.alidns.com/dns-query
|
|
129
|
+
- https://doh.pub/dns-query
|
|
106
130
|
sniffer:
|
|
107
131
|
enable: true
|
|
108
132
|
parse-pure-ip: true
|
pymecli-0.5.2/core/clash.py
DELETED
|
@@ -1,664 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
import requests
|
|
5
|
-
import yaml
|
|
6
|
-
from fastapi import Depends
|
|
7
|
-
|
|
8
|
-
module_dir = Path(__file__).resolve().parent.parent
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class ClashConfig:
|
|
12
|
-
def __init__(self, rule_base_url: str, my_rule_base_url: str, request_proxy: str):
|
|
13
|
-
self.rule_base_url = rule_base_url.rstrip("/")
|
|
14
|
-
self.my_rule_base_url = my_rule_base_url.rstrip("/")
|
|
15
|
-
self.request_proxy = request_proxy
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class ClashYamlGenerator:
|
|
19
|
-
def __init__(self, config: ClashConfig):
|
|
20
|
-
self.rule_base_url = config.rule_base_url
|
|
21
|
-
self.my_rule_base_url = config.my_rule_base_url
|
|
22
|
-
self.request_proxy = config.request_proxy
|
|
23
|
-
|
|
24
|
-
# 白名单模式 Rules 配置方式
|
|
25
|
-
def genPW(self, sub_list: list[dict]):
|
|
26
|
-
proxies = None
|
|
27
|
-
if self.request_proxy:
|
|
28
|
-
proxies = {
|
|
29
|
-
"http": self.request_proxy,
|
|
30
|
-
"https": self.request_proxy,
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
with open(str(module_dir / "data/template.yaml"), "r", encoding="utf-8") as f:
|
|
34
|
-
template = yaml.safe_load(f)
|
|
35
|
-
|
|
36
|
-
template["proxy-groups"].extend(
|
|
37
|
-
[
|
|
38
|
-
{
|
|
39
|
-
"name": "全局选择",
|
|
40
|
-
"type": "select",
|
|
41
|
-
"proxies": ["自动选择", "手动选择", "轮询"]
|
|
42
|
-
+ [item["name"] for item in sub_list],
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"name": "自动选择",
|
|
46
|
-
"type": "url-test",
|
|
47
|
-
"url": "https://www.gstatic.com/generate_204",
|
|
48
|
-
"interval": 300,
|
|
49
|
-
"tolerance": 11,
|
|
50
|
-
"lazy": True,
|
|
51
|
-
"use": [f"provider.{item['name']}" for item in sub_list],
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"name": "手动选择",
|
|
55
|
-
"type": "select",
|
|
56
|
-
"use": [f"provider.{item['name']}" for item in sub_list],
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"name": "轮询",
|
|
60
|
-
"type": "load-balance",
|
|
61
|
-
"url": "https://api.bitget.com/api/v2/public/time",
|
|
62
|
-
"interval": 300,
|
|
63
|
-
"lazy": True,
|
|
64
|
-
"strategy": "round-robin",
|
|
65
|
-
"use": [f"provider.{item['name']}" for item in sub_list],
|
|
66
|
-
},
|
|
67
|
-
]
|
|
68
|
-
)
|
|
69
|
-
userinfo = ""
|
|
70
|
-
for item in sub_list:
|
|
71
|
-
headers = {"User-Agent": item["user_agent"]} if item["user_agent"] else {}
|
|
72
|
-
|
|
73
|
-
if not item["url"]:
|
|
74
|
-
raise ValueError("Invalid subscription URL.")
|
|
75
|
-
response = requests.get(item["url"], headers=headers, proxies=proxies)
|
|
76
|
-
response.raise_for_status()
|
|
77
|
-
if not userinfo:
|
|
78
|
-
userinfo = response.headers["Subscription-Userinfo"]
|
|
79
|
-
remote_config = yaml.safe_load(response.text)
|
|
80
|
-
|
|
81
|
-
# 检查 remote_config 是否为 None
|
|
82
|
-
if remote_config is None:
|
|
83
|
-
raise ValueError("Invalid subscription content: empty or invalid YAML.")
|
|
84
|
-
|
|
85
|
-
ps = remote_config.get("proxies", [])
|
|
86
|
-
if not ps:
|
|
87
|
-
raise ValueError("No proxies found in subscription.")
|
|
88
|
-
|
|
89
|
-
template["proxy-providers"][f"provider.{item['name']}"] = {
|
|
90
|
-
"type": "inline",
|
|
91
|
-
"payload": ps,
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
template["proxy-groups"].append(
|
|
95
|
-
{
|
|
96
|
-
"name": item["name"],
|
|
97
|
-
"type": "url-test",
|
|
98
|
-
"url": "https://www.gstatic.com/generate_204",
|
|
99
|
-
"interval": 300,
|
|
100
|
-
"tolerance": 11,
|
|
101
|
-
"lazy": True,
|
|
102
|
-
"use": [f"provider.{item['name']}"],
|
|
103
|
-
},
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
# 获取rules
|
|
107
|
-
rule_list = [
|
|
108
|
-
[f"{self.my_rule_base_url}/direct.yaml", "DIRECT"],
|
|
109
|
-
[f"{self.my_rule_base_url}/proxy.yaml", "全局选择"],
|
|
110
|
-
[
|
|
111
|
-
f"{self.my_rule_base_url}/round.yaml",
|
|
112
|
-
"轮询",
|
|
113
|
-
],
|
|
114
|
-
[
|
|
115
|
-
f"{self.my_rule_base_url}/reject.yaml",
|
|
116
|
-
"REJECT",
|
|
117
|
-
],
|
|
118
|
-
]
|
|
119
|
-
|
|
120
|
-
for item in rule_list:
|
|
121
|
-
response = requests.get(item[0], proxies=proxies)
|
|
122
|
-
response.raise_for_status()
|
|
123
|
-
remote = yaml.safe_load(response.text)
|
|
124
|
-
template["rule-providers"][os.path.basename(item[0])] = {
|
|
125
|
-
"type": "inline",
|
|
126
|
-
"behavior": "classical",
|
|
127
|
-
"payload": remote["payload"],
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
template["rules"].append(f"RULE-SET,{os.path.basename(item[0])},{item[1]}")
|
|
131
|
-
|
|
132
|
-
template["rule-providers"].update(
|
|
133
|
-
{
|
|
134
|
-
"applications": {
|
|
135
|
-
"type": "http",
|
|
136
|
-
"format": "yaml",
|
|
137
|
-
"behavior": "classical",
|
|
138
|
-
"url": f"{self.rule_base_url}/applications.txt",
|
|
139
|
-
"path": "./ruleset/applications.yaml",
|
|
140
|
-
"interval": 86400,
|
|
141
|
-
},
|
|
142
|
-
"private": {
|
|
143
|
-
"type": "http",
|
|
144
|
-
"format": "yaml",
|
|
145
|
-
"behavior": "domain",
|
|
146
|
-
"url": f"{self.rule_base_url}/private.txt",
|
|
147
|
-
"path": "./ruleset/private.yaml",
|
|
148
|
-
"interval": 86400,
|
|
149
|
-
},
|
|
150
|
-
"icloud": {
|
|
151
|
-
"type": "http",
|
|
152
|
-
"format": "yaml",
|
|
153
|
-
"behavior": "domain",
|
|
154
|
-
"url": f"{self.rule_base_url}/icloud.txt",
|
|
155
|
-
"path": "./ruleset/icloud.yaml",
|
|
156
|
-
"interval": 86400,
|
|
157
|
-
},
|
|
158
|
-
"apple": {
|
|
159
|
-
"type": "http",
|
|
160
|
-
"format": "yaml",
|
|
161
|
-
"behavior": "domain",
|
|
162
|
-
"url": f"{self.rule_base_url}/apple.txt",
|
|
163
|
-
"path": "./ruleset/apple.yaml",
|
|
164
|
-
"interval": 86400,
|
|
165
|
-
},
|
|
166
|
-
"google": {
|
|
167
|
-
"type": "http",
|
|
168
|
-
"format": "yaml",
|
|
169
|
-
"behavior": "domain",
|
|
170
|
-
"url": f"{self.rule_base_url}/google.txt",
|
|
171
|
-
"path": "./ruleset/google.yaml",
|
|
172
|
-
"interval": 86400,
|
|
173
|
-
},
|
|
174
|
-
"proxy": {
|
|
175
|
-
"type": "http",
|
|
176
|
-
"format": "yaml",
|
|
177
|
-
"behavior": "domain",
|
|
178
|
-
"url": f"{self.rule_base_url}/proxy.txt",
|
|
179
|
-
"path": "./ruleset/proxy.yaml",
|
|
180
|
-
"interval": 86400,
|
|
181
|
-
},
|
|
182
|
-
"direct": {
|
|
183
|
-
"type": "http",
|
|
184
|
-
"format": "yaml",
|
|
185
|
-
"behavior": "domain",
|
|
186
|
-
"url": f"{self.rule_base_url}/direct.txt",
|
|
187
|
-
"path": "./ruleset/direct.yaml",
|
|
188
|
-
"interval": 86400,
|
|
189
|
-
},
|
|
190
|
-
"lancidr": {
|
|
191
|
-
"type": "http",
|
|
192
|
-
"format": "yaml",
|
|
193
|
-
"behavior": "ipcidr",
|
|
194
|
-
"url": f"{self.rule_base_url}/lancidr.txt",
|
|
195
|
-
"path": "./ruleset/lancidr.yaml",
|
|
196
|
-
"interval": 86400,
|
|
197
|
-
},
|
|
198
|
-
"cncidr": {
|
|
199
|
-
"type": "http",
|
|
200
|
-
"format": "yaml",
|
|
201
|
-
"behavior": "ipcidr",
|
|
202
|
-
"url": f"{self.rule_base_url}/cncidr.txt",
|
|
203
|
-
"path": "./ruleset/cncidr.yaml",
|
|
204
|
-
"interval": 86400,
|
|
205
|
-
},
|
|
206
|
-
"telegramcidr": {
|
|
207
|
-
"type": "http",
|
|
208
|
-
"format": "yaml",
|
|
209
|
-
"behavior": "ipcidr",
|
|
210
|
-
"url": f"{self.rule_base_url}/telegramcidr.txt",
|
|
211
|
-
"path": "./ruleset/telegramcidr.yaml",
|
|
212
|
-
"interval": 86400,
|
|
213
|
-
},
|
|
214
|
-
# "reject": {
|
|
215
|
-
# "type": "http",
|
|
216
|
-
# "format": "yaml",
|
|
217
|
-
# "behavior": "domain",
|
|
218
|
-
# "url": f"{self.rule_base_url}/reject.txt",
|
|
219
|
-
# "path": "./ruleset/reject.yaml",
|
|
220
|
-
# "interval": 86400,
|
|
221
|
-
# },
|
|
222
|
-
},
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
template["rules"].extend(
|
|
226
|
-
[
|
|
227
|
-
"RULE-SET,applications,DIRECT",
|
|
228
|
-
"RULE-SET,private,DIRECT",
|
|
229
|
-
# "RULE-SET,reject,REJECT",
|
|
230
|
-
"RULE-SET,icloud,DIRECT",
|
|
231
|
-
"RULE-SET,apple,DIRECT",
|
|
232
|
-
"RULE-SET,google,全局选择",
|
|
233
|
-
"RULE-SET,proxy,全局选择",
|
|
234
|
-
"RULE-SET,direct,DIRECT",
|
|
235
|
-
"RULE-SET,lancidr,DIRECT",
|
|
236
|
-
"RULE-SET,cncidr,DIRECT",
|
|
237
|
-
"RULE-SET,telegramcidr,全局选择",
|
|
238
|
-
"GEOIP,LAN,DIRECT",
|
|
239
|
-
"GEOIP,CN,DIRECT",
|
|
240
|
-
"MATCH,全局选择",
|
|
241
|
-
]
|
|
242
|
-
)
|
|
243
|
-
|
|
244
|
-
return template, userinfo
|
|
245
|
-
|
|
246
|
-
# 黑名单模式 Rules 配置方式
|
|
247
|
-
def genPB(self, sub_list: list[dict]):
|
|
248
|
-
proxies = None
|
|
249
|
-
if self.request_proxy:
|
|
250
|
-
proxies = {
|
|
251
|
-
"http": self.request_proxy,
|
|
252
|
-
"https": self.request_proxy,
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
with open(str(module_dir / "data/template.yaml"), "r", encoding="utf-8") as f:
|
|
256
|
-
template = yaml.safe_load(f)
|
|
257
|
-
|
|
258
|
-
template["proxy-groups"].extend(
|
|
259
|
-
[
|
|
260
|
-
{
|
|
261
|
-
"name": "全局选择",
|
|
262
|
-
"type": "select",
|
|
263
|
-
"proxies": ["自动选择", "手动选择", "轮询"]
|
|
264
|
-
+ [item["name"] for item in sub_list],
|
|
265
|
-
},
|
|
266
|
-
{
|
|
267
|
-
"name": "自动选择",
|
|
268
|
-
"type": "url-test",
|
|
269
|
-
"url": "https://www.gstatic.com/generate_204",
|
|
270
|
-
"interval": 300,
|
|
271
|
-
"tolerance": 11,
|
|
272
|
-
"lazy": True,
|
|
273
|
-
"use": [f"provider.{item['name']}" for item in sub_list],
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
"name": "手动选择",
|
|
277
|
-
"type": "select",
|
|
278
|
-
"use": [f"provider.{item['name']}" for item in sub_list],
|
|
279
|
-
},
|
|
280
|
-
{
|
|
281
|
-
"name": "轮询",
|
|
282
|
-
"type": "load-balance",
|
|
283
|
-
"url": "https://api.bitget.com/api/v2/public/time",
|
|
284
|
-
"interval": 300,
|
|
285
|
-
"lazy": True,
|
|
286
|
-
"strategy": "round-robin",
|
|
287
|
-
"use": [f"provider.{item['name']}" for item in sub_list],
|
|
288
|
-
},
|
|
289
|
-
]
|
|
290
|
-
)
|
|
291
|
-
|
|
292
|
-
userinfo = ""
|
|
293
|
-
for item in sub_list:
|
|
294
|
-
headers = {"User-Agent": item["user_agent"]} if item["user_agent"] else {}
|
|
295
|
-
|
|
296
|
-
if not item["url"]:
|
|
297
|
-
raise ValueError("Invalid subscription URL.")
|
|
298
|
-
response = requests.get(item["url"], headers=headers, proxies=proxies)
|
|
299
|
-
response.raise_for_status()
|
|
300
|
-
if not userinfo:
|
|
301
|
-
userinfo = response.headers["Subscription-Userinfo"]
|
|
302
|
-
remote_config = yaml.safe_load(response.text)
|
|
303
|
-
|
|
304
|
-
# 检查 remote_config 是否为 None
|
|
305
|
-
if remote_config is None:
|
|
306
|
-
raise ValueError("Invalid subscription content: empty or invalid YAML.")
|
|
307
|
-
|
|
308
|
-
ps = remote_config.get("proxies", [])
|
|
309
|
-
if not ps:
|
|
310
|
-
raise ValueError("No proxies found in subscription.")
|
|
311
|
-
|
|
312
|
-
template["proxy-providers"][f"provider.{item['name']}"] = {
|
|
313
|
-
"type": "inline",
|
|
314
|
-
"payload": ps,
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
template["proxy-groups"].append(
|
|
318
|
-
{
|
|
319
|
-
"name": item["name"],
|
|
320
|
-
"type": "url-test",
|
|
321
|
-
"url": "https://www.gstatic.com/generate_204",
|
|
322
|
-
"interval": 300,
|
|
323
|
-
"tolerance": 11,
|
|
324
|
-
"lazy": True,
|
|
325
|
-
"use": [f"provider.{item['name']}"],
|
|
326
|
-
},
|
|
327
|
-
)
|
|
328
|
-
|
|
329
|
-
template["rules"].append("IP-CIDR,192.168.0.0/16,DIRECT,no-resolve")
|
|
330
|
-
template["rules"].append("IP-CIDR,10.0.0.0/8,DIRECT,no-resolve")
|
|
331
|
-
template["rules"].append("IP-CIDR,172.16.0.0/12,DIRECT,no-resolve")
|
|
332
|
-
template["rules"].append("IP-CIDR,127.0.0.0/8,DIRECT,no-resolve")
|
|
333
|
-
# ⬇️饥荒服务器相关端口
|
|
334
|
-
# template["rules"].append("DST-PORT,10999,DIRECT")
|
|
335
|
-
# template["rules"].append("DST-PORT,10998,DIRECT")
|
|
336
|
-
# template["rules"].append("DST-PORT,27016,DIRECT")
|
|
337
|
-
# template["rules"].append("DST-PORT,27017,DIRECT")
|
|
338
|
-
# template["rules"].append("DST-PORT,8766,DIRECT")
|
|
339
|
-
# template["rules"].append("DST-PORT,8767,DIRECT")
|
|
340
|
-
|
|
341
|
-
template["rules"].append("DOMAIN,clash.razord.top,DIRECT")
|
|
342
|
-
template["rules"].append("DOMAIN,yacd.haishan.me,DIRECT")
|
|
343
|
-
# template["rules"].append("DOMAIN,accounts.klei.com,全局选择")
|
|
344
|
-
|
|
345
|
-
# 获取rules
|
|
346
|
-
rule_list = [
|
|
347
|
-
[f"{self.my_rule_base_url}/direct.yaml", "DIRECT"],
|
|
348
|
-
[f"{self.my_rule_base_url}/proxy.yaml", "全局选择"],
|
|
349
|
-
[
|
|
350
|
-
f"{self.my_rule_base_url}/round.yaml",
|
|
351
|
-
"轮询",
|
|
352
|
-
],
|
|
353
|
-
[
|
|
354
|
-
f"{self.my_rule_base_url}/reject.yaml",
|
|
355
|
-
"REJECT",
|
|
356
|
-
],
|
|
357
|
-
]
|
|
358
|
-
|
|
359
|
-
for item in rule_list:
|
|
360
|
-
response = requests.get(item[0], proxies=proxies)
|
|
361
|
-
response.raise_for_status()
|
|
362
|
-
remote = yaml.safe_load(response.text)
|
|
363
|
-
template["rule-providers"][os.path.basename(item[0])] = {
|
|
364
|
-
"type": "inline",
|
|
365
|
-
"behavior": "classical",
|
|
366
|
-
"payload": remote["payload"],
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
template["rules"].append(f"RULE-SET,{os.path.basename(item[0])},{item[1]}")
|
|
370
|
-
|
|
371
|
-
template["rule-providers"].update(
|
|
372
|
-
{
|
|
373
|
-
"applications": {
|
|
374
|
-
"type": "http",
|
|
375
|
-
"format": "yaml",
|
|
376
|
-
"behavior": "classical",
|
|
377
|
-
"url": f"{self.rule_base_url}/applications.txt",
|
|
378
|
-
"path": "./ruleset/applications.yaml",
|
|
379
|
-
"interval": 86400,
|
|
380
|
-
},
|
|
381
|
-
"private": {
|
|
382
|
-
"type": "http",
|
|
383
|
-
"format": "yaml",
|
|
384
|
-
"behavior": "domain",
|
|
385
|
-
"url": f"{self.rule_base_url}/private.txt",
|
|
386
|
-
"path": "./ruleset/private.yaml",
|
|
387
|
-
"interval": 86400,
|
|
388
|
-
},
|
|
389
|
-
"tld-not-cn": {
|
|
390
|
-
"type": "http",
|
|
391
|
-
"format": "yaml",
|
|
392
|
-
"behavior": "domain",
|
|
393
|
-
"url": f"{self.rule_base_url}/tld-not-cn.txt",
|
|
394
|
-
"path": "./ruleset/tld-not-cn.yaml",
|
|
395
|
-
"interval": 86400,
|
|
396
|
-
},
|
|
397
|
-
"telegramcidr": {
|
|
398
|
-
"type": "http",
|
|
399
|
-
"format": "yaml",
|
|
400
|
-
"behavior": "ipcidr",
|
|
401
|
-
"url": f"{self.rule_base_url}/telegramcidr.txt",
|
|
402
|
-
"path": "./ruleset/telegramcidr.yaml",
|
|
403
|
-
"interval": 86400,
|
|
404
|
-
},
|
|
405
|
-
"gfw": {
|
|
406
|
-
"type": "http",
|
|
407
|
-
"format": "yaml",
|
|
408
|
-
"behavior": "domain",
|
|
409
|
-
"url": f"{self.rule_base_url}/gfw.txt",
|
|
410
|
-
"path": "./ruleset/gfw.yaml",
|
|
411
|
-
"interval": 86400,
|
|
412
|
-
},
|
|
413
|
-
# "reject": {
|
|
414
|
-
# "type": "http",
|
|
415
|
-
# "format": "yaml",
|
|
416
|
-
# "behavior": "domain",
|
|
417
|
-
# "url": f"{self.rule_base_url}/reject.txt",
|
|
418
|
-
# "path": "./ruleset/reject.yaml",
|
|
419
|
-
# "interval": 86400,
|
|
420
|
-
# },
|
|
421
|
-
},
|
|
422
|
-
)
|
|
423
|
-
|
|
424
|
-
template["rules"].extend(
|
|
425
|
-
[
|
|
426
|
-
"RULE-SET,applications,DIRECT",
|
|
427
|
-
"RULE-SET,private,DIRECT",
|
|
428
|
-
# "RULE-SET,reject,REJECT",
|
|
429
|
-
"RULE-SET,tld-not-cn,全局选择",
|
|
430
|
-
"RULE-SET,gfw,全局选择",
|
|
431
|
-
"RULE-SET,telegramcidr,全局选择",
|
|
432
|
-
"MATCH,DIRECT",
|
|
433
|
-
]
|
|
434
|
-
)
|
|
435
|
-
|
|
436
|
-
return template, userinfo
|
|
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
|
-
|
|
617
|
-
def query2sub(self, urls: str, agents: str, names: str):
|
|
618
|
-
url_list = urls.split(",") if urls and urls.strip() else []
|
|
619
|
-
agents_list = agents.split(",") if agents else []
|
|
620
|
-
name_list = names.split(",") if names else []
|
|
621
|
-
|
|
622
|
-
max_length = max(len(url_list), len(agents_list), len(name_list))
|
|
623
|
-
|
|
624
|
-
while len(url_list) < max_length:
|
|
625
|
-
url_list.append("")
|
|
626
|
-
while len(agents_list) < max_length:
|
|
627
|
-
agents_list.append("")
|
|
628
|
-
while len(name_list) < max_length:
|
|
629
|
-
name_list.append("")
|
|
630
|
-
|
|
631
|
-
sub_list = []
|
|
632
|
-
|
|
633
|
-
for i in range(max_length):
|
|
634
|
-
if not url_list[i]:
|
|
635
|
-
raise ValueError(f"Invalid subscription URL. #{i + 1}")
|
|
636
|
-
|
|
637
|
-
sub_list.append(
|
|
638
|
-
{
|
|
639
|
-
"url": url_list[i],
|
|
640
|
-
"user_agent": agents_list[i] if agents_list[i] else "",
|
|
641
|
-
"name": name_list[i] if name_list[i] else f"订阅{i}",
|
|
642
|
-
}
|
|
643
|
-
)
|
|
644
|
-
|
|
645
|
-
return sub_list
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
generator_instance = None
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
def init_generator(config: ClashConfig):
|
|
652
|
-
global generator_instance
|
|
653
|
-
generator_instance = ClashYamlGenerator(config)
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
def get_generator():
|
|
657
|
-
global generator_instance
|
|
658
|
-
if generator_instance is None:
|
|
659
|
-
raise RuntimeError("ClashYamlGenerator not initialized")
|
|
660
|
-
return generator_instance
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
def get_generator_dependency():
|
|
664
|
-
return Depends(get_generator)
|
|
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
|