clash-linux commited on
Commit
a0e1ff5
·
verified ·
1 Parent(s): ac8610d

Upload 20 files

Browse files
Files changed (2) hide show
  1. Dockerfile +0 -3
  2. app/sub_manager.py +213 -179
Dockerfile CHANGED
@@ -99,9 +99,6 @@ RUN echo "Downloading Yacd-meta UI (gh-pages branch)..." && \
99
  rm /tmp/yacd-gh-pages.zip && \
100
  rm -rf /tmp/Yacd-meta-gh-pages
101
 
102
- # 在容器内创建空的 minimal_pref.yml 文件
103
- RUN touch /app/subconverter/minimal_pref.yml
104
-
105
  # 设置环境变量
106
  ENV PYTHONDONTWRITEBYTECODE=1 \
107
  PYTHONUNBUFFERED=1 \
 
99
  rm /tmp/yacd-gh-pages.zip && \
100
  rm -rf /tmp/Yacd-meta-gh-pages
101
 
 
 
 
102
  # 设置环境变量
103
  ENV PYTHONDONTWRITEBYTECODE=1 \
104
  PYTHONUNBUFFERED=1 \
app/sub_manager.py CHANGED
@@ -121,66 +121,131 @@ class SubscriptionManager:
121
 
122
  # 确保数据目录存在
123
  data_dir = os.path.dirname(self.config_path)
124
- os.makedirs(data_dir, exist_ok=True)
 
 
 
 
 
125
 
126
- # 准备subconverter命令 - 使用 -i/-o 并强制指定空白配置
127
- minimal_pref_path = os.path.join(os.path.dirname(self.subconverter_path), "minimal_pref.yml")
 
 
 
 
 
 
 
 
 
 
 
128
 
 
129
  cmd = [
130
  self.subconverter_path,
131
- "-i", input_file, # 输入文件
132
- "-o", self.config_path, # 输出文件
133
- "-t", "clash", # 目标格式
134
- "--config", minimal_pref_path # 强制使用空白配置,避免默认行为
 
135
  ]
136
 
137
  logger.info(f"执行命令: {' '.join(cmd)}")
138
 
139
- # 检查 subconverter 是否存在 (之前的代码已修复)
140
  if not os.path.exists(self.subconverter_path):
141
- # ... (省略 subconverter 未找到的回退逻辑) ...
142
- logger.error(f"subconverter未找到: {self.subconverter_path}. 无法转换配置。")
143
- raise RuntimeError(f"无法转换配置 (subconverter未找到)")
 
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
- # --- 正式执行 subconverter ---
146
  try:
147
  # 执行subconverter
148
  process = subprocess.Popen(
149
  cmd,
150
  stdout=subprocess.PIPE,
151
  stderr=subprocess.PIPE,
152
- universal_newlines=True,
153
- cwd=os.path.dirname(self.subconverter_path) # 尝试在subconverter目录下运行
154
  )
155
- stdout, stderr = process.communicate(timeout=60)
156
 
157
- # 记录完整的 subconverter 输出用于调试
158
- logger.info(f"subconverter STDOUT:\n---\n{stdout}\n---")
159
- if stderr:
160
- logger.warning(f"subconverter STDERR:\n---\n{stderr}\n---")
161
 
162
- # 检查 subconverter 返回码
163
  if process.returncode != 0:
164
- logger.error(f"subconverter执行失败,返回码: {process.returncode}.")
165
- # ... (省略 subconverter 失败的回退逻辑) ...
166
- raise RuntimeError(f"subconverter转换失败(code {process.returncode})。 Subconverter STDERR: {stderr[:200]}...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  else:
168
- # subconverter 返回码为 0,表示理论上成功
169
- logger.info("subconverter成功执行 (返回码 0)")
170
- # 验证输出文件是否存在且非空
171
- if os.path.exists(self.config_path) and os.path.getsize(self.config_path) > 0:
172
  logger.info(f"配置文件已生成,路径: {self.config_path},大小: {os.path.getsize(self.config_path)} 字节")
173
  else:
174
- logger.error(f"subconverter执行成功,但配置文件不存在或为空: {self.config_path}. 请检查subconverter日志了解详情。")
175
- # 检查STDERR中是否有线索
176
- error_detail = stderr if stderr else "(无错误输出)"
177
- raise RuntimeError(f"subconverter执行成功但未生成有效配置文件。 Subconverter STDERR: {error_detail[:500]}...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
- except subprocess.TimeoutExpired:
180
- logger.error(f"执行subconverter超时 (超过60秒)")
181
- raise RuntimeError("subconverter执行超时")
182
  except (subprocess.SubprocessError, OSError) as e:
183
- logger.error(f"执行subconverter时发生错误: {str(e)}")
184
  raise RuntimeError(f"配置转换失败: {str(e)}")
185
 
186
  def _add_clash_headers(self):
@@ -246,186 +311,155 @@ secret: ""
246
  # 解析配置
247
  config_yaml = yaml.safe_load(config_content)
248
 
249
- # 检查解析结果是否为有效字典
250
- if not isinstance(config_yaml, dict):
251
- logger.error("配置文件解析结果不是有效的YAML字典,无法进行修复")
252
- # 使用简单的文本分析,避免添加重复的配置
253
- has_proxy_groups = "proxy-groups:" in config_content
254
- has_global_group = "GLOBAL" in config_content or "- name: GLOBAL" in config_content
255
- if not has_proxy_groups and not has_global_group:
256
- logger.info("未检测到proxy-groups或GLOBAL策略组,添加基本的GLOBAL策略组")
257
- config_content += "\nproxy-groups:\n - name: GLOBAL\n type: select\n proxies:\n - DIRECT\n"
258
- has_patch = True
259
- else:
260
- logger.info("检测到已有proxy-groups或GLOBAL配置,跳过添加")
261
- else:
262
- # --- 开始修复逻辑 ---
263
- groups_fixed = False
264
- proxies_list = config_yaml.get("proxies")
265
-
266
- # 1. 检查代理列表是否有效
267
- if not proxies_list or not isinstance(proxies_list, list):
268
- logger.error("配置文件缺少有效的 'proxies' 列表或列表为空!将仅使用 DIRECT 代理。请检查原始订阅内容。")
269
- # 设置一个最小化的 proxies 和 proxy-groups
270
- # 不要在 proxies 中显式定义 DIRECT,Clash 内置了它
271
- config_yaml["proxies"] = []
272
- config_yaml["proxy-groups"] = [{
273
- "name": "GLOBAL",
274
- "type": "select",
275
- "proxies": ["DIRECT"] # 直接引用内置的 DIRECT
276
- }]
277
- proxy_names = {"DIRECT", "REJECT"} # 仍然需要知道内置名称
278
- groups_fixed = True # 标记需要重新生成配置内容
279
- has_patch = True
280
- else:
281
- # 2. 代理列表有效,获取所有代理节点名称
282
  proxy_names = set()
283
- valid_proxies_list = []
284
- for proxy in proxies_list:
285
  if isinstance(proxy, dict) and "name" in proxy:
286
  proxy_names.add(proxy["name"])
287
- valid_proxies_list.append(proxy) # 只保留有效的代理定义
288
- else:
289
- logger.warning(f"配置文件中的 'proxies' 列表包含无效条目: {proxy}")
290
 
291
- # 如果所有条目都无效,则回退
292
- if not valid_proxies_list:
293
- logger.error("配置文件中的 'proxies' 列表所有条目均无效!将仅使用 DIRECT 代理。")
294
- config_yaml["proxies"] = [{"name": "DIRECT", "type": "direct"}]
295
- config_yaml["proxy-groups"] = [{
296
- "name": "GLOBAL",
297
- "type": "select",
298
- "proxies": ["DIRECT"]
299
- }]
300
- proxy_names = {"DIRECT", "REJECT"}
301
- groups_fixed = True
302
- has_patch = True
303
- else:
304
- config_yaml["proxies"] = valid_proxies_list # 更新为仅包含有效代理的列表
305
- # 添加内置代理
306
- proxy_names.add("DIRECT")
307
- proxy_names.add("REJECT")
308
-
309
- # 3. 修复或添加代理组
310
- proxy_groups = config_yaml.get("proxy-groups", [])
311
- if not isinstance(proxy_groups, list):
312
- logger.warning("'proxy-groups' 不是一个列表,将重新创建")
313
- proxy_groups = []
314
- config_yaml["proxy-groups"] = proxy_groups
315
- groups_fixed = True
316
 
317
- has_global = False
318
- valid_groups = []
319
- for group in proxy_groups:
320
- if not isinstance(group, dict) or "name" not in group or "type" not in group:
321
- logger.warning(f"'proxy-groups' 中包含无效组定义: {group}")
322
- groups_fixed = True
323
- continue # 跳过无效组
324
-
325
- if "proxies" in group:
326
- if not isinstance(group["proxies"], list):
327
- logger.warning(f"代理组 '{group['name']}' 的 'proxies' 不是列表,设置为 ['DIRECT']")
328
- group["proxies"] = ["DIRECT"]
329
- groups_fixed = True
330
- else:
331
  # 删除组中引用的不存在的代理
332
- original_group_proxies = group["proxies"].copy()
333
- group["proxies"] = [p for p in group["proxies"] if p in proxy_names or p == group["name"]]
334
- if len(group["proxies"]) != len(original_group_proxies):
335
- removed = set(original_group_proxies) - set(group["proxies"])
336
- if removed:
337
- logger.warning(f"删除代理组 '{group['name']}' 中的无效引用: {', '.join(removed)}")
338
  groups_fixed = True
339
 
340
  # 确保代理组至少有一个有效代理
341
- if not group["proxies"]:
342
- logger.warning(f"为空代理组 '{group['name']}' 添加默认代理: DIRECT")
343
- group["proxies"].append("DIRECT")
344
  groups_fixed = True
345
- else:
346
- # 如果组定义没有proxies列表(例如 url-test),通常是正常的
347
- pass
348
-
349
- valid_groups.append(group)
350
- if group["name"] == "GLOBAL":
351
- has_global = True
352
 
353
- # 更新为只包含有效组定义的列表
354
- config_yaml["proxy-groups"] = valid_groups
355
-
356
- # 4. 如果没有 GLOBAL 组,添加一个
357
- if not has_global:
358
- logger.info("添加 GLOBAL 策略组")
359
- # 确定要放入 GLOBAL 组的代理 (所有已知真实代理 + DIRECT)
360
- global_proxies = [p for p in proxy_names if p not in ("DIRECT", "REJECT")]
361
- global_proxies.sort() # 可选:排序
362
- global_proxies.insert(0, "DIRECT") # 将 DIRECT 放前面
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
 
364
- config_yaml["proxy-groups"].insert(0, {
365
- "name": "GLOBAL",
366
- "type": "select",
367
- "proxies": global_proxies
368
- })
369
- groups_fixed = True
 
 
 
 
 
 
 
 
 
 
 
 
370
  has_patch = True
 
 
371
 
372
- # 5. 如果进行了修复,重新序列化 YAML
373
- if groups_fixed:
374
- try:
375
- config_content = yaml.dump(config_yaml, sort_keys=False, allow_unicode=True, default_flow_style=None)
376
- has_patch = True # 确保标记为已修改
377
- except Exception as dump_err:
378
- logger.error(f"重新序列化 YAML 失败: {dump_err}")
379
- # 序列化失败,放弃修复,依赖原始 content + 基础 patch
380
- # 重新读取原始content,只应用基础patch
381
- with open(self.config_path, "r", encoding="utf-8") as f:
382
- config_content = f.read()
383
- if "mixed-port:" not in config_content and "port:" not in config_content: config_content = "mixed-port: 7890\n" + config_content
384
- if "external-controller:" not in config_content: config_content = "external-controller: 127.0.0.1:9090\n" + config_content
385
- if "find-process-mode:" not in config_content: config_content = "find-process-mode: strict\n" + config_content
386
- if "secret:" not in config_content: config_content = "secret: ''\n" + config_content
387
- has_patch = True # 标记基础 patch 已应用
388
-
389
- # --- 处理 YAML 导入或解析错误的回退逻辑 ---
390
  except ImportError as e:
391
- logger.error(f"导入PyYAML模块失败: {str(e)}. 无法执行详细配置修复。")
392
  # 使用简单的文本分析,避免添加重复的配置
393
  has_proxy_groups = "proxy-groups:" in config_content
394
  has_global_group = "GLOBAL" in config_content or "- name: GLOBAL" in config_content
 
395
  if not has_proxy_groups and not has_global_group:
396
  logger.info("未检测到proxy-groups或GLOBAL策略组,添加基本的GLOBAL策略组")
397
- config_content += "\nproxy-groups:\n - name: GLOBAL\n type: select\n proxies:\n - DIRECT\n"
 
 
 
 
 
 
398
  has_patch = True
399
  else:
400
  logger.info("检测到已有proxy-groups或GLOBAL配置,跳过添加")
401
  except Exception as yaml_err:
402
- logger.error(f"处理YAML配置时出错: {str(yaml_err)}. 无法执行详细配置修复。")
403
  # 使用简单的文本分析,避免添加重复的配置
404
  has_proxy_groups = "proxy-groups:" in config_content
405
  has_global_group = "GLOBAL" in config_content or "- name: GLOBAL" in config_content
 
406
  if not has_proxy_groups and not has_global_group:
407
  logger.info("未检测到proxy-groups或GLOBAL策略组,添加基本的GLOBAL策略组")
408
- config_content += "\nproxy-groups:\n - name: GLOBAL\n type: select\n proxies:\n - DIRECT\n"
 
 
 
 
 
 
409
  has_patch = True
410
  else:
411
  logger.info("检测到已有proxy-groups或GLOBAL配置,跳过添加")
412
 
413
- # --- 写回文件 ---
414
  if has_patch:
415
- logger.info("检测到配置更改,正在写回文件...")
416
- try:
417
- with open(self.config_path, "w", encoding="utf-8") as f:
418
- f.write(config_content)
419
- logger.info("已修补配置文件并成功保存")
420
- except Exception as write_err:
421
- logger.error(f"写回配置文件失败: {write_err}")
422
- # 记录错误,但允许程序继续
423
- else:
424
- logger.info("配置文件无需修补")
425
 
426
- # 这个 except 对应最外层的 try
427
  except Exception as e:
428
- logger.error(f"修补配置文件过程中发生意外错误: {str(e)}")
429
 
430
  def _mask_url(self, url):
431
  """
 
121
 
122
  # 确保数据目录存在
123
  data_dir = os.path.dirname(self.config_path)
124
+ if not os.path.exists(data_dir):
125
+ logger.info(f"创建数据目录: {data_dir}")
126
+ try:
127
+ os.makedirs(data_dir, exist_ok=True)
128
+ except Exception as e:
129
+ logger.error(f"创建数据目录失败: {str(e)}")
130
 
131
+ # 尝试直接读取订阅内容,确认它是否已经是Clash配置
132
+ try:
133
+ with open(input_file, "r", encoding="utf-8") as f:
134
+ content = f.read()
135
+
136
+ # 简单检查是否已经是Clash配置
137
+ if "proxies:" in content and ("port:" in content or "mixed-port:" in content):
138
+ logger.info("检测到输入文件已是Clash配置格式,直接使用")
139
+ with open(self.config_path, "w", encoding="utf-8") as f:
140
+ f.write(content)
141
+ return
142
+ except Exception as e:
143
+ logger.warning(f"读取输入文件时出错: {str(e)},将尝试转换")
144
 
145
+ # 准备subconverter命令
146
  cmd = [
147
  self.subconverter_path,
148
+ "-g", # 生成配置文件
149
+ "--target", "clash", # 输出格式为Clash (修正为 --target)
150
+ "--url", input_file, # 修正为 --url 参数
151
+ "--output", self.config_path, # 输出文件
152
+ "--include-remarks", ".*" # 包含所有节点
153
  ]
154
 
155
  logger.info(f"执行命令: {' '.join(cmd)}")
156
 
157
+ # 如果subconverter不存在或执行出错,我们就尝试直接使用订阅内容
158
  if not os.path.exists(self.subconverter_path):
159
+ logger.warning("subconverter不存在,尝试直接使用订阅内容")
160
+ try:
161
+ with open(input_file, "r", encoding="utf-8") as f:
162
+ content = f.read()
163
+ with open(self.config_path, "w", encoding="utf-8") as f:
164
+ f.write(content)
165
+ logger.info(f"已将订阅内容直接写入到: {self.config_path}")
166
+ # 验证文件是否成功写入
167
+ if os.path.exists(self.config_path):
168
+ logger.info(f"文件已成功写入,大小: {os.path.getsize(self.config_path)} 字节")
169
+ else:
170
+ logger.error(f"文件写入失败,{self.config_path} 不存在")
171
+ except Exception as e:
172
+ logger.error(f"直接使用订阅内容时出错: {str(e)}")
173
+ raise RuntimeError(f"写入配置文件失败: {str(e)}")
174
+ return
175
 
 
176
  try:
177
  # 执行subconverter
178
  process = subprocess.Popen(
179
  cmd,
180
  stdout=subprocess.PIPE,
181
  stderr=subprocess.PIPE,
182
+ universal_newlines=True
 
183
  )
184
+ stdout, stderr = process.communicate(timeout=30)
185
 
186
+ logger.info(f"subconverter输出: {stdout[:200]}...") # 限制日志长度
 
 
 
187
 
 
188
  if process.returncode != 0:
189
+ logger.error(f"subconverter执行失败: {stderr}")
190
+ # 错误处理:尝试直接使用订阅内容
191
+ try:
192
+ with open(input_file, "r", encoding="utf-8") as f:
193
+ content = f.read()
194
+
195
+ # 确保它是有效的配置,如果是普通订阅格式,添加基本的Clash头
196
+ if "proxies:" not in content:
197
+ content = self._add_clash_headers() + content
198
+
199
+ with open(self.config_path, "w", encoding="utf-8") as f:
200
+ f.write(content)
201
+ logger.warning("尝试直接使用订阅内容作为配置文件")
202
+ # 验证文件是否成功写入
203
+ if os.path.exists(self.config_path):
204
+ logger.info(f"文件已成功写入,大小: {os.path.getsize(self.config_path)} 字节")
205
+ else:
206
+ logger.error(f"文件写入失败,{self.config_path} 不存在")
207
+ except Exception as e:
208
+ logger.error(f"使用订阅内容作为配置文件时出错: {str(e)}")
209
+ raise RuntimeError(f"写入配置文件失败: {str(e)}")
210
  else:
211
+ logger.info("成功转换配置")
212
+ # 验证输出文件是否存在
213
+ if os.path.exists(self.config_path):
 
214
  logger.info(f"配置文件已生成,路径: {self.config_path},大小: {os.path.getsize(self.config_path)} 字节")
215
  else:
216
+ # 如果文件不存在但subconverter返回成功,尝试查找配置文件
217
+ logger.warning(f"subconverter声称成功但配置文件不存在: {self.config_path}")
218
+ # 查找当前目录下可能生成的配置文件
219
+ possible_files = [f for f in os.listdir('.') if f.endswith('.yaml') or f.endswith('.yml')]
220
+ if possible_files:
221
+ logger.info(f"找到可能的配置文件: {possible_files}")
222
+ # 尝试复制找到的第一个文件
223
+ try:
224
+ import shutil
225
+ shutil.copy(possible_files[0], self.config_path)
226
+ logger.info(f"已复制 {possible_files[0]} 到 {self.config_path}")
227
+ except Exception as e:
228
+ logger.error(f"复制文件失败: {str(e)}")
229
+ else:
230
+ logger.error("未找到任何可能的配置文件")
231
+ # 尝试使用原始订阅内容作为配置
232
+ try:
233
+ with open(input_file, "r", encoding="utf-8") as f:
234
+ content = f.read()
235
+
236
+ # 确保它是有效的配置,如果是普通订阅格式,添加基本的Clash头
237
+ if "proxies:" not in content:
238
+ content = self._add_clash_headers() + content
239
+
240
+ with open(self.config_path, "w", encoding="utf-8") as f:
241
+ f.write(content)
242
+ logger.warning("使用订阅内容作为配置文件")
243
+ except Exception as e:
244
+ logger.error(f"使用订阅内容时出错: {str(e)}")
245
+ raise RuntimeError(f"写入配置文件失败: {str(e)}")
246
 
 
 
 
247
  except (subprocess.SubprocessError, OSError) as e:
248
+ logger.error(f"执行subconverter时出错: {str(e)}")
249
  raise RuntimeError(f"配置转换失败: {str(e)}")
250
 
251
  def _add_clash_headers(self):
 
311
  # 解析配置
312
  config_yaml = yaml.safe_load(config_content)
313
 
314
+ # 检查是否有代理和代理组
315
+ if config_yaml and isinstance(config_yaml, dict):
316
+ if "proxies" in config_yaml and "proxy-groups" in config_yaml:
317
+ # 获取所有代理节点名称
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  proxy_names = set()
319
+ for proxy in config_yaml.get("proxies", []):
 
320
  if isinstance(proxy, dict) and "name" in proxy:
321
  proxy_names.add(proxy["name"])
 
 
 
322
 
323
+ # 添加内置代理
324
+ proxy_names.add("DIRECT")
325
+ proxy_names.add("REJECT")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
 
327
+ # 修复代理组引用
328
+ groups_fixed = False
329
+ for group in config_yaml.get("proxy-groups", []):
330
+ if isinstance(group, dict) and "proxies" in group:
 
 
 
 
 
 
 
 
 
 
331
  # 删除组中引用的不存在的代理
332
+ valid_proxies = []
333
+ for proxy in group["proxies"]:
334
+ if proxy in proxy_names or proxy == group.get("name", ""):
335
+ valid_proxies.append(proxy)
336
+ else:
337
+ logger.warning(f"删除代理组 {group.get('name', '未知')} 中的无效引用: {proxy}")
338
  groups_fixed = True
339
 
340
  # 确保代理组至少有一个有效代理
341
+ if not valid_proxies:
342
+ valid_proxies.append("DIRECT")
343
+ logger.warning(f"为空代理组 {group.get('name', '未知')} 添加默认代理: DIRECT")
344
  groups_fixed = True
345
+
346
+ group["proxies"] = valid_proxies
 
 
 
 
 
347
 
348
+ # 检查是否有GLOBAL组,如果没有,添加一个
349
+ has_global = False
350
+ for group in config_yaml.get("proxy-groups", []):
351
+ if isinstance(group, dict) and group.get("name") == "GLOBAL":
352
+ has_global = True
353
+ break
354
+
355
+ if not has_global:
356
+ # 添加GLOBAL策略组
357
+ logger.info("添加GLOBAL策略组")
358
+ if "proxy-groups" not in config_yaml:
359
+ config_yaml["proxy-groups"] = []
360
+
361
+ # 确定要放入GLOBAL组的代理
362
+ global_proxies = ["DIRECT"]
363
+ # 添加所有代理节点到GLOBAL组
364
+ for proxy in config_yaml.get("proxies", []):
365
+ if isinstance(proxy, dict) and "name" in proxy:
366
+ proxy_name = proxy["name"]
367
+ if proxy_name != "DIRECT" and proxy_name != "REJECT":
368
+ global_proxies.append(proxy_name)
369
+
370
+ config_yaml["proxy-groups"].insert(0, {
371
+ "name": "GLOBAL",
372
+ "type": "select",
373
+ "proxies": global_proxies
374
+ })
375
+ groups_fixed = True
376
+ has_patch = True
377
+
378
+ # 如果修复了代理组,重新生成配置
379
+ if groups_fixed:
380
+ config_content = yaml.dump(config_yaml, sort_keys=False, allow_unicode=True)
381
+ has_patch = True
382
+ else:
383
+ # 配置缺少代理或代理组,添加基本结构
384
+ if "proxies" not in config_yaml:
385
+ config_yaml["proxies"] = [{"name": "DIRECT", "type": "direct"}]
386
+ has_patch = True
387
+
388
+ if "proxy-groups" not in config_yaml:
389
+ config_yaml["proxy-groups"] = [{
390
+ "name": "GLOBAL",
391
+ "type": "select",
392
+ "proxies": ["DIRECT"]
393
+ }]
394
+ has_patch = True
395
 
396
+ # 更新配置内容
397
+ config_content = yaml.dump(config_yaml, sort_keys=False, allow_unicode=True)
398
+ else:
399
+ # 配置为空或无效,添加基本配置
400
+ logger.warning("配置为空或无效,添加基本配置结构")
401
+ # 使用简单的文本分析,避免添加重复的配置
402
+ has_proxy_groups = "proxy-groups:" in config_content
403
+ has_global_group = "GLOBAL" in config_content or "- name: GLOBAL" in config_content
404
+
405
+ if not has_proxy_groups and not has_global_group:
406
+ logger.info("未检测到proxy-groups或GLOBAL策略组,添加基本的GLOBAL策略组")
407
+ config_content += """
408
+ proxy-groups:
409
+ - name: GLOBAL
410
+ type: select
411
+ proxies:
412
+ - DIRECT
413
+ """
414
  has_patch = True
415
+ else:
416
+ logger.info("检测到已有proxy-groups或GLOBAL配置,跳过添加")
417
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
  except ImportError as e:
419
+ logger.error(f"导入PyYAML模块失败: {str(e)}")
420
  # 使用简单的文本分析,避免添加重复的配置
421
  has_proxy_groups = "proxy-groups:" in config_content
422
  has_global_group = "GLOBAL" in config_content or "- name: GLOBAL" in config_content
423
+
424
  if not has_proxy_groups and not has_global_group:
425
  logger.info("未检测到proxy-groups或GLOBAL策略组,添加基本的GLOBAL策略组")
426
+ config_content += """
427
+ proxy-groups:
428
+ - name: GLOBAL
429
+ type: select
430
+ proxies:
431
+ - DIRECT
432
+ """
433
  has_patch = True
434
  else:
435
  logger.info("检测到已有proxy-groups或GLOBAL配置,跳过添加")
436
  except Exception as yaml_err:
437
+ logger.error(f"处理YAML配置时出错: {str(yaml_err)}")
438
  # 使用简单的文本分析,避免添加重复的配置
439
  has_proxy_groups = "proxy-groups:" in config_content
440
  has_global_group = "GLOBAL" in config_content or "- name: GLOBAL" in config_content
441
+
442
  if not has_proxy_groups and not has_global_group:
443
  logger.info("未检测到proxy-groups或GLOBAL策略组,添加基本的GLOBAL策略组")
444
+ config_content += """
445
+ proxy-groups:
446
+ - name: GLOBAL
447
+ type: select
448
+ proxies:
449
+ - DIRECT
450
+ """
451
  has_patch = True
452
  else:
453
  logger.info("检测到已有proxy-groups或GLOBAL配置,跳过添加")
454
 
455
+ # 如果我们修改了配置,保存回文件
456
  if has_patch:
457
+ with open(self.config_path, "w", encoding="utf-8") as f:
458
+ f.write(config_content)
459
+ logger.info("已修补配置文件以添加必要的设置")
 
 
 
 
 
 
 
460
 
 
461
  except Exception as e:
462
+ logger.error(f"修补配置文件时出错: {str(e)}")
463
 
464
  def _mask_url(self, url):
465
  """