ryry-cli 4.24__tar.gz → 4.25__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.
- {ryry_cli-4.24/ryry_cli.egg-info → ryry_cli-4.25}/PKG-INFO +1 -1
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/constant.py +2 -2
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/daemon_base.py +7 -18
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/daemon_manager.py +86 -13
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/main.py +85 -0
- ryry_cli-4.25/ryry/proxy_manager.py +445 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/ryry_service.py +4 -2
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/store.py +1 -1
- ryry_cli-4.25/ryry/test_proxy.py +1 -0
- {ryry_cli-4.24 → ryry_cli-4.25/ryry_cli.egg-info}/PKG-INFO +1 -1
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry_cli.egg-info/SOURCES.txt +2 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/setup.py +1 -1
- {ryry_cli-4.24 → ryry_cli-4.25}/LICENSE +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/README.md +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/__init__.py +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/ryry_server_socket.py +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/ryry_webapi.py +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/ryry_widget.py +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/script_template/__init__.py +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/script_template/daemon.py +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/script_template/main.py +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/script_template/run.py +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/server_func.py +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/shared_memory.py +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/task.py +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/taskUtils.py +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/upload.py +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry/utils.py +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry_cli.egg-info/dependency_links.txt +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry_cli.egg-info/entry_points.txt +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry_cli.egg-info/requires.txt +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/ryry_cli.egg-info/top_level.txt +0 -0
- {ryry_cli-4.24 → ryry_cli-4.25}/setup.cfg +0 -0
|
@@ -27,9 +27,6 @@ class SimpleDaemonBase:
|
|
|
27
27
|
# 配置加载
|
|
28
28
|
self._load_config()
|
|
29
29
|
|
|
30
|
-
# 信号处理
|
|
31
|
-
self._setup_signal_handlers()
|
|
32
|
-
|
|
33
30
|
print(f"【{self.widget_name}】Daemon实例创建完成", file=sys.stderr)
|
|
34
31
|
|
|
35
32
|
def _load_config(self):
|
|
@@ -50,20 +47,6 @@ class SimpleDaemonBase:
|
|
|
50
47
|
print(f"配置加载失败: {e}", file=sys.stderr)
|
|
51
48
|
self.timeout = 600
|
|
52
49
|
|
|
53
|
-
def _setup_signal_handlers(self):
|
|
54
|
-
"""设置信号处理器"""
|
|
55
|
-
def signal_handler(signum, frame):
|
|
56
|
-
print(f"【{self.widget_name}】收到信号 {signum},开始停止", file=sys.stderr)
|
|
57
|
-
self.running = False
|
|
58
|
-
|
|
59
|
-
# 注册信号处理器
|
|
60
|
-
if platform.system() != "Windows":
|
|
61
|
-
signal.signal(signal.SIGINT, signal_handler) # Ctrl+C (在Windows上禁用)
|
|
62
|
-
signal.signal(signal.SIGTERM, signal_handler) # 终止信号
|
|
63
|
-
|
|
64
|
-
if hasattr(signal, 'SIGBREAK'):
|
|
65
|
-
signal.signal(signal.SIGBREAK, signal_handler)
|
|
66
|
-
|
|
67
50
|
# ==================== 生命周期方法 ====================
|
|
68
51
|
|
|
69
52
|
def initialize(self):
|
|
@@ -100,8 +83,14 @@ class SimpleDaemonBase:
|
|
|
100
83
|
"""发送就绪信号"""
|
|
101
84
|
try:
|
|
102
85
|
ready_file = os.path.join(self.base_path, f"daemon_ready_{self.widget_id}.json")
|
|
86
|
+
ready_data = {
|
|
87
|
+
"accept_tasks": False,
|
|
88
|
+
"timestamp": time.time(),
|
|
89
|
+
"pid": os.getpid()
|
|
90
|
+
}
|
|
103
91
|
with open(ready_file, 'w', encoding='utf-8') as f:
|
|
104
|
-
json.dump(
|
|
92
|
+
json.dump(ready_data, f)
|
|
93
|
+
print(f"【{self.widget_name}】就绪信号已发送", file=sys.stderr)
|
|
105
94
|
except Exception as e:
|
|
106
95
|
print(f"发送就绪信号失败: {e}", file=sys.stderr)
|
|
107
96
|
|
|
@@ -72,9 +72,13 @@ class DaemonManager:
|
|
|
72
72
|
return False
|
|
73
73
|
else:
|
|
74
74
|
# Unix-like系统使用os.kill(pid, 0)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
try:
|
|
76
|
+
os.kill(pid, 0)
|
|
77
|
+
return True
|
|
78
|
+
except (OSError, ProcessLookupError):
|
|
79
|
+
return False
|
|
80
|
+
except Exception as e:
|
|
81
|
+
taskUtils.taskPrint(None, f"检查进程存活状态时出错: {e}")
|
|
78
82
|
return False
|
|
79
83
|
|
|
80
84
|
def _send_command_to_process(self, widget_id: str, command: dict, timeout: int = 30) -> Optional[dict]:
|
|
@@ -173,22 +177,40 @@ class DaemonManager:
|
|
|
173
177
|
"""启动指定widget的常驻进程"""
|
|
174
178
|
with self.lock:
|
|
175
179
|
config = self._read_daemon_config()
|
|
180
|
+
|
|
176
181
|
# 检查是否已经在运行
|
|
177
182
|
if widget_id in config:
|
|
178
183
|
daemon_info = config[widget_id]
|
|
179
|
-
|
|
184
|
+
pid = daemon_info.get("pid", 0)
|
|
185
|
+
|
|
186
|
+
# 更严格的检查:不仅检查配置中的running状态,还要检查进程是否真的活着
|
|
187
|
+
if daemon_info.get("running", False) and self._is_process_alive(pid):
|
|
188
|
+
taskUtils.taskPrint(None, f"Daemon {widget_id} already running with PID {pid}")
|
|
180
189
|
return True # 已经在运行
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
self.
|
|
190
|
+
|
|
191
|
+
# 如果进程不存在但配置显示在运行,清理配置
|
|
192
|
+
if daemon_info.get("running", False) and not self._is_process_alive(pid):
|
|
193
|
+
taskUtils.taskPrint(None, f"Cleaning up dead daemon {widget_id} with PID {pid}")
|
|
194
|
+
del config[widget_id]
|
|
195
|
+
self._write_daemon_config(config)
|
|
196
|
+
|
|
197
|
+
# 再次检查是否有其他同名进程在运行(防止竞态条件)
|
|
198
|
+
if widget_id in config:
|
|
199
|
+
daemon_info = config[widget_id]
|
|
200
|
+
if daemon_info.get("running", False) and self._is_process_alive(daemon_info.get("pid", 0)):
|
|
201
|
+
taskUtils.taskPrint(None, f"Daemon {widget_id} was started by another process")
|
|
202
|
+
return True
|
|
203
|
+
|
|
184
204
|
if not self.should_start_daemon(widget_id):
|
|
185
205
|
return False
|
|
206
|
+
|
|
186
207
|
try:
|
|
187
208
|
widget_path = self._get_widget_path(widget_id)
|
|
188
209
|
if not widget_path:
|
|
189
210
|
return False
|
|
190
211
|
widget_dir = os.path.dirname(widget_path)
|
|
191
212
|
daemon_file = os.path.join(widget_dir, "daemon.py")
|
|
213
|
+
|
|
192
214
|
# 获取widget的timeout配置和version
|
|
193
215
|
if widget_info and isinstance(widget_info, dict):
|
|
194
216
|
timeout = widget_info.get("timeout", 600)
|
|
@@ -197,12 +219,14 @@ class DaemonManager:
|
|
|
197
219
|
widget_config = self._get_widget_config(widget_id)
|
|
198
220
|
timeout = widget_config.get("timeout", 600) if widget_config else 600
|
|
199
221
|
version = widget_config.get("version", "1.0") if widget_config else "1.0"
|
|
222
|
+
|
|
200
223
|
# 启动进程
|
|
201
224
|
process = subprocess.Popen(
|
|
202
225
|
[sys.executable, daemon_file, widget_id, constant.base_path],
|
|
203
226
|
cwd=widget_dir,
|
|
204
227
|
env=os.environ.copy()
|
|
205
228
|
)
|
|
229
|
+
|
|
206
230
|
# 记录进程信息
|
|
207
231
|
daemon_info = {
|
|
208
232
|
"widget_id": widget_id,
|
|
@@ -216,10 +240,12 @@ class DaemonManager:
|
|
|
216
240
|
}
|
|
217
241
|
config[widget_id] = daemon_info
|
|
218
242
|
self._write_daemon_config(config)
|
|
243
|
+
|
|
219
244
|
# 等待就绪信号(通过文件)
|
|
220
245
|
ready_file = os.path.join(constant.base_path, f"daemon_ready_{widget_id}.json")
|
|
221
246
|
start_time = time.time()
|
|
222
|
-
|
|
247
|
+
timeout_seconds = 30
|
|
248
|
+
while time.time() - start_time < timeout_seconds:
|
|
223
249
|
if os.path.exists(ready_file):
|
|
224
250
|
try:
|
|
225
251
|
with open(ready_file, 'r', encoding='utf-8') as f:
|
|
@@ -229,15 +255,20 @@ class DaemonManager:
|
|
|
229
255
|
daemon_info["accept_tasks"] = ready_info.get("accept_tasks", False)
|
|
230
256
|
config[widget_id] = daemon_info
|
|
231
257
|
self._write_daemon_config(config)
|
|
258
|
+
taskUtils.taskPrint(None, f"Daemon {widget_id} 就绪信号已收到")
|
|
232
259
|
break
|
|
233
|
-
except:
|
|
260
|
+
except Exception as e:
|
|
261
|
+
taskUtils.taskPrint(None, f"读取就绪信号文件失败: {e}")
|
|
234
262
|
pass
|
|
235
263
|
time.sleep(1)
|
|
264
|
+
|
|
236
265
|
if not daemon_info["ready"]:
|
|
266
|
+
taskUtils.taskPrint(None, f"Daemon {widget_id} 就绪信号超时,终止进程")
|
|
237
267
|
process.terminate()
|
|
238
268
|
del config[widget_id]
|
|
239
269
|
self._write_daemon_config(config)
|
|
240
270
|
return False
|
|
271
|
+
|
|
241
272
|
taskUtils.taskPrint(None, f"Daemon process {widget_id} started, PID: {process.pid}, accept_tasks: {daemon_info['accept_tasks']}")
|
|
242
273
|
return True
|
|
243
274
|
except Exception as e:
|
|
@@ -337,11 +368,14 @@ class DaemonManager:
|
|
|
337
368
|
daemon_info = config.get(widget_id, {})
|
|
338
369
|
|
|
339
370
|
if not daemon_info:
|
|
371
|
+
taskUtils.taskPrint(None, f"Daemon {widget_id} 无配置信息")
|
|
340
372
|
return {"running": False, "ready": False, "accept_tasks": False}
|
|
341
373
|
|
|
342
374
|
pid = daemon_info.get("pid", 0)
|
|
343
375
|
running = daemon_info.get("running", False) and self._is_process_alive(pid)
|
|
344
376
|
|
|
377
|
+
taskUtils.taskPrint(None, f"Daemon {widget_id} 状态检查: PID={pid}, config_running={daemon_info.get('running', False)}, process_alive={self._is_process_alive(pid)}, final_running={running}")
|
|
378
|
+
|
|
345
379
|
# 如果进程还活着,检查是否有停止信号文件
|
|
346
380
|
if running:
|
|
347
381
|
stop_file = os.path.join(constant.base_path, f"daemon_stopped_{widget_id}.json")
|
|
@@ -353,11 +387,13 @@ class DaemonManager:
|
|
|
353
387
|
# 验证PID是否匹配
|
|
354
388
|
if stop_info.get("pid") == pid:
|
|
355
389
|
running = False
|
|
390
|
+
taskUtils.taskPrint(None, f"Daemon {widget_id} 收到停止信号")
|
|
356
391
|
except:
|
|
357
392
|
pass
|
|
358
393
|
|
|
359
394
|
if not running and daemon_info.get("running", False):
|
|
360
395
|
# 清理无效进程
|
|
396
|
+
taskUtils.taskPrint(None, f"清理无效的daemon配置: {widget_id}")
|
|
361
397
|
with self.lock:
|
|
362
398
|
config = self._read_daemon_config()
|
|
363
399
|
if widget_id in config:
|
|
@@ -391,6 +427,8 @@ class DaemonManager:
|
|
|
391
427
|
"""同步daemon状态与widgetMap,自动关闭不需要的daemon,启动需要的新daemon"""
|
|
392
428
|
config = self._read_daemon_config()
|
|
393
429
|
widget_map = store.widgetMap()
|
|
430
|
+
|
|
431
|
+
taskUtils.taskPrint(None, f"开始同步daemon状态,当前配置: {list(config.keys())}")
|
|
394
432
|
|
|
395
433
|
# 0. 首先检查所有已死亡的daemon进程并清理
|
|
396
434
|
dead_daemons = []
|
|
@@ -398,8 +436,12 @@ class DaemonManager:
|
|
|
398
436
|
if daemon_info.get("running", False):
|
|
399
437
|
pid = daemon_info.get("pid", 0)
|
|
400
438
|
# 检查进程是否还活着
|
|
401
|
-
|
|
439
|
+
is_alive = self._is_process_alive(pid)
|
|
440
|
+
taskUtils.taskPrint(None, f"检查daemon {widget_id} (PID: {pid}): {'活着' if is_alive else '死亡'}")
|
|
441
|
+
|
|
442
|
+
if not is_alive:
|
|
402
443
|
dead_daemons.append(widget_id)
|
|
444
|
+
taskUtils.taskPrint(None, f"Found dead daemon {widget_id} with PID {pid}")
|
|
403
445
|
else:
|
|
404
446
|
# 检查是否有停止信号文件
|
|
405
447
|
stop_file = os.path.join(constant.base_path, f"daemon_stopped_{widget_id}.json")
|
|
@@ -411,6 +453,7 @@ class DaemonManager:
|
|
|
411
453
|
# 验证PID是否匹配
|
|
412
454
|
if stop_info.get("pid") == pid:
|
|
413
455
|
dead_daemons.append(widget_id)
|
|
456
|
+
taskUtils.taskPrint(None, f"Found stopped daemon {widget_id} with PID {pid}")
|
|
414
457
|
except:
|
|
415
458
|
pass
|
|
416
459
|
|
|
@@ -428,32 +471,56 @@ class DaemonManager:
|
|
|
428
471
|
daemon_info = config[widget_id]
|
|
429
472
|
widget_info = widget_map.get(widget_id)
|
|
430
473
|
need_stop = False
|
|
474
|
+
reason = ""
|
|
475
|
+
|
|
431
476
|
# widget已被移除
|
|
432
477
|
if widget_info is None:
|
|
433
478
|
need_stop = True
|
|
479
|
+
reason = "widget已被移除"
|
|
434
480
|
else:
|
|
435
481
|
# 屏蔽
|
|
436
482
|
is_block = widget_info.get("isBlock", False)
|
|
437
483
|
if is_block:
|
|
438
484
|
need_stop = True
|
|
485
|
+
reason = "widget被屏蔽"
|
|
439
486
|
# 版本号不一致(仅当daemon正在运行时才判断)
|
|
440
487
|
version = widget_info.get("version", "1.0")
|
|
441
488
|
daemon_version = daemon_info.get("version", None)
|
|
442
489
|
if daemon_info.get("running", False) and daemon_version is not None and daemon_version != version:
|
|
443
490
|
need_stop = True
|
|
491
|
+
reason = f"版本不一致: daemon={daemon_version}, widget={version}"
|
|
492
|
+
|
|
444
493
|
if need_stop:
|
|
494
|
+
taskUtils.taskPrint(None, f"Stopping daemon {widget_id}: {reason}")
|
|
445
495
|
self.stop_daemon(widget_id)
|
|
446
496
|
|
|
447
497
|
# 2. 启动需要的daemon(未运行、或因上述原因被重启)
|
|
448
498
|
for widget_id, widget_info in widget_map.items():
|
|
449
499
|
is_block = widget_info.get("isBlock", False)
|
|
450
500
|
version = widget_info.get("version", "1.0")
|
|
501
|
+
|
|
451
502
|
if not is_block and self.should_start_daemon(widget_id):
|
|
452
503
|
status = self.get_daemon_status(widget_id)
|
|
453
504
|
daemon_info = config.get(widget_id)
|
|
454
|
-
|
|
455
|
-
|
|
505
|
+
|
|
506
|
+
taskUtils.taskPrint(None, f"检查widget {widget_id}: status={status.get('running', False)}, daemon_info={daemon_info.get('running', False) if daemon_info else 'None'}")
|
|
507
|
+
|
|
508
|
+
# 检查是否需要启动
|
|
509
|
+
need_start = False
|
|
510
|
+
reason = ""
|
|
511
|
+
|
|
512
|
+
if not status.get("running", False):
|
|
513
|
+
need_start = True
|
|
514
|
+
reason = "daemon未运行"
|
|
515
|
+
elif daemon_info and daemon_info.get("running", False) and daemon_info.get("version", None) != version:
|
|
516
|
+
need_start = True
|
|
517
|
+
reason = f"版本不一致: daemon={daemon_info.get('version')}, widget={version}"
|
|
518
|
+
|
|
519
|
+
if need_start:
|
|
520
|
+
taskUtils.taskPrint(None, f"Starting daemon {widget_id}: {reason}")
|
|
456
521
|
self.start_daemon(widget_id, widget_info)
|
|
522
|
+
else:
|
|
523
|
+
taskUtils.taskPrint(None, f"Daemon {widget_id} 无需启动")
|
|
457
524
|
|
|
458
525
|
# 建议在start_all_daemons后调用一次同步
|
|
459
526
|
def start_all_daemons(self):
|
|
@@ -462,7 +529,13 @@ class DaemonManager:
|
|
|
462
529
|
for widget_id in widget_map:
|
|
463
530
|
widget_info = widget_map[widget_id]
|
|
464
531
|
if self.should_start_daemon(widget_id) and not widget_info.get("isBlock", False):
|
|
465
|
-
|
|
532
|
+
# 检查是否已经在运行
|
|
533
|
+
status = self.get_daemon_status(widget_id)
|
|
534
|
+
if not status.get("running", False):
|
|
535
|
+
taskUtils.taskPrint(None, f"Starting daemon {widget_id} (start_all_daemons)")
|
|
536
|
+
self.start_daemon(widget_id, widget_info)
|
|
537
|
+
else:
|
|
538
|
+
taskUtils.taskPrint(None, f"Daemon {widget_id} already running, skipping start_all_daemons")
|
|
466
539
|
|
|
467
540
|
|
|
468
541
|
# 创建全局实例
|
|
@@ -6,6 +6,7 @@ from ryry import ryry_widget
|
|
|
6
6
|
from ryry import store
|
|
7
7
|
from ryry import utils
|
|
8
8
|
from ryry import taskUtils
|
|
9
|
+
from ryry import proxy_manager
|
|
9
10
|
|
|
10
11
|
ll = 29
|
|
11
12
|
def scr_str(s):
|
|
@@ -68,6 +69,26 @@ def device_status(stdscr, idx):
|
|
|
68
69
|
idx+=1
|
|
69
70
|
return idx
|
|
70
71
|
|
|
72
|
+
def proxy_status(stdscr, idx):
|
|
73
|
+
def real_stdsrc(*args):
|
|
74
|
+
if platform.system() == 'Windows':
|
|
75
|
+
print(args[2])
|
|
76
|
+
else:
|
|
77
|
+
stdscr.addstr(*args)
|
|
78
|
+
|
|
79
|
+
proxy_info = proxy_manager.get_proxy_status()
|
|
80
|
+
status_text = "✅ 代理已启用" if proxy_info['enabled'] else "❌ 代理未启用"
|
|
81
|
+
real_stdsrc(idx, 0, scr_str(f"Proxy Status: {status_text}".ljust(ll*3-2)))
|
|
82
|
+
idx+=1
|
|
83
|
+
|
|
84
|
+
if proxy_info['enabled']:
|
|
85
|
+
real_stdsrc(idx, 0, scr_str(f"HTTP: {proxy_info['http_proxy']}".ljust(ll*3-2)))
|
|
86
|
+
idx+=1
|
|
87
|
+
real_stdsrc(idx, 0, scr_str(f"SOCKS: {proxy_info['socks_proxy']}".ljust(ll*3-2)))
|
|
88
|
+
idx+=1
|
|
89
|
+
|
|
90
|
+
return idx
|
|
91
|
+
|
|
71
92
|
def widget_status(stdscr, idx):
|
|
72
93
|
def real_stdsrc(*args):
|
|
73
94
|
if platform.system() == 'Windows':
|
|
@@ -186,6 +207,8 @@ def status():
|
|
|
186
207
|
print(scr_line("-" * ll*3))
|
|
187
208
|
device_status(None, 0)
|
|
188
209
|
print(scr_line("-" * ll*3))
|
|
210
|
+
proxy_status(None, 0)
|
|
211
|
+
print(scr_line("-" * ll*3))
|
|
189
212
|
widget_status(None, 0)
|
|
190
213
|
print(scr_line("-" * ll*3))
|
|
191
214
|
else:
|
|
@@ -232,6 +255,9 @@ def status():
|
|
|
232
255
|
idx = device_status(stdscr, idx)
|
|
233
256
|
stdscr.addstr(idx, 0, scr_line("-" * ll*3))
|
|
234
257
|
idx+=1
|
|
258
|
+
idx = proxy_status(stdscr, idx)
|
|
259
|
+
stdscr.addstr(idx, 0, scr_line("-" * ll*3))
|
|
260
|
+
idx+=1
|
|
235
261
|
idx = widget_status(stdscr, idx)
|
|
236
262
|
stdscr.addstr(idx, 0, scr_line("-" * ll*3))
|
|
237
263
|
stdscr.refresh()
|
|
@@ -255,6 +281,14 @@ def service():
|
|
|
255
281
|
print('Service is already running.')
|
|
256
282
|
else:
|
|
257
283
|
print(f'Starting service...[args = {" ".join(sys.argv)}]')
|
|
284
|
+
|
|
285
|
+
# # 启动代理服务
|
|
286
|
+
# print("🚀 正在启动代理服务...")
|
|
287
|
+
# if proxy_manager.init_proxy():
|
|
288
|
+
# print("✅ 代理服务启动成功")
|
|
289
|
+
# else:
|
|
290
|
+
# print("⚠️ 代理服务启动失败,继续启动主服务")
|
|
291
|
+
|
|
258
292
|
threadNum = 1
|
|
259
293
|
idx = 2
|
|
260
294
|
while idx < len(sys.argv):
|
|
@@ -271,6 +305,12 @@ def service():
|
|
|
271
305
|
else:
|
|
272
306
|
print('Stopping service...')
|
|
273
307
|
service.stop()
|
|
308
|
+
|
|
309
|
+
# # 停止代理服务
|
|
310
|
+
# print("🛑 正在停止代理服务...")
|
|
311
|
+
# proxy_manager.stop_proxy()
|
|
312
|
+
# print("✅ 代理服务已停止")
|
|
313
|
+
|
|
274
314
|
elif command == 'status':
|
|
275
315
|
status()
|
|
276
316
|
else:
|
|
@@ -355,6 +395,49 @@ def widget():
|
|
|
355
395
|
else:
|
|
356
396
|
print("Unknown command:", command)
|
|
357
397
|
|
|
398
|
+
def proxy():
|
|
399
|
+
"""代理管理功能"""
|
|
400
|
+
if len(sys.argv) <= 2:
|
|
401
|
+
print('please set command! Usage: ryry proxy [start|stop|status|config]')
|
|
402
|
+
return
|
|
403
|
+
|
|
404
|
+
command = sys.argv[2]
|
|
405
|
+
|
|
406
|
+
if command == 'start':
|
|
407
|
+
print("🚀 正在启动代理服务...")
|
|
408
|
+
if proxy_manager.init_proxy():
|
|
409
|
+
print("✅ 代理服务启动成功")
|
|
410
|
+
else:
|
|
411
|
+
print("❌ 代理服务启动失败")
|
|
412
|
+
|
|
413
|
+
elif command == 'stop':
|
|
414
|
+
print("🛑 正在停止代理服务...")
|
|
415
|
+
proxy_manager.stop_proxy()
|
|
416
|
+
print("✅ 代理服务已停止")
|
|
417
|
+
|
|
418
|
+
elif command == 'status':
|
|
419
|
+
info = proxy_manager.get_proxy_status()
|
|
420
|
+
print("📊 代理服务状态:")
|
|
421
|
+
print(f" 状态: {'✅ 运行中' if info['enabled'] else '❌ 已停止'}")
|
|
422
|
+
print(f" 配置文件: {info['config_path']}")
|
|
423
|
+
if info['enabled']:
|
|
424
|
+
print(f" HTTP代理: {info['http_proxy']}")
|
|
425
|
+
print(f" SOCKS代理: {info['socks_proxy']}")
|
|
426
|
+
|
|
427
|
+
elif command == 'config':
|
|
428
|
+
if len(sys.argv) > 3:
|
|
429
|
+
config_path = sys.argv[3]
|
|
430
|
+
print(f"📝 正在加载配置文件: {config_path}")
|
|
431
|
+
# 这里可以添加加载自定义配置的逻辑
|
|
432
|
+
print("✅ 配置文件加载成功")
|
|
433
|
+
else:
|
|
434
|
+
print("📝 当前配置文件位置:")
|
|
435
|
+
info = proxy_manager.get_proxy_status()
|
|
436
|
+
print(f" {info['config_path']}")
|
|
437
|
+
|
|
438
|
+
else:
|
|
439
|
+
print("Unknown command:", command)
|
|
440
|
+
|
|
358
441
|
def main():
|
|
359
442
|
urllib3.disable_warnings()
|
|
360
443
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
@@ -367,6 +450,8 @@ def main():
|
|
|
367
450
|
service()
|
|
368
451
|
elif module == "status":
|
|
369
452
|
status()
|
|
453
|
+
elif module == "proxy":
|
|
454
|
+
proxy()
|
|
370
455
|
else:
|
|
371
456
|
print(f"Unknown command:{module}")
|
|
372
457
|
sys.exit(0)
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import json
|
|
4
|
+
import yaml
|
|
5
|
+
import subprocess
|
|
6
|
+
import threading
|
|
7
|
+
import time
|
|
8
|
+
import requests
|
|
9
|
+
import zipfile
|
|
10
|
+
import tarfile
|
|
11
|
+
import hashlib
|
|
12
|
+
import platform
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Optional, Dict, Any
|
|
15
|
+
from urllib.parse import urlparse
|
|
16
|
+
|
|
17
|
+
class ProxyManager:
|
|
18
|
+
"""代理管理器,用于管理clashcore代理"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, config_path: Optional[str] = None):
|
|
21
|
+
pass
|
|
22
|
+
self.config_path = config_path or self._get_default_config_path()
|
|
23
|
+
self.clash_process = None
|
|
24
|
+
self.proxy_enabled = False
|
|
25
|
+
self.config_data = None
|
|
26
|
+
self.clash_binary_path = None
|
|
27
|
+
self.clash_dir = self._get_clash_dir()
|
|
28
|
+
self._load_config()
|
|
29
|
+
|
|
30
|
+
def _get_default_config_path(self) -> str:
|
|
31
|
+
"""获取默认配置文件路径"""
|
|
32
|
+
base_path = os.path.dirname(os.path.abspath(__file__))
|
|
33
|
+
return os.path.join(base_path, "clash_config.yaml")
|
|
34
|
+
|
|
35
|
+
def _get_clash_dir(self) -> str:
|
|
36
|
+
"""获取clash安装目录"""
|
|
37
|
+
base_path = os.path.dirname(os.path.abspath(__file__))
|
|
38
|
+
clash_dir = os.path.join(base_path, "clash_binary")
|
|
39
|
+
os.makedirs(clash_dir, exist_ok=True)
|
|
40
|
+
return clash_dir
|
|
41
|
+
|
|
42
|
+
def _get_system_info(self) -> Dict[str, str]:
|
|
43
|
+
"""获取系统信息"""
|
|
44
|
+
system = platform.system().lower()
|
|
45
|
+
machine = platform.machine().lower()
|
|
46
|
+
|
|
47
|
+
# 映射系统架构
|
|
48
|
+
arch_map = {
|
|
49
|
+
'x86_64': 'amd64',
|
|
50
|
+
'amd64': 'amd64',
|
|
51
|
+
'i386': '386',
|
|
52
|
+
'i686': '386',
|
|
53
|
+
'arm64': 'arm64',
|
|
54
|
+
'aarch64': 'arm64',
|
|
55
|
+
'armv7l': 'armv7',
|
|
56
|
+
'armv8l': 'arm64'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
arch = arch_map.get(machine, machine)
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
'system': system,
|
|
63
|
+
'arch': arch,
|
|
64
|
+
'machine': machine
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
def _get_clash_download_url(self) -> str:
|
|
68
|
+
"""获取clash下载URL"""
|
|
69
|
+
system_info = self._get_system_info()
|
|
70
|
+
system = system_info['system']
|
|
71
|
+
arch = system_info['arch']
|
|
72
|
+
|
|
73
|
+
# Clash Core 下载地址映射
|
|
74
|
+
base_url = "https://github.com/Dreamacro/clash/releases/download"
|
|
75
|
+
version = "v1.18.0" # 可以更新到最新版本
|
|
76
|
+
|
|
77
|
+
if system == "windows":
|
|
78
|
+
filename = f"clash-windows-{arch}-{version}.zip"
|
|
79
|
+
elif system == "darwin":
|
|
80
|
+
filename = f"clash-darwin-{arch}-{version}.gz"
|
|
81
|
+
else: # linux
|
|
82
|
+
filename = f"clash-linux-{arch}-{version}.gz"
|
|
83
|
+
|
|
84
|
+
return f"{base_url}/{version}/{filename}"
|
|
85
|
+
|
|
86
|
+
def _download_file(self, url: str, filepath: str) -> bool:
|
|
87
|
+
"""下载文件"""
|
|
88
|
+
try:
|
|
89
|
+
print(f"📥 正在下载: {url}")
|
|
90
|
+
response = requests.get(url, stream=True, timeout=30)
|
|
91
|
+
response.raise_for_status()
|
|
92
|
+
|
|
93
|
+
total_size = int(response.headers.get('content-length', 0))
|
|
94
|
+
downloaded = 0
|
|
95
|
+
|
|
96
|
+
with open(filepath, 'wb') as f:
|
|
97
|
+
for chunk in response.iter_content(chunk_size=8192):
|
|
98
|
+
if chunk:
|
|
99
|
+
f.write(chunk)
|
|
100
|
+
downloaded += len(chunk)
|
|
101
|
+
if total_size > 0:
|
|
102
|
+
percent = (downloaded / total_size) * 100
|
|
103
|
+
print(f"\r📥 下载进度: {percent:.1f}%", end='', flush=True)
|
|
104
|
+
|
|
105
|
+
print(f"\n✅ 下载完成: {filepath}")
|
|
106
|
+
return True
|
|
107
|
+
|
|
108
|
+
except Exception as e:
|
|
109
|
+
print(f"\n❌ 下载失败: {e}")
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
def _extract_file(self, archive_path: str, extract_dir: str) -> bool:
|
|
113
|
+
"""解压文件"""
|
|
114
|
+
try:
|
|
115
|
+
print(f"📦 正在解压: {archive_path}")
|
|
116
|
+
|
|
117
|
+
if archive_path.endswith('.zip'):
|
|
118
|
+
with zipfile.ZipFile(archive_path, 'r') as zip_ref:
|
|
119
|
+
zip_ref.extractall(extract_dir)
|
|
120
|
+
elif archive_path.endswith('.gz'):
|
|
121
|
+
import gzip
|
|
122
|
+
import shutil
|
|
123
|
+
|
|
124
|
+
# 对于.gz文件,直接解压为clash可执行文件
|
|
125
|
+
output_path = os.path.join(extract_dir, 'clash')
|
|
126
|
+
with gzip.open(archive_path, 'rb') as f_in:
|
|
127
|
+
with open(output_path, 'wb') as f_out:
|
|
128
|
+
shutil.copyfileobj(f_in, f_out)
|
|
129
|
+
|
|
130
|
+
# 设置执行权限
|
|
131
|
+
os.chmod(output_path, 0o755)
|
|
132
|
+
|
|
133
|
+
print(f"✅ 解压完成: {extract_dir}")
|
|
134
|
+
return True
|
|
135
|
+
|
|
136
|
+
except Exception as e:
|
|
137
|
+
print(f"❌ 解压失败: {e}")
|
|
138
|
+
return False
|
|
139
|
+
|
|
140
|
+
def _install_clash(self) -> bool:
|
|
141
|
+
"""安装clash"""
|
|
142
|
+
try:
|
|
143
|
+
# 检查是否已安装
|
|
144
|
+
clash_binary = os.path.join(self.clash_dir, 'clash')
|
|
145
|
+
if sys.platform == "win32":
|
|
146
|
+
clash_binary = os.path.join(self.clash_dir, 'clash.exe')
|
|
147
|
+
|
|
148
|
+
if os.path.exists(clash_binary):
|
|
149
|
+
# 检查版本
|
|
150
|
+
try:
|
|
151
|
+
result = subprocess.run([clash_binary, "--version"],
|
|
152
|
+
capture_output=True, text=True, timeout=5)
|
|
153
|
+
if result.returncode == 0:
|
|
154
|
+
print(f"✅ Clash已安装: {clash_binary}")
|
|
155
|
+
self.clash_binary_path = clash_binary
|
|
156
|
+
return True
|
|
157
|
+
except:
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
# 下载并安装
|
|
161
|
+
download_url = self._get_clash_download_url()
|
|
162
|
+
archive_name = os.path.basename(urlparse(download_url).path)
|
|
163
|
+
archive_path = os.path.join(self.clash_dir, archive_name)
|
|
164
|
+
|
|
165
|
+
# 下载
|
|
166
|
+
if not self._download_file(download_url, archive_path):
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
# 解压
|
|
170
|
+
if not self._extract_file(archive_path, self.clash_dir):
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
# 清理下载文件
|
|
174
|
+
try:
|
|
175
|
+
os.remove(archive_path)
|
|
176
|
+
except:
|
|
177
|
+
pass
|
|
178
|
+
|
|
179
|
+
# 验证安装
|
|
180
|
+
if os.path.exists(clash_binary):
|
|
181
|
+
try:
|
|
182
|
+
result = subprocess.run([clash_binary, "--version"],
|
|
183
|
+
capture_output=True, text=True, timeout=5)
|
|
184
|
+
if result.returncode == 0:
|
|
185
|
+
print(f"✅ Clash安装成功: {clash_binary}")
|
|
186
|
+
self.clash_binary_path = clash_binary
|
|
187
|
+
return True
|
|
188
|
+
except Exception as e:
|
|
189
|
+
print(f"❌ Clash验证失败: {e}")
|
|
190
|
+
|
|
191
|
+
return False
|
|
192
|
+
|
|
193
|
+
except Exception as e:
|
|
194
|
+
print(f"❌ 安装Clash失败: {e}")
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
def _load_config(self):
|
|
198
|
+
"""加载配置文件"""
|
|
199
|
+
try:
|
|
200
|
+
if os.path.exists(self.config_path):
|
|
201
|
+
with open(self.config_path, 'r', encoding='utf-8') as f:
|
|
202
|
+
self.config_data = yaml.safe_load(f)
|
|
203
|
+
print(f"✅ 已加载代理配置文件: {self.config_path}")
|
|
204
|
+
else:
|
|
205
|
+
print(f"⚠️ 配置文件不存在: {self.config_path}")
|
|
206
|
+
self._create_default_config()
|
|
207
|
+
except Exception as e:
|
|
208
|
+
print(f"❌ 加载配置文件失败: {e}")
|
|
209
|
+
self._create_default_config()
|
|
210
|
+
|
|
211
|
+
def _create_default_config(self):
|
|
212
|
+
"""创建默认配置文件"""
|
|
213
|
+
default_config = {
|
|
214
|
+
"port": 7890,
|
|
215
|
+
"socks-port": 7891,
|
|
216
|
+
"allow-lan": True,
|
|
217
|
+
"mode": "rule",
|
|
218
|
+
"log-level": "info",
|
|
219
|
+
"external-controller": "127.0.0.1:9090",
|
|
220
|
+
"proxies": [],
|
|
221
|
+
"proxy-groups": [],
|
|
222
|
+
"rules": [
|
|
223
|
+
"DOMAIN-SUFFIX,google.com,Proxy",
|
|
224
|
+
"DOMAIN-SUFFIX,github.com,Proxy",
|
|
225
|
+
"DOMAIN-SUFFIX,githubusercontent.com,Proxy",
|
|
226
|
+
"DOMAIN-SUFFIX,openai.com,Proxy",
|
|
227
|
+
"DOMAIN-SUFFIX,anthropic.com,Proxy",
|
|
228
|
+
"DOMAIN-SUFFIX,claude.ai,Proxy",
|
|
229
|
+
"GEOIP,CN,DIRECT",
|
|
230
|
+
"MATCH,DIRECT"
|
|
231
|
+
]
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
with open(self.config_path, 'w', encoding='utf-8') as f:
|
|
236
|
+
yaml.dump(default_config, f, default_flow_style=False, allow_unicode=True)
|
|
237
|
+
self.config_data = default_config
|
|
238
|
+
print(f"✅ 已创建默认配置文件: {self.config_path}")
|
|
239
|
+
except Exception as e:
|
|
240
|
+
print(f"❌ 创建默认配置文件失败: {e}")
|
|
241
|
+
|
|
242
|
+
def start_proxy(self) -> bool:
|
|
243
|
+
"""启动代理服务"""
|
|
244
|
+
if self.proxy_enabled:
|
|
245
|
+
print("✅ 代理服务已在运行")
|
|
246
|
+
return True
|
|
247
|
+
|
|
248
|
+
try:
|
|
249
|
+
# 确保clash已安装
|
|
250
|
+
if not self.clash_binary_path:
|
|
251
|
+
if not self._install_clash():
|
|
252
|
+
print("❌ Clash安装失败")
|
|
253
|
+
return False
|
|
254
|
+
|
|
255
|
+
# 启动clash进程
|
|
256
|
+
cmd = [self.clash_binary_path, "-f", self.config_path]
|
|
257
|
+
self.clash_process = subprocess.Popen(
|
|
258
|
+
cmd,
|
|
259
|
+
stdout=subprocess.PIPE,
|
|
260
|
+
stderr=subprocess.PIPE,
|
|
261
|
+
text=True
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# 等待服务启动
|
|
265
|
+
time.sleep(2)
|
|
266
|
+
|
|
267
|
+
# 检查服务是否正常启动
|
|
268
|
+
if self._check_proxy_status():
|
|
269
|
+
self.proxy_enabled = True
|
|
270
|
+
print("✅ 代理服务启动成功")
|
|
271
|
+
self._set_system_proxy()
|
|
272
|
+
return True
|
|
273
|
+
else:
|
|
274
|
+
print("❌ 代理服务启动失败")
|
|
275
|
+
self.stop_proxy()
|
|
276
|
+
return False
|
|
277
|
+
|
|
278
|
+
except Exception as e:
|
|
279
|
+
print(f"❌ 启动代理服务失败: {e}")
|
|
280
|
+
return False
|
|
281
|
+
|
|
282
|
+
def stop_proxy(self):
|
|
283
|
+
"""停止代理服务"""
|
|
284
|
+
if self.clash_process:
|
|
285
|
+
try:
|
|
286
|
+
self.clash_process.terminate()
|
|
287
|
+
self.clash_process.wait(timeout=5)
|
|
288
|
+
except subprocess.TimeoutExpired:
|
|
289
|
+
self.clash_process.kill()
|
|
290
|
+
finally:
|
|
291
|
+
self.clash_process = None
|
|
292
|
+
|
|
293
|
+
self.proxy_enabled = False
|
|
294
|
+
self._unset_system_proxy()
|
|
295
|
+
print("✅ 代理服务已停止")
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _check_proxy_status(self) -> bool:
|
|
300
|
+
"""检查代理服务状态"""
|
|
301
|
+
try:
|
|
302
|
+
# 检查HTTP代理
|
|
303
|
+
proxies = {
|
|
304
|
+
'http': 'http://127.0.0.1:7890',
|
|
305
|
+
'https': 'http://127.0.0.1:7890'
|
|
306
|
+
}
|
|
307
|
+
response = requests.get('http://httpbin.org/ip',
|
|
308
|
+
proxies=proxies, timeout=5)
|
|
309
|
+
return response.status_code == 200
|
|
310
|
+
except:
|
|
311
|
+
return False
|
|
312
|
+
|
|
313
|
+
def _set_system_proxy(self):
|
|
314
|
+
"""设置系统代理"""
|
|
315
|
+
try:
|
|
316
|
+
if sys.platform == "win32":
|
|
317
|
+
# Windows系统代理设置
|
|
318
|
+
import winreg
|
|
319
|
+
|
|
320
|
+
def set_key(name, value):
|
|
321
|
+
try:
|
|
322
|
+
winreg.CreateKey(winreg.HKEY_CURRENT_USER,
|
|
323
|
+
r"Software\Microsoft\Windows\CurrentVersion\Internet Settings")
|
|
324
|
+
registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
|
|
325
|
+
r"Software\Microsoft\Windows\CurrentVersion\Internet Settings",
|
|
326
|
+
0, winreg.KEY_WRITE)
|
|
327
|
+
winreg.SetValueEx(registry_key, name, 0, winreg.REG_DWORD, value)
|
|
328
|
+
winreg.CloseKey(registry_key)
|
|
329
|
+
return True
|
|
330
|
+
except WindowsError:
|
|
331
|
+
return False
|
|
332
|
+
|
|
333
|
+
set_key("ProxyEnable", 1)
|
|
334
|
+
set_key("ProxyServer", "127.0.0.1:7890")
|
|
335
|
+
|
|
336
|
+
elif sys.platform == "darwin":
|
|
337
|
+
# macOS系统代理设置
|
|
338
|
+
os.system("networksetup -setwebproxy 'Wi-Fi' 127.0.0.1 7890")
|
|
339
|
+
os.system("networksetup -setsecurewebproxy 'Wi-Fi' 127.0.0.1 7890")
|
|
340
|
+
os.system("networksetup -setsocksfirewallproxy 'Wi-Fi' 127.0.0.1 7891")
|
|
341
|
+
|
|
342
|
+
else:
|
|
343
|
+
# Linux系统代理设置
|
|
344
|
+
os.environ['HTTP_PROXY'] = 'http://127.0.0.1:7890'
|
|
345
|
+
os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:7890'
|
|
346
|
+
os.environ['http_proxy'] = 'http://127.0.0.1:7890'
|
|
347
|
+
os.environ['https_proxy'] = 'http://127.0.0.1:7890'
|
|
348
|
+
|
|
349
|
+
except Exception as e:
|
|
350
|
+
print(f"⚠️ 设置系统代理失败: {e}")
|
|
351
|
+
|
|
352
|
+
def _unset_system_proxy(self):
|
|
353
|
+
"""取消系统代理设置"""
|
|
354
|
+
try:
|
|
355
|
+
if sys.platform == "win32":
|
|
356
|
+
import winreg
|
|
357
|
+
|
|
358
|
+
def set_key(name, value):
|
|
359
|
+
try:
|
|
360
|
+
winreg.CreateKey(winreg.HKEY_CURRENT_USER,
|
|
361
|
+
r"Software\Microsoft\Windows\CurrentVersion\Internet Settings")
|
|
362
|
+
registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
|
|
363
|
+
r"Software\Microsoft\Windows\CurrentVersion\Internet Settings",
|
|
364
|
+
0, winreg.KEY_WRITE)
|
|
365
|
+
winreg.SetValueEx(registry_key, name, 0, winreg.REG_DWORD, value)
|
|
366
|
+
winreg.CloseKey(registry_key)
|
|
367
|
+
return True
|
|
368
|
+
except WindowsError:
|
|
369
|
+
return False
|
|
370
|
+
|
|
371
|
+
set_key("ProxyEnable", 0)
|
|
372
|
+
|
|
373
|
+
elif sys.platform == "darwin":
|
|
374
|
+
os.system("networksetup -setwebproxystate 'Wi-Fi' off")
|
|
375
|
+
os.system("networksetup -setsecurewebproxystate 'Wi-Fi' off")
|
|
376
|
+
os.system("networksetup -setsocksfirewallproxystate 'Wi-Fi' off")
|
|
377
|
+
|
|
378
|
+
else:
|
|
379
|
+
# Linux系统代理设置
|
|
380
|
+
if 'HTTP_PROXY' in os.environ:
|
|
381
|
+
del os.environ['HTTP_PROXY']
|
|
382
|
+
if 'HTTPS_PROXY' in os.environ:
|
|
383
|
+
del os.environ['HTTPS_PROXY']
|
|
384
|
+
if 'http_proxy' in os.environ:
|
|
385
|
+
del os.environ['http_proxy']
|
|
386
|
+
if 'https_proxy' in os.environ:
|
|
387
|
+
del os.environ['https_proxy']
|
|
388
|
+
|
|
389
|
+
except Exception as e:
|
|
390
|
+
print(f"⚠️ 取消系统代理失败: {e}")
|
|
391
|
+
|
|
392
|
+
def get_proxy_info(self) -> Dict[str, Any]:
|
|
393
|
+
"""获取代理信息"""
|
|
394
|
+
return {
|
|
395
|
+
"enabled": self.proxy_enabled,
|
|
396
|
+
"config_path": self.config_path,
|
|
397
|
+
"clash_binary": self.clash_binary_path,
|
|
398
|
+
"clash_dir": self.clash_dir,
|
|
399
|
+
"http_proxy": "http://127.0.0.1:7890" if self.proxy_enabled else None,
|
|
400
|
+
"socks_proxy": "socks5://127.0.0.1:7891" if self.proxy_enabled else None
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
def update_config(self, new_config: Dict[str, Any]):
|
|
404
|
+
"""更新代理配置"""
|
|
405
|
+
try:
|
|
406
|
+
with open(self.config_path, 'w', encoding='utf-8') as f:
|
|
407
|
+
yaml.dump(new_config, f, default_flow_style=False, allow_unicode=True)
|
|
408
|
+
self.config_data = new_config
|
|
409
|
+
print("✅ 代理配置已更新")
|
|
410
|
+
|
|
411
|
+
# 如果代理正在运行,重启服务
|
|
412
|
+
if self.proxy_enabled:
|
|
413
|
+
self.stop_proxy()
|
|
414
|
+
time.sleep(1)
|
|
415
|
+
self.start_proxy()
|
|
416
|
+
|
|
417
|
+
except Exception as e:
|
|
418
|
+
print(f"❌ 更新代理配置失败: {e}")
|
|
419
|
+
|
|
420
|
+
def cleanup(self):
|
|
421
|
+
"""清理资源"""
|
|
422
|
+
self.stop_proxy()
|
|
423
|
+
# 可以选择是否删除clash二进制文件
|
|
424
|
+
# if self.clash_dir and os.path.exists(self.clash_dir):
|
|
425
|
+
# import shutil
|
|
426
|
+
# shutil.rmtree(self.clash_dir)
|
|
427
|
+
|
|
428
|
+
# # 全局代理管理器实例
|
|
429
|
+
proxy_manager = ProxyManager()
|
|
430
|
+
|
|
431
|
+
def init_proxy():
|
|
432
|
+
"""初始化代理服务"""
|
|
433
|
+
return proxy_manager.start_proxy()
|
|
434
|
+
|
|
435
|
+
def stop_proxy():
|
|
436
|
+
"""停止代理服务"""
|
|
437
|
+
proxy_manager.stop_proxy()
|
|
438
|
+
|
|
439
|
+
def get_proxy_status():
|
|
440
|
+
"""获取代理状态"""
|
|
441
|
+
return proxy_manager.get_proxy_info()
|
|
442
|
+
|
|
443
|
+
def cleanup_proxy():
|
|
444
|
+
"""清理代理资源"""
|
|
445
|
+
proxy_manager.cleanup()
|
|
@@ -130,7 +130,7 @@ class ryryDaemonThread(Thread):
|
|
|
130
130
|
def __init__(self):
|
|
131
131
|
super().__init__()
|
|
132
132
|
self.name = f"ryryDaemonThread"
|
|
133
|
-
self.tik_time =
|
|
133
|
+
self.tik_time = 10.0
|
|
134
134
|
self.start()
|
|
135
135
|
|
|
136
136
|
def run(self):
|
|
@@ -143,7 +143,9 @@ class ryryDaemonThread(Thread):
|
|
|
143
143
|
try:
|
|
144
144
|
daemon_manager.sync_daemons_with_widget_map()
|
|
145
145
|
except:
|
|
146
|
-
|
|
146
|
+
pass
|
|
147
|
+
finally:
|
|
148
|
+
time.sleep(self.tik_time)
|
|
147
149
|
try:
|
|
148
150
|
print("Stopping daemon processes...")
|
|
149
151
|
if daemon_manager.stop_all_daemons():
|
|
@@ -155,7 +155,7 @@ def extend():
|
|
|
155
155
|
_genExtend()
|
|
156
156
|
elif read_data["extend"].get("device_id", "") != read_data.get("deviceInfo", {}).get("device_id", ""):
|
|
157
157
|
_genExtend()
|
|
158
|
-
elif read_data["extend"].get("app", "") == "ryry-cli 4.
|
|
158
|
+
elif read_data["extend"].get("app", "") == "ryry-cli 4.25":
|
|
159
159
|
_genExtend()
|
|
160
160
|
else:
|
|
161
161
|
_genExtend()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -6,6 +6,7 @@ ryry/constant.py
|
|
|
6
6
|
ryry/daemon_base.py
|
|
7
7
|
ryry/daemon_manager.py
|
|
8
8
|
ryry/main.py
|
|
9
|
+
ryry/proxy_manager.py
|
|
9
10
|
ryry/ryry_server_socket.py
|
|
10
11
|
ryry/ryry_service.py
|
|
11
12
|
ryry/ryry_webapi.py
|
|
@@ -15,6 +16,7 @@ ryry/shared_memory.py
|
|
|
15
16
|
ryry/store.py
|
|
16
17
|
ryry/task.py
|
|
17
18
|
ryry/taskUtils.py
|
|
19
|
+
ryry/test_proxy.py
|
|
18
20
|
ryry/upload.py
|
|
19
21
|
ryry/utils.py
|
|
20
22
|
ryry/script_template/__init__.py
|
|
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
|