switch_proxy_group.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. # -*- coding: utf-8 -*-
  2. '''
  3. 切换到指定代理组
  4. '''
  5. import httpx
  6. import time
  7. import logging
  8. import subprocess
  9. from typing import Optional, List
  10. logging.basicConfig(level=logging.INFO)
  11. BASE_URL = "http://192.168.31.194"
  12. PORT_LIST = [
  13. ["58001", "59001"],
  14. ["58002", "59002"],
  15. ["58003", "59003"],
  16. ["58004", "59004"],
  17. ["58005", "59005"],
  18. ["58006", "59006"],
  19. ["58007", "59007"],
  20. ["58008", "59008"],
  21. ["58009", "59009"],
  22. ["58010", "59010"],
  23. ]
  24. class ClashProxyManager:
  25. def __init__(self, base_url, base_port):
  26. self.key_group = 0
  27. self.base_url = base_url
  28. self.base_port = base_port
  29. self.proxy_keyword = [
  30. ['sg', 'SG', '新加坡', '马来西亚'],
  31. ['jp', '日本'],
  32. ]
  33. self.all_proxies = []
  34. self.selected_proxies = []
  35. def get_all_proxies(self, clash_tool_url: str) -> List[str]:
  36. url = f"{clash_tool_url}/api/proxies"
  37. try:
  38. response = httpx.get(url)
  39. response.raise_for_status()
  40. proxies = response.json()
  41. logging.info("Available proxies:")
  42. # 输出读取的所有代理信息
  43. # for proxy_name, proxy_info in proxies['proxies'].items():
  44. # logging.info(f"Name: {proxy_name}, Type: {proxy_info.get('type', 'Unknown')}")
  45. proxy_list = list(proxies['proxies'].keys())
  46. filtered_list = [item for item in proxy_list if item not in {'REJECT', 'GLOBAL', 'DIRECT'}]
  47. return filtered_list
  48. except Exception as e:
  49. logging.error(f"Failed to get proxies: {e}")
  50. return []
  51. def filter_proxy(self):
  52. for keyword in self.proxy_keyword[self.key_group]:
  53. for item in self.all_proxies:
  54. if keyword.lower() in item.lower():
  55. self.selected_proxies.append(item)
  56. def switch_proxy(self, proxy_name: str, url_and_port: str) -> None:
  57. logging.info("switch proxy")
  58. url = f"{url_and_port}/api/proxies/GLOBAL"
  59. data = {"name": proxy_name}
  60. try:
  61. response = httpx.put(url, json=data)
  62. if response.status_code == 204:
  63. logging.info(f"Switched to proxy: {proxy_name}")
  64. else:
  65. logging.error(f"Failed to switch proxy: {response.status_code} - {proxy_name}")
  66. except Exception as e:
  67. logging.error(f"Failed to switch proxy: {e}")
  68. def update_configs(self):
  69. for base_port in self.base_port:
  70. url_and_port = self.base_url + ":" + base_port[0]
  71. key = "/api/configs"
  72. url = url_and_port + key
  73. headers = {
  74. "accept": "application/json, text/plain, */*",
  75. "accept-encoding": "gzip, deflate, br, zstd",
  76. "accept-language": "zh-CN,zh",
  77. "connection": "keep-alive",
  78. "content-type": "application/json",
  79. "origin": url_and_port,
  80. "referer": url_and_port,
  81. "sec-ch-ua": '"Not A(Brand";v="8", "Chromium";v="132", "Brave";v="132"',
  82. "sec-ch-ua-mobile": "?0",
  83. "sec-ch-ua-platform": '"macOS"',
  84. "sec-fetch-dest": "empty",
  85. "sec-fetch-mode": "cors",
  86. "sec-fetch-site": "same-origin",
  87. "sec-gpc": "1",
  88. "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
  89. }
  90. # 请求体数据
  91. data = {"mode": "Global"} # 替换为实际的请求数据
  92. # 使用 httpx 发送 PATCH 请求
  93. try:
  94. with httpx.Client() as client:
  95. response = client.patch(url, headers=headers, json=data)
  96. if response.status_code == 204:
  97. print(f"{url} OK")
  98. else:
  99. print("响应内容:", response.text)
  100. except httpx.RequestError as exc:
  101. print(f"请求失败: {exc}")
  102. def check_proxy(self, proxy_url):
  103. # proxy_url: 代理地址, 没有密码
  104. # 测试目标地址:
  105. command = ["curl", "-x", proxy_url, "ip.sb"]
  106. # 执行命令并获取输出
  107. try:
  108. result = subprocess.run(command, capture_output=True, text=True, check=True)
  109. # 打印命令的标准输出
  110. print("Output:", result.stdout)
  111. return True
  112. except subprocess.CalledProcessError as e:
  113. # 如果命令执行失败,打印错误信息
  114. print("Error:", e.stderr)
  115. return False
  116. def main(self) -> None:
  117. # 设置全局代理
  118. self.update_configs()
  119. # 读取所有代理
  120. if not self.all_proxies:
  121. for port_list in self.base_port:
  122. base_url = self.base_url + ":" + port_list[0]
  123. clash_tool_url = f"{base_url}"
  124. proxies = self.get_all_proxies(clash_tool_url)
  125. if proxies:
  126. self.all_proxies = proxies
  127. break
  128. if not self.all_proxies:
  129. logging.error("Failed to get all proxies")
  130. return
  131. # 通过关键词过滤出需要的代理
  132. self.filter_proxy()
  133. if not self.selected_proxies:
  134. logging.error("Failed to filter proxies")
  135. return
  136. # 遍历所有的线路api, 切换不重复代理
  137. # 切换后, 检测代理, 如果检测返回失败, 再次切换
  138. used_proxy = []
  139. for base_port in self.base_port:
  140. url_and_port = self.base_url + ":" + base_port[0]
  141. proxy_url = self.base_url + ":" + base_port[1]
  142. for select_proxy in self.selected_proxies:
  143. if select_proxy in used_proxy:
  144. continue
  145. # 尝试切换代理并检测
  146. self.switch_proxy(select_proxy, url_and_port)
  147. if self.check_proxy(proxy_url):
  148. print(f"代理 {select_proxy} 切换成功,检测通过!")
  149. used_proxy.append(select_proxy) # 标记为已使用
  150. break # 成功后退出当前代理的重试循环
  151. else:
  152. print(f"{url_and_port} 切换 {select_proxy} 检测失败")
  153. time.sleep(1) # 等待一段时间后重试
  154. if __name__ == "__main__":
  155. manager = ClashProxyManager(BASE_URL, PORT_LIST)
  156. manager.main()