flask / main.py
rtian001's picture
Upload 5 files
8e0c91a verified
Raw History Blame Contribute Delete
18.1 kB
import os
import re
import json
import time
import base64
import shutil
import asyncio
import requests
import subprocess
import threading
from threading import Thread
# WEB站点
WEB_ARGO_DOMAIN=os.environ.get('WEB_DOMAIN','')
WEB_ARGO_AUTH = os.environ.get('WEB_ARGO_AUTH', '')
# Environment variables
FILE_PATH = os.environ.get('FILE_PATH', './.cache') # 运行路径,sub.txt保存路径
SUB_PATH = os.environ.get('SUB_PATH', 'getmysubs') # 订阅token,默认sub,例如:https://www.google.com/sub
UUID = os.environ.get('UUID', 'b0cc09a7-06f0-42fb-a161-17434162ebdf') # UUID,如使用哪吒v1,在不同的平台部署需要修改,否则会覆盖
NEZHA_SERVER = os.environ.get('NEZHA_SERVER', '') # 哪吒面板域名或ip, v1格式: nezha.xxx.com:8008, v0格式: nezha.xxx.com
NEZHA_PORT = os.environ.get('NEZHA_PORT', '') # v1哪吒请留空, v0哪吒的agent通信端口,自动匹配tls
NEZHA_KEY = os.environ.get('NEZHA_KEY', '') # v1哪吒的NZ_CLIENT_SECRET或v0哪吒agent密钥
ARGO_DOMAIN = os.environ.get('ARGO_DOMAIN', '') # Argo固定隧道域名,留空即使用临时隧道
ARGO_AUTH = os.environ.get('ARGO_AUTH', '') # Argo固定隧道密钥,留空即使用临时隧道
ARGO_PORT = int(os.environ.get('ARGO_PORT', '8001')) # Argo端口,使用固定隧道token需在cloudflare后台设置端口和这里一致
CFIP = os.environ.get('CFIP', 'www.visa.com.tw') # 优选ip或优选域名
CFPORT = int(os.environ.get('CFPORT', '443')) # 优选ip或优选域名对应端口
NAME = os.environ.get('NAME', 'dbs-rtian1') # 节点名称
PORT = int(os.environ.get('SERVER_PORT') or os.environ.get('PORT') or 3000) # 订阅端口,如无法订阅,请手动修改为分配的端口
# Execute shell command and return output
def exec_cmd(command):
try:
process = subprocess.Popen(
command,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
stdout, stderr = process.communicate()
return stdout.strip()
except Exception as e:
print(f"Error executing command: {e}")
return null
# Create running folder
def create_directory():
if not os.path.exists(FILE_PATH):
os.makedirs(FILE_PATH)
# print(f"{FILE_PATH} is created")
# else:
# print(f"{FILE_PATH} already exists")
# Global variables
npm_path = os.path.join(FILE_PATH, 'npm')
php_path = os.path.join(FILE_PATH, 'php')
crawl_path = os.path.join(FILE_PATH, 'crawl')
bot_path = os.path.join(FILE_PATH, 'bot')
sub_path = os.path.join(FILE_PATH, 'sub.txt')
list_path = os.path.join(FILE_PATH, 'list.txt')
boot_log_path = os.path.join(FILE_PATH, 'boot.log')
config_path = os.path.join(FILE_PATH, 'config.json')
# Delete nodes
def delete_nodes():
try:
if not os.path.exists(sub_path):
return
try:
with open(sub_path, 'r') as file:
file_content = file.read()
except:
return None
decoded = base64.b64decode(file_content).decode('utf-8')
nodes = [line for line in decoded.split('\n') if any(protocol in line for protocol in ['vless://', 'vmess://', 'trojan://', 'hysteria2://', 'tuic://'])]
if not nodes:
return
except Exception as e:
print(f"Error in delete_nodes: {e}")
return None
# Clean up old files
def cleanup_old_files():
paths_to_delete = ['crawl', 'bot', 'npm', 'php', 'boot.log', 'list.txt']
for file in paths_to_delete:
file_path = os.path.join(FILE_PATH, file)
try:
if os.path.exists(file_path):
if os.path.isdir(file_path):
shutil.rmtree(file_path)
else:
os.remove(file_path)
except Exception as e:
print(f"Error removing {file_path}: {e}")
# Determine system architecture
def get_system_architecture():
# architecture = platform.machine().lower()
architecture = exec_cmd("uname -m").lower()
if 'arm' in architecture or 'aarch64' in architecture:
return 'arm'
else:
return 'amd'
# Download file based on architecture
def download_file(file_name, file_url):
file_path = os.path.join(FILE_PATH, file_name)
try:
response = requests.get(file_url, stream=True)
response.raise_for_status()
with open(file_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
print(f"Download {file_name} successfully")
return True
except Exception as e:
if os.path.exists(file_path):
os.remove(file_path)
print(f"Download {file_name} failed: {e}")
return False
# Get files for architecture
def get_files_for_architecture(architecture):
if architecture == 'arm':
base_files = [
{"fileName": "crawl", "fileUrl": "https://arm64.ssss.nyc.mn/web"},
{"fileName": "bot", "fileUrl": "https://arm64.ssss.nyc.mn/2go"}
]
else:
base_files = [
{"fileName": "crawl", "fileUrl": "https://amd64.ssss.nyc.mn/web"},
{"fileName": "bot", "fileUrl": "https://amd64.ssss.nyc.mn/2go"}
]
if NEZHA_SERVER and NEZHA_KEY:
if NEZHA_PORT:
npm_url = "https://arm64.ssss.nyc.mn/agent" if architecture == 'arm' else "https://amd64.ssss.nyc.mn/agent"
base_files.insert(0, {"fileName": "npm", "fileUrl": npm_url})
else:
php_url = "https://arm64.ssss.nyc.mn/v1" if architecture == 'arm' else "https://amd64.ssss.nyc.mn/v1"
base_files.insert(0, {"fileName": "php", "fileUrl": php_url})
return base_files
# Authorize files with execute permission
def authorize_files(file_paths):
for relative_file_path in file_paths:
absolute_file_path = os.path.join(FILE_PATH, relative_file_path)
if os.path.exists(absolute_file_path):
try:
os.chmod(absolute_file_path, 0o775)
print(f"Empowerment success for {absolute_file_path}: 775")
except Exception as e:
print(f"Empowerment failed for {absolute_file_path}: {e}")
# Configure Argo tunnel
def argo_type():
if not ARGO_AUTH or not ARGO_DOMAIN:
# print("ARGO_DOMAIN or ARGO_AUTH variable is empty, use quick tunnels")
return
if "TunnelSecret" in ARGO_AUTH:
with open(os.path.join(FILE_PATH, 'tunnel.json'), 'w') as f:
f.write(ARGO_AUTH)
tunnel_id = ARGO_AUTH.split('"')[11]
tunnel_yml = f"""
tunnel: {tunnel_id}
credentials-file: {os.path.join(FILE_PATH, 'tunnel.json')}
protocol: http2
ingress:
- hostname: {ARGO_DOMAIN}
service: http://localhost:{ARGO_PORT}
originRequest:
noTLSVerify: true
- service: http_status:404
"""
with open(os.path.join(FILE_PATH, 'tunnel.yml'), 'w') as f:
f.write(tunnel_yml)
else:
# print("Use token connect to tunnel,please set the {ARGO_PORT} in cloudflare")
return
# Download and run necessary files
async def download_files_and_run():
global private_key, public_key
architecture = get_system_architecture()
files_to_download = get_files_for_architecture(architecture)
if not files_to_download:
print("Can't find a file for the current architecture")
return
# Download all files
download_success = True
for file_info in files_to_download:
if not download_file(file_info["fileName"], file_info["fileUrl"]):
download_success = False
if not download_success:
print("Error downloading files")
return
# Authorize files
files_to_authorize = ['npm', 'crawl', 'bot'] if NEZHA_PORT else ['php', 'crawl', 'bot']
authorize_files(files_to_authorize)
# Check TLS
port = NEZHA_SERVER.split(":")[-1] if ":" in NEZHA_SERVER else ""
if port in ["443", "8443", "2096", "2087", "2083", "2053"]:
nezha_tls = "true"
else:
nezha_tls = "false"
# Configure nezha
if NEZHA_SERVER and NEZHA_KEY:
if not NEZHA_PORT:
# Generate config.yaml for v1
config_yaml = f"""
client_secret: {NEZHA_KEY}
debug: false
disable_auto_update: true
disable_command_execute: false
disable_force_update: true
disable_nat: false
disable_send_query: false
gpu: false
insecure_tls: false
ip_report_period: 1800
report_delay: 4
server: {NEZHA_SERVER}
skip_connection_count: false
skip_procs_count: false
temperature: false
tls: {nezha_tls}
use_gitee_to_upgrade: false
use_ipv6_country_code: false
uuid: {UUID}"""
with open(os.path.join(FILE_PATH, 'config.yaml'), 'w') as f:
f.write(config_yaml)
# Generate configuration file
config ={"log":{"access":"/dev/null","error":"/dev/null","loglevel":"none",},"inbounds":[{"port":ARGO_PORT ,"protocol":"vless","settings":{"clients":[{"id":UUID ,"flow":"xtls-rprx-vision",},],"decryption":"none","fallbacks":[{"dest":3001 },{"path":"/vless-argo","dest":3002 },{"path":"/vmess-argo","dest":3003 },{"path":"/trojan-argo","dest":3004 },],},"streamSettings":{"network":"tcp",},},{"port":3001 ,"listen":"127.0.0.1","protocol":"vless","settings":{"clients":[{"id":UUID },],"decryption":"none"},"streamSettings":{"network":"ws","security":"none"}},{"port":3002 ,"listen":"127.0.0.1","protocol":"vless","settings":{"clients":[{"id":UUID ,"level":0 }],"decryption":"none"},"streamSettings":{"network":"ws","security":"none","wsSettings":{"path":"/vless-argo"}},"sniffing":{"enabled":True ,"destOverride":["http","tls","quic"],"metadataOnly":False }},{"port":3003 ,"listen":"127.0.0.1","protocol":"vmess","settings":{"clients":[{"id":UUID ,"alterId":0 }]},"streamSettings":{"network":"ws","wsSettings":{"path":"/vmess-argo"}},"sniffing":{"enabled":True ,"destOverride":["http","tls","quic"],"metadataOnly":False }},{"port":3004 ,"listen":"127.0.0.1","protocol":"trojan","settings":{"clients":[{"password":UUID },]},"streamSettings":{"network":"ws","security":"none","wsSettings":{"path":"/trojan-argo"}},"sniffing":{"enabled":True ,"destOverride":["http","tls","quic"],"metadataOnly":False }},],"outbounds":[{"protocol":"freedom","tag": "direct" },{"protocol":"blackhole","tag":"block"}]}
with open(os.path.join(FILE_PATH, 'config.json'), 'w', encoding='utf-8') as config_file:
json.dump(config, config_file, ensure_ascii=False, indent=2)
# Run nezha
if NEZHA_SERVER and NEZHA_PORT and NEZHA_KEY:
tls_ports = ['443', '8443', '2096', '2087', '2083', '2053']
nezha_tls = '--tls' if NEZHA_PORT in tls_ports else ''
command = f"nohup {os.path.join(FILE_PATH, 'npm')} -s {NEZHA_SERVER}:{NEZHA_PORT} -p {NEZHA_KEY} {nezha_tls} >/dev/null 2>&1 &"
try:
exec_cmd(command)
print('npm is running')
time.sleep(1)
except Exception as e:
print(f"npm running error: {e}")
elif NEZHA_SERVER and NEZHA_KEY:
# Run V1
command = f"nohup {FILE_PATH}/php -c \"{FILE_PATH}/config.yaml\" >/dev/null 2>&1 &"
try:
exec_cmd(command)
print('php is running')
time.sleep(1)
except Exception as e:
print(f"php running error: {e}")
else:
print('NEZHA variable is empty, skipping running')
# Run sbX
command = f"nohup {os.path.join(FILE_PATH, 'crawl')} -c {os.path.join(FILE_PATH, 'config.json')} >/dev/null 2>&1 &"
try:
# 首次启动不开启sbx
# exec_cmd(command)
print('crawl is running')
time.sleep(1)
except Exception as e:
print(f"crawl running error: {e}")
# Run cloudflared
if os.path.exists(os.path.join(FILE_PATH, 'bot')):
if re.match(r'^[A-Z0-9a-z=]{120,250}$', ARGO_AUTH):
args = f"tunnel --edge-ip-version auto --no-autoupdate --protocol http2 run --token {ARGO_AUTH}"
elif "TunnelSecret" in ARGO_AUTH:
args = f"tunnel --edge-ip-version auto --config {os.path.join(FILE_PATH, 'tunnel.yml')} run"
else:
args = f"tunnel --edge-ip-version auto --no-autoupdate --protocol http2 --logfile {os.path.join(FILE_PATH, 'boot.log')} --loglevel info --url http://localhost:{ARGO_PORT}"
if WEB_ARGO_DOMAIN and WEB_ARGO_AUTH:
web_args=f"tunnel --edge-ip-version auto --no-autoupdate --protocol http2 run --token {WEB_ARGO_AUTH}"
exec_cmd(f"nohup {os.path.join(FILE_PATH, 'bot')} {web_args} >/dev/null 2>&1 &")
print('web_bot is running')
time.sleep(3)
try:
exec_cmd(f"nohup {os.path.join(FILE_PATH, 'bot')} {args} >/dev/null 2>&1 &")
print('bot is running')
time.sleep(2)
except Exception as e:
print(f"Error executing command: {e}")
time.sleep(5)
# Extract domains and generate sub.txt
await extract_domains()
# Extract domains from cloudflared logs
async def extract_domains():
argo_domain = None
if ARGO_AUTH and ARGO_DOMAIN:
argo_domain = ARGO_DOMAIN
print(f'ARGO_DOMAIN: {argo_domain}')
await generate_links(argo_domain)
else:
try:
with open(boot_log_path, 'r') as f:
file_content = f.read()
lines = file_content.split('\n')
argo_domains = []
for line in lines:
domain_match = re.search(r'https?://([^ ]*trycloudflare\.com)/?', line)
if domain_match:
domain = domain_match.group(1)
argo_domains.append(domain)
if argo_domains:
argo_domain = argo_domains[0]
print(f'ArgoDomain: {argo_domain}')
await generate_links(argo_domain)
else:
print('ArgoDomain not found, re-running bot to obtain ArgoDomain')
# Remove boot.log and restart bot
if os.path.exists(boot_log_path):
os.remove(boot_log_path)
try:
exec_cmd('pkill -f "[b]ot" > /dev/null 2>&1')
except:
pass
time.sleep(1)
args = f'tunnel --edge-ip-version auto --no-autoupdate --protocol http2 --logfile {FILE_PATH}/boot.log --loglevel info --url http://localhost:{ARGO_PORT}'
exec_cmd(f'nohup {os.path.join(FILE_PATH, "bot")} {args} >/dev/null 2>&1 &')
print('bot is running.')
time.sleep(6) # Wait 6 seconds
await extract_domains() # Try again
except Exception as e:
print(f'Error reading boot.log: {e}')
# Generate links and subscription content
async def generate_links(argo_domain):
meta_info = exec_cmd('curl -s https://speed.cloudflare.com/meta')
meta_info = meta_info.split('"')
ISP = f"【{meta_info[25]}】"
time.sleep(2)
VMESS = {"v": "2", "ps": f"{NAME}-{ISP}-1", "add": CFIP, "port": CFPORT, "id": UUID, "aid": "0", "scy": "none", "net": "ws", "type": "none", "host": argo_domain, "path": "/vmess-argo?ed=2560", "tls": "tls", "sni": argo_domain, "alpn": "", "fp": "chrome"}
list_txt = f"""
vmess://{ base64.b64encode(json.dumps(VMESS).encode('utf-8')).decode('utf-8')}
vless://{UUID}@{CFIP}:{CFPORT}?encryption=none&security=tls&sni={argo_domain}&fp=chrome&type=ws&host={argo_domain}&path=%2Fvless-argo%3Fed%3D2560#{NAME}-{ISP}-2
trojan://{UUID}@{CFIP}:{CFPORT}?security=tls&sni={argo_domain}&fp=chrome&type=ws&host={argo_domain}&path=%2Ftrojan-argo%3Fed%3D2560#{NAME}-{ISP}-3
"""
with open(os.path.join(FILE_PATH, 'list.txt'), 'w', encoding='utf-8') as list_file:
list_file.write(list_txt)
sub_txt = base64.b64encode(list_txt.encode('utf-8')).decode('utf-8')
with open(os.path.join(FILE_PATH, 'sub.txt'), 'w', encoding='utf-8') as sub_file:
sub_file.write(sub_txt)
print(f"{FILE_PATH}/sub.txt saved successfully")
return sub_txt
# Clean up files after 90 seconds
def clean_files():
def _cleanup():
time.sleep(90) # Wait 90 seconds
files_to_delete = [boot_log_path, config_path, list_path, crawl_path, bot_path, php_path, npm_path]
for file in files_to_delete:
try:
if os.path.exists(file):
if os.path.isdir(file):
shutil.rmtree(file)
else:
os.remove(file)
except:
pass
print('App is running')
print('Thank you for using this script, enjoy!')
threading.Thread(target=_cleanup, daemon=True).start()
# Main function to start the server
async def start_sbx():
if os.path.exists(crawl_path):
command = f"nohup {os.path.join(FILE_PATH, 'crawl')} -c {os.path.join(FILE_PATH, 'config.json')} >/dev/null 2>&1 &"
exec_cmd(command)
else:
# delete_nodes()
# cleanup_old_files()
create_directory()
argo_type()
await download_files_and_run()
# clean_files()
def run_start():
crawl_loop = asyncio.new_event_loop()
asyncio.set_event_loop(crawl_loop)
crawl_loop.run_until_complete(start_sbx())
crawl_loop.close()
def run_stop():
exec_cmd('pkill -f crawl > /dev/null 2>&1')
def get_links():
running=exec_cmd('ps -ef|grep cache/crawl |grep -v grep')
if running:
status='running'
else:
status='stopped'
if os.path.exists(sub_path):
with open(sub_path, 'r', encoding='utf-8') as sub_file:
sub_txt = sub_file.read()
return json.dumps({"status":status,"data":sub_txt,"running":running}).encode('utf-8')
else:
return json.dumps({"status":status,"data":None,"running":running}).encode('utf-8')
if __name__ == "__main__":
run_start()