#!/usr/bin/env python3 """ Script to extract Tailscale DERP server IPv4 addresses from tailscale.json and generate a Clash rule list file for direct connection. """ import json import ipaddress from pathlib import Path def load_tailscale_data(json_file): """Load tailscale.json data""" with open(json_file, 'r', encoding='utf-8') as f: return json.load(f) def extract_ipv4_addresses(data): """Extract all IPv4 addresses from tailscale regions data""" ipv4_addresses = set() for region_id, region_data in data['Regions'].items(): for node in region_data.get('Nodes', []): ipv4 = node.get('IPv4') if ipv4: try: # Validate IPv4 address ipaddress.IPv4Address(ipv4) ipv4_addresses.add(ipv4) except ipaddress.AddressValueError: print(f"Invalid IPv4 address: {ipv4}") return sorted(ipv4_addresses) def generate_clash_rules(ipv4_addresses, output_file): """Generate Clash rule list file""" with open(output_file, 'w', encoding='utf-8') as f: f.write("# Tailscale DERP Servers - Direct Connection Rules\n") f.write("# Generated from tailscale.json\n") f.write("# All Tailscale DERP relay servers should use direct connection\n\n") for ip in ipv4_addresses: f.write(f"IP-CIDR,{ip}/32\n") def main(): json_file = Path("tailscale.json") output_file = Path("Rules/Tailscale.list") if not json_file.exists(): print(f"Error: {json_file} not found") return # Create Rules directory if it doesn't exist output_file.parent.mkdir(exist_ok=True) try: # Load tailscale data data = load_tailscale_data(json_file) # Extract IPv4 addresses ipv4_addresses = extract_ipv4_addresses(data) print(f"Found {len(ipv4_addresses)} unique IPv4 addresses") # Generate Clash rules generate_clash_rules(ipv4_addresses, output_file) print(f"Generated {output_file} successfully!") print(f"Rules created for {len(ipv4_addresses)} Tailscale DERP server IPs") except Exception as e: print(f"Error: {e}") if __name__ == "__main__": main()