Update get_vpn.py
This commit is contained in:
+34
-79
@@ -1,109 +1,64 @@
|
|||||||
import base64
|
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
||||||
def get_vpn_servers():
|
def fetch_clash_config():
|
||||||
|
# Получаем ссылку-подписку из секретов Gitea/GitHub
|
||||||
sub_url = os.getenv("VPN_SUB_URL")
|
sub_url = os.getenv("VPN_SUB_URL")
|
||||||
|
|
||||||
if not sub_url:
|
if not sub_url:
|
||||||
print("Ошибка: Переменная окружения VPN_SUB_URL не задана!")
|
print("Ошибка: Переменная окружения VPN_SUB_URL не задана!")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Имитируем заголовки оригинального клиента OpenClash / Mihomo
|
||||||
headers = {
|
headers = {
|
||||||
"User-Agent": "Happ/2.1.0 (iPhone; iOS 15.5; Scale/3.00)",
|
"User-Agent": "Clash.meta; mihomo; OpenClash",
|
||||||
"Accept": "*/*",
|
"Accept": "*/*",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# На всякий случай проверяем, есть ли уже в ссылке флаг клиента.
|
||||||
|
# Если нет — панель может проигнорировать User-Agent, поэтому добавляем флаг принудительно.
|
||||||
|
if "flag=" not in sub_url:
|
||||||
|
separator = "&" if "?" in sub_url else "?"
|
||||||
|
sub_url += f"{separator}flag=clash"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print("Отправка запроса к серверу VPN...")
|
print("Отправка запроса к серверу VPN с заголовками Clash...")
|
||||||
response = requests.get(sub_url, headers=headers, timeout=15)
|
response = requests.get(sub_url, headers=headers, timeout=20)
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
print(f"Ошибка服务器: Статус {response.status_code}")
|
print(
|
||||||
|
f"Ошибка сервера: Статус {response.status_code}. Проверьте ссылку."
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
raw_content = response.text.strip()
|
content = response.text.strip()
|
||||||
|
|
||||||
# 1. Проверяем формат JSON
|
# Проверяем, что сервер вернул именно YAML-подобную структуру, а не ошибку JSON
|
||||||
try:
|
if "proxies:" in content or "proxy-groups:" in content:
|
||||||
data = json.loads(raw_content)
|
print("Успешно получен готовый Clash-конфиг от сервера!")
|
||||||
print("Обнаружен формат JSON.")
|
|
||||||
|
|
||||||
# Если сервер вернул словарь, ищем ключи. Если вернул сразу список — берем его как есть.
|
# Сохраняем результат в файл
|
||||||
if isinstance(data, dict):
|
output_file = "clash.yaml"
|
||||||
servers = data.get("data", {}).get("server_user", data)
|
with open(output_file, "w", encoding="utf-8") as f:
|
||||||
else:
|
f.write(content)
|
||||||
servers = data
|
|
||||||
|
|
||||||
with open("servers.json", "w", encoding="utf-8") as f:
|
print(f"Конфигурация сохранена в файл: {output_file}")
|
||||||
json.dump(servers, f, indent=2, ensure_ascii=False)
|
else:
|
||||||
|
print("Предупреждение: Сервер вернул ответ, но он не похож на Clash YAML.")
|
||||||
|
print("Первые 200 символов ответа для отладки:")
|
||||||
|
print(content[:200])
|
||||||
|
|
||||||
# Выводим количество найденных элементов для наглядности в логах
|
# Сохраняем как есть, чтобы вы могли посмотреть, что пришло
|
||||||
count = len(servers) if isinstance(servers, list) else 1
|
with open("response_debug.txt", "w", encoding="utf-8") as f:
|
||||||
print(
|
f.write(content)
|
||||||
f"Данные успешно сохранены в файл servers.json (Элементов: {count})"
|
sys.exit(1)
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
# Если не JSON, двигаемся к проверке Base64
|
|
||||||
pass
|
|
||||||
|
|
||||||
# 2. Проверяем формат Base64 (на случай изменения выдачи)
|
|
||||||
try:
|
|
||||||
padded_content = raw_content + "=" * (4 - len(raw_content) % 4)
|
|
||||||
decoded_bytes = base64.b64decode(padded_content)
|
|
||||||
decoded_text = decoded_bytes.decode("utf-8", errors="ignore").strip()
|
|
||||||
|
|
||||||
if any(
|
|
||||||
proto in decoded_text
|
|
||||||
for proto in ["vless://", "vmess://", "ss://", "trojan://"]
|
|
||||||
):
|
|
||||||
print("Обнаружена Base64 подписка.")
|
|
||||||
with open("servers.txt", "w", encoding="utf-8") as f:
|
|
||||||
f.write(decoded_text)
|
|
||||||
|
|
||||||
links_list = [
|
|
||||||
line.strip()
|
|
||||||
for line in decoded_text.splitlines()
|
|
||||||
if line.strip()
|
|
||||||
]
|
|
||||||
with open("servers.json", "w", encoding="utf-8") as f:
|
|
||||||
json.dump(links_list, f, indent=2, ensure_ascii=False)
|
|
||||||
print("Ссылки сохранены в servers.txt и servers.json")
|
|
||||||
return
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# 3. Проверяем сырой текст со ссылками
|
|
||||||
if any(
|
|
||||||
proto in raw_content
|
|
||||||
for proto in ["vless://", "vmess://", "ss://", "trojan://"]
|
|
||||||
):
|
|
||||||
print("Сервер вернул сырой текст со ссылками.")
|
|
||||||
with open("servers.txt", "w", encoding="utf-8") as f:
|
|
||||||
f.write(raw_content)
|
|
||||||
|
|
||||||
links_list = [
|
|
||||||
line.strip() for line in raw_content.splitlines() if line.strip()
|
|
||||||
]
|
|
||||||
with open("servers.json", "w", encoding="utf-8") as f:
|
|
||||||
json.dump(links_list, f, indent=2, ensure_ascii=False)
|
|
||||||
print("Данные сохранены в servers.txt и servers.json")
|
|
||||||
return
|
|
||||||
|
|
||||||
print("Неизвестный формат ответа. Сохраняем в raw_response.txt")
|
|
||||||
with open("raw_response.txt", "w", encoding="utf-8") as f:
|
|
||||||
f.write(raw_content)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Произошла критическая ошибка: {e}")
|
print(f"Критическая ошибка при запросе: {e}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
get_vpn_servers()
|
fetch_clash_config()
|
||||||
|
|||||||
Reference in New Issue
Block a user