WWW.YOUINFO.SITE
标签聚合 Party

/tag/Party

LinuxDo 最新话题 · 2026-06-10 01:10:31+08:00 · tech

週末的狂歡party,奧特曼給了我們的大禮物,這一次我沒跟上,因為一直在折騰CPA,加上又是深夜,忍不住說著了,起床的時候已經人去樓空… 站內有許多大佬給出CPA的詳細部屬的教程,我這邊分享一個奴役AI寫的腳本,把需要的東西放好,一鍵執行,就可以把你的CPA弄好,希望對大家有幫助,如果奧特曼在發免費禮物,party狂歡的時候大家也能一同參與 (sh 腳本 改成txt 因為不然不能上船,直接貼出來,上船死活不給) #!/bin/bash # ============================================================================ # CLIProxyAPI One-Click Deploy Script # ============================================================================ # Usage: bash deploy-cpa.sh config.txt # # Config file format (one per line): # [email protected] # CF_API_KEY=your_global_api_key # CF_ACCOUNT_ID=your_account_id # CF_ZONE_ID=your_zone_id # DOMAIN=cpa.yourdomain.com # TOKENS_PATH=/path/to/tokens.zip # API_KEY=sk-cpa-custom-key (optional, auto-generated if missing) # MGMT_KEY=mgt-cpa-custom-key (optional, auto-generated if missing) # TUNNEL_NAME=cpa-proxy (optional, defaults to cpa-proxy) # ============================================================================ set -e # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' log() { echo -e "${GREEN}[✓]${NC} $1"; } warn() { echo -e "${YELLOW}[!]${NC} $1"; } err() { echo -e "${RED}[✗]${NC} $1"; exit 1; } info() { echo -e "${BLUE}[i]${NC} $1"; } # ============================================================================ # Check arguments # ============================================================================ if [ -z "$1" ]; then echo "" echo -e "${CYAN}CLIProxyAPI One-Click Deploy${NC}" echo "" echo "Usage: bash deploy-cpa.sh <config-file>" echo "" echo "Create a config file with the following format:" echo "" echo " [email protected]" echo " CF_API_KEY=your_global_api_key" echo " CF_ACCOUNT_ID=your_account_id" echo " CF_ZONE_ID=your_zone_id" echo " DOMAIN=cpa.yourdomain.com" echo " TOKENS_PATH=/path/to/tokens.zip" echo "" echo "Optional:" echo " API_KEY=sk-cpa-custom-key" echo " MGMT_KEY=mgt-cpa-custom-key" echo " TUNNEL_NAME=cpa-proxy" echo "" exit 1 fi CONFIG_FILE="$1" [ ! -f "$CONFIG_FILE" ] && err "Config file not found: $CONFIG_FILE" # ============================================================================ # Load config # ============================================================================ info "Loading config from $CONFIG_FILE..." # Source the config file set -a source "$CONFIG_FILE" set +a # Validate required fields [ -z "$CF_EMAIL" ] && err "CF_EMAIL is required" [ -z "$CF_API_KEY" ] && err "CF_API_KEY is required" [ -z "$CF_ACCOUNT_ID" ] && err "CF_ACCOUNT_ID is required" [ -z "$CF_ZONE_ID" ] && err "CF_ZONE_ID is required" [ -z "$DOMAIN" ] && err "DOMAIN is required (e.g., cpa.yourdomain.com)" [ -z "$TOKENS_PATH" ] && err "TOKENS_PATH is required (path to token zip files)" # Set defaults TUNNEL_NAME="${TUNNEL_NAME:-cpa-proxy}" API_KEY="${API_KEY:-sk-cpa-$(tr -dc 'a-z0-9' </dev/urandom | head -c 32)}" MGMT_KEY="${MGMT_KEY:-mgt-cpa-$(tr -dc 'a-z0-9' </dev/urandom | head -c 32)}" INSTALL_DIR="/root/cpa" AUTH_DIR="/root/.cli-proxy-api" # Extract subdomain and root domain for DNS SUBDOMAIN=$(echo "$DOMAIN" | cut -d. -f1) ROOT_DOMAIN=$(echo "$DOMAIN" | cut -d. -f2-) log "Config loaded successfully" info "Domain: $DOMAIN" info "Tunnel: $TUNNEL_NAME" info "Tokens: $TOKENS_PATH" # ============================================================================ # Step 1: System setup # ============================================================================ echo "" echo -e "${CYAN}=== Step 1: System Setup ===${NC}" # Update and install dependencies apt-get update -qq apt-get install -y -qq curl unzip python3 jq p7zip-full > /dev/null 2>&1 log "System dependencies installed" # ============================================================================ # Step 2: Install Docker # ============================================================================ echo "" echo -e "${CYAN}=== Step 2: Docker Setup ===${NC}" if command -v docker &> /dev/null; then log "Docker already installed: $(docker --version)" else info "Installing Docker..." curl -fsSL https://get.docker.com | sh > /dev/null 2>&1 log "Docker installed: $(docker --version)" fi # ============================================================================ # Step 3: Install cloudflared # ============================================================================ echo "" echo -e "${CYAN}=== Step 3: Cloudflared Setup ===${NC}" if command -v cloudflared &> /dev/null; then log "cloudflared already installed: $(cloudflared --version | head -1)" else info "Installing cloudflared..." curl -fsSL https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared chmod +x /usr/local/bin/cloudflared log "cloudflared installed: $(cloudflared --version | head -1)" fi # ============================================================================ # Step 4: Prepare tokens # ============================================================================ echo "" echo -e "${CYAN}=== Step 4: Token Import ===${NC}" mkdir -p "$AUTH_DIR" mkdir -p "$INSTALL_DIR/auths" # Handle token files - support zip, json, or directory of zips if [ -d "$TOKENS_PATH" ]; then # Directory - check for JSON files first, then zip files JSON_COUNT=$(find "$TOKENS_PATH" -maxdepth 1 -name "*.json" 2>/dev/null | wc -l) ZIP_COUNT=$(find "$TOKENS_PATH" -maxdepth 1 -name "*.zip" 2>/dev/null | wc -l) if [ "$JSON_COUNT" -gt 0 ]; then info "Found $JSON_COUNT JSON files in directory" cp "$TOKENS_PATH"/*.json "$INSTALL_DIR/auths/" 2>/dev/null || true fi if [ "$ZIP_COUNT" -gt 0 ]; then info "Found $ZIP_COUNT zip files in directory" for zipfile in "$TOKENS_PATH"/*.zip; do [ -f "$zipfile" ] && unzip -o "$zipfile" -d "$INSTALL_DIR/auths/" > /dev/null 2>&1 done fi # Also check for 7z files SEVEN_COUNT=$(find "$TOKENS_PATH" -maxdepth 1 -name "*.7z" 2>/dev/null | wc -l) if [ "$SEVEN_COUNT" -gt 0 ]; then info "Found $SEVEN_COUNT 7z files in directory" for sevenfile in "$TOKENS_PATH"/*.7z; do [ -f "$sevenfile" ] && 7z x -y -o"$INSTALL_DIR/auths/" "$sevenfile" > /dev/null 2>&1 done fi if [ "$JSON_COUNT" -eq 0 ] && [ "$ZIP_COUNT" -eq 0 ] && [ "$SEVEN_COUNT" -eq 0 ]; then err "No .json, .zip, or .7z files found in $TOKENS_PATH" fi elif [ -f "$TOKENS_PATH" ] && [[ "$TOKENS_PATH" == *.zip ]]; then # Single zip file info "Extracting tokens from zip..." unzip -o "$TOKENS_PATH" -d "$INSTALL_DIR/auths/" > /dev/null 2>&1 elif [ -f "$TOKENS_PATH" ] && [[ "$TOKENS_PATH" == *.7z ]]; then # Single 7z file info "Extracting tokens from 7z..." 7z x -y -o"$INSTALL_DIR/auths/" "$TOKENS_PATH" > /dev/null 2>&1 elif [ -f "$TOKENS_PATH" ] && [[ "$TOKENS_PATH" == *.json ]]; then # Single JSON file cp "$TOKENS_PATH" "$INSTALL_DIR/auths/" else err "TOKENS_PATH must be a directory, .zip, .7z, or .json file" fi TOKEN_COUNT=$(find "$INSTALL_DIR/auths" -name "*.json" | wc -l) [ "$TOKEN_COUNT" -eq 0 ] && err "No token files found in $TOKENS_PATH" # Convert tokens to CLIProxyAPI format info "Converting $TOKEN_COUNT tokens to CLIProxyAPI format..." python3 << PYEOF import json, os, glob, base64, datetime auth_dir = "$INSTALL_DIR/auths" files = glob.glob(os.path.join(auth_dir, "*.json")) converted = 0 errors = 0 for f in files: try: with open(f) as fh: data = json.load(fh) # Skip if already in correct format if "expired" in data and "last_refresh" in data: continue # Extract expiry from access_token JWT access_token = data.get("access_token", "") expire = "" account_id = "" if access_token and "." in access_token: parts = access_token.split(".") if len(parts) >= 2: payload = parts[1] + "=" * (4 - len(parts[1]) % 4) try: claims = json.loads(base64.b64decode(payload)) exp_ts = claims.get("exp", 0) if exp_ts: expire = datetime.datetime.fromtimestamp(exp_ts, tz=datetime.timezone.utc).isoformat() auth = claims.get("https://api.openai.com/auth", {}) account_id = auth.get("chatgpt_account_id", "") except: pass # Convert to CLIProxyAPI format new_data = { "id_token": data.get("id_token", ""), "access_token": data.get("access_token", ""), "refresh_token": data.get("refresh_token", ""), "account_id": account_id, "last_refresh": data.get("saved_at", datetime.datetime.now(tz=datetime.timezone.utc).isoformat()), "email": data.get("email", ""), "type": "codex", "expired": expire } with open(f, 'w') as fh: json.dump(new_data, fh) converted += 1 except Exception as e: errors += 1 print(f"Converted: {converted}, Errors: {errors}") PYEOF log "Token conversion complete: $TOKEN_COUNT tokens ready" # ============================================================================ # Step 5: Create CLIProxyAPI config # ============================================================================ echo "" echo -e "${CYAN}=== Step 5: CLIProxyAPI Configuration ===${NC}" cat > "$INSTALL_DIR/config.yaml" << EOF # CLIProxyAPI Configuration - Auto-generated by deploy-cpa.sh host: "127.0.0.1" port: 8317 auth-dir: "/root/.cli-proxy-api" request-retry: 3 max-retry-credentials: 5 quota-exceeded: switch-project: true switch-preview-model: true api-keys: - "$API_KEY" remote-management: allow-remote: true secret-key: "$MGMT_KEY" disable-control-panel: false logging-to-file: true usage-statistics-enabled: true logs-max-total-size-mb: 100 # Prevent Cloudflare blocking -> payment_required false positive disable-cooling: true # Set Codex CLI User-Agent to bypass Cloudflare codex-header-defaults: user-agent: "codex_cli_rs/0.114.0 (Mac OS 14.2.0; x86_64) vscode/1.111.0" beta-features: "multi_agent" routing: strategy: "round-robin" session-affinity: true session-affinity-ttl: "30m" ws-auth: false debug: false EOF log "Config created at $INSTALL_DIR/config.yaml" # ============================================================================ # Step 6: Create docker-compose.yml # ============================================================================ echo "" echo -e "${CYAN}=== Step 6: Docker Compose Setup ===${NC}" cat > "$INSTALL_DIR/docker-compose.yml" << 'EOF' services: cli-proxy-api: image: eceasy/cli-proxy-api:latest pull_policy: always container_name: cli-proxy-api network_mode: host volumes: - ./config.yaml:/CLIProxyAPI/config.yaml - ./auths:/root/.cli-proxy-api - ./logs:/CLIProxyAPI/logs restart: unless-stopped EOF log "docker-compose.yml created" # ============================================================================ # Step 7: Start CLIProxyAPI # ============================================================================ echo "" echo -e "${CYAN}=== Step 7: Starting CLIProxyAPI ===${NC}" cd "$INSTALL_DIR" # Stop and remove existing container if it exists docker stop cli-proxy-api 2>/dev/null || true docker rm cli-proxy-api 2>/dev/null || true docker compose up -d 2>&1 # Wait for startup sleep 5 # Verify it's running if docker ps | grep -q cli-proxy-api; then log "CLIProxyAPI is running" docker logs cli-proxy-api 2>&1 | tail -3 else err "CLIProxyAPI failed to start. Check: docker logs cli-proxy-api" fi # ============================================================================ # Step 8: Create Cloudflare Tunnel # ============================================================================ echo "" echo -e "${CYAN}=== Step 8: Cloudflare Tunnel Setup ===${NC}" # Check if tunnel already exists EXISTING_TUNNEL=$(curl -s "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/cfd_tunnel?name=${TUNNEL_NAME}" \ -H "X-Auth-Email: ${CF_EMAIL}" \ -H "X-Auth-Key: ${CF_API_KEY}" | python3 -c "import sys, json; d=json.load(sys.stdin); t=[x for x in d.get('result',[]) if x['name']=='${TUNNEL_NAME}']; print(t[0]['id'] if t else '')" 2>/dev/null) if [ -n "$EXISTING_TUNNEL" ]; then warn "Tunnel '$TUNNEL_NAME' already exists (ID: $EXISTING_TUNNEL)" TUNNEL_ID="$EXISTING_TUNNEL" # Get existing token TUNNEL_TOKEN=$(curl -s "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/cfd_tunnel/${TUNNEL_ID}/token" \ -H "X-Auth-Email: ${CF_EMAIL}" \ -H "X-Auth-Key: ${CF_API_KEY}" | python3 -c "import sys, json; print(json.load(sys.stdin)['result'])" 2>/dev/null) else # Create new tunnel info "Creating tunnel '$TUNNEL_NAME'..." TUNNEL_RESULT=$(curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/cfd_tunnel" \ -H "X-Auth-Email: ${CF_EMAIL}" \ -H "X-Auth-Key: ${CF_API_KEY}" \ -H "Content-Type: application/json" \ -d "{\"name\":\"${TUNNEL_NAME}\"}") TUNNEL_ID=$(echo "$TUNNEL_RESULT" | python3 -c "import sys, json; print(json.load(sys.stdin)['result']['id'])" 2>/dev/null) TUNNEL_TOKEN=$(echo "$TUNNEL_RESULT" | python3 -c "import sys, json; print(json.load(sys.stdin)['result']['token'])" 2>/dev/null) [ -z "$TUNNEL_ID" ] && err "Failed to create tunnel: $TUNNEL_RESULT" log "Tunnel created: $TUNNEL_ID" fi # Configure tunnel ingress info "Configuring tunnel routing..." curl -s -X PUT "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/cfd_tunnel/${TUNNEL_ID}/configurations" \ -H "X-Auth-Email: ${CF_EMAIL}" \ -H "X-Auth-Key: ${CF_API_KEY}" \ -H "Content-Type: application/json" \ -d "{ \"config\": { \"ingress\": [ { \"hostname\": \"${DOMAIN}\", \"service\": \"http://localhost:8317\", \"originRequest\": { \"noTLSVerify\": true } }, { \"service\": \"http_status:404\" } ] } }" > /dev/null 2>&1 log "Tunnel routing configured" # ============================================================================ # Step 9: DNS Record # ============================================================================ echo "" echo -e "${CYAN}=== Step 9: DNS Record Setup ===${NC}" # Check if DNS record already exists EXISTING_DNS=$(curl -s "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records?name=${DOMAIN}" \ -H "X-Auth-Email: ${CF_EMAIL}" \ -H "X-Auth-Key: ${CF_API_KEY}" | python3 -c "import sys, json; d=json.load(sys.stdin); print(d['result'][0]['id'] if d['result'] else '')" 2>/dev/null) if [ -n "$EXISTING_DNS" ]; then # Update existing record to point to new tunnel info "Updating existing DNS record to point to new tunnel..." curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records/${EXISTING_DNS}" \ -H "X-Auth-Email: ${CF_EMAIL}" \ -H "X-Auth-Key: ${CF_API_KEY}" \ -H "Content-Type: application/json" \ -d "{\"content\":\"${TUNNEL_ID}.cfargotunnel.com\"}" > /dev/null 2>&1 log "DNS record updated: $DOMAIN -> tunnel" else info "Creating CNAME record for $DOMAIN..." curl -s -X POST "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records" \ -H "X-Auth-Email: ${CF_EMAIL}" \ -H "X-Auth-Key: ${CF_API_KEY}" \ -H "Content-Type: application/json" \ -d "{ \"type\": \"CNAME\", \"name\": \"${SUBDOMAIN}\", \"content\": \"${TUNNEL_ID}.cfargotunnel.com\", \"proxied\": true, \"ttl\": 1 }" > /dev/null 2>&1 log "DNS record created: $DOMAIN -> tunnel" fi # ============================================================================ # Step 10: WAF Skip Rule # ============================================================================ echo "" echo -e "${CYAN}=== Step 10: WAF Configuration ===${NC}" # Get the firewall custom ruleset RULESET_ID=$(curl -s "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/rulesets" \ -H "X-Auth-Email: ${CF_EMAIL}" \ -H "X-Auth-Key: ${CF_API_KEY}" | python3 -c " import sys, json d = json.load(sys.stdin) for r in d.get('result', []): if r['phase'] == 'http_request_firewall_custom' and r['name'] == 'default': print(r['id']) break " 2>/dev/null) if [ -n "$RULESET_ID" ]; then # Get existing rules curl -s "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/rulesets/${RULESET_ID}" \ -H "X-Auth-Email: ${CF_EMAIL}" \ -H "X-Auth-Key: ${CF_API_KEY}" > /tmp/cf_ruleset.json # Check if rule already exists if grep -q "CPA API" /tmp/cf_ruleset.json 2>/dev/null; then warn "WAF skip rule already exists" else # Add skip rule python3 << PYEOF import json with open('/tmp/cf_ruleset.json') as f: data = json.load(f) rules = data.get('result', {}).get('rules', []) rules.append({ 'expression': 'starts_with(http.request.uri.path, "/v1/")', 'description': 'CPA API - Skip WAF', 'action': 'skip', 'action_parameters': {'ruleset': 'current'}, 'enabled': True }) with open('/tmp/cf_update.json', 'w') as f: json.dump({'rules': rules}, f) PYEOF curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/rulesets/${RULESET_ID}" \ -H "X-Auth-Email: ${CF_EMAIL}" \ -H "X-Auth-Key: ${CF_API_KEY}" \ -H "Content-Type: application/json" \ -d @/tmp/cf_update.json > /dev/null 2>&1 log "WAF skip rule added for /v1/* paths" fi else warn "Could not find WAF ruleset, skipping (you may need to add manually)" fi # ============================================================================ # Step 11: Install cloudflared service # ============================================================================ echo "" echo -e "${CYAN}=== Step 11: Cloudflared Service ===${NC}" # Stop existing service if running systemctl stop cloudflared 2>/dev/null || true # Uninstall existing service if needed cloudflared service uninstall 2>/dev/null || true # Install with token cloudflared service install "$TUNNEL_TOKEN" 2>&1 systemctl daemon-reload systemctl start cloudflared sleep 3 if systemctl is-active --quiet cloudflared; then log "cloudflared service is running" else err "cloudflared failed to start. Check: journalctl -u cloudflared" fi # ============================================================================ # Step 12: Verify deployment # ============================================================================ echo "" echo -e "${CYAN}=== Step 12: Verification ===${NC}" # Wait for tunnel to connect sleep 5 # Test local endpoint LOCAL_TEST=$(curl -s --max-time 10 http://127.0.0.1:8317/v1/models \ -H "Authorization: Bearer $API_KEY" 2>/dev/null | python3 -c "import sys, json; d=json.load(sys.stdin); print(len(d.get('data',[])))" 2>/dev/null) if [ "$LOCAL_TEST" -gt 0 ] 2>/dev/null; then log "Local API working: $LOCAL_TEST models available" else warn "Local API test failed - tokens may need time to load" fi # Test tunnel endpoint sleep 3 TUNNEL_TEST=$(curl -s --max-time 15 "https://${DOMAIN}/v1/models" \ -H "Authorization: Bearer $API_KEY" 2>/dev/null | python3 -c "import sys, json; d=json.load(sys.stdin); print(len(d.get('data',[])))" 2>/dev/null) if [ "$TUNNEL_TEST" -gt 0 ] 2>/dev/null; then log "Tunnel API working: $TUNNEL_TEST models available" else warn "Tunnel test failed - DNS may need time to propagate" fi # ============================================================================ # Print Summary # ============================================================================ echo "" echo "" echo -e "${GREEN}============================================================================${NC}" echo -e "${GREEN} CLIProxyAPI Deploy Complete!${NC}" echo -e "${GREEN}============================================================================${NC}" echo "" echo -e "${CYAN}API Endpoint:${NC} https://${DOMAIN}" echo -e "${CYAN}API Key:${NC} ${API_KEY}" echo -e "${CYAN}Management:${NC} https://${DOMAIN}/management.html" echo -e "${CYAN}Mgmt Key:${NC} ${MGMT_KEY}" echo "" echo -e "${CYAN}Token Count:${NC} ${TOKEN_COUNT}" echo -e "${CYAN}Models:${NC} gpt-5.4, gpt-5.4-mini, gpt-5.5, codex-auto-review, gpt-image-2" echo "" echo -e "${CYAN}Install Dir:${NC} ${INSTALL_DIR}" echo -e "${CYAN}Auth Dir:${NC} ${INSTALL_DIR}/auths" echo -e "${CYAN}Logs:${NC} ${INSTALL_DIR}/logs" echo "" echo -e "${YELLOW}Test Commands:${NC}" echo " curl https://${DOMAIN}/v1/models -H 'Authorization: Bearer ${API_KEY}'" echo " curl https://${DOMAIN}/v1/chat/completions -H 'Authorization: Bearer ${API_KEY}' -H 'Content-Type: application/json' -d '{\"model\":\"gpt-5.5\",\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}],\"max_tokens\":10}'" echo "" echo -e "${YELLOW}Management Commands:${NC}" echo " docker logs -f cli-proxy-api # View logs" echo " docker restart cli-proxy-api # Restart" echo " cd ${INSTALL_DIR} && docker compose up -d --pull always # Update" echo "" echo -e "${GREEN}============================================================================${NC}" # Save credentials to file cat > "$INSTALL_DIR/credentials.txt" << EOF CLIProxyAPI Credentials Generated: $(date) API Endpoint: https://${DOMAIN} API Key: ${API_KEY} Management: https://${DOMAIN}/management.html Management Key: ${MGMT_KEY} Tunnel ID: ${TUNNEL_ID} Tunnel Name: ${TUNNEL_NAME} Install Dir: ${INSTALL_DIR} Auth Dir: ${INSTALL_DIR}/auths EOF log "Credentials saved to $INSTALL_DIR/credentials.txt" 準備工作 Cloudflare 帳號(免費即可) 一個域名托管在 Cloudflare 上 ChatGPT Token 文件(.zip / .7z / .json) 使用步驟 1. 複製模板 cp cpa-config-template.txt my-config.txt 2. 填入你的資訊(5 個必填項) nano my-config.txt 3. 一鍵部署 bash deploy-cpa.sh my-config.txt 必填的 5 個欄位 ┌───────────────┬─────────────────────┬───────────────────────────────┐ │ 欄位 │ 說明 │ 在哪裡找 │ ├───────────────┼─────────────────────┼───────────────────────────────┤ │ CF_EMAIL │ Cloudflare 登錄郵箱 │ 你的帳號 │ ├───────────────┼─────────────────────┼───────────────────────────────┤ │ CF_API_KEY │ Global API Key │ CF後台 → 個人資料 → API Token │ ├───────────────┼─────────────────────┼───────────────────────────────┤ │ CF_ACCOUNT_ID │ 帳號 ID │ 任意域名首頁右側欄 │ ├───────────────┼─────────────────────┼───────────────────────────────┤ │ CF_ZONE_ID │ 域名 Zone ID │ 具體域名首頁右側欄 │ ├───────────────┼─────────────────────┼───────────────────────────────┤ │ DOMAIN │ 要用的子域名 │ 例如 cpa.你的域名.com │ ├───────────────┼─────────────────────┼───────────────────────────────┤ │ TOKENS_PATH │ Token 文件路徑 │ 支持 .zip/.7z/.json/目錄 │ └───────────────┴─────────────────────┴───────────────────────────────┘ 部署完成後 腳本會顯示: API 地址: https://cpa.你的域名.com API Key:客戶端連接用的密鑰 管理面板: https://cpa.你的域名.com/management.html 把 API 地址和 Key 填進 Cherry Studio 等客戶端即可使用。 4 个帖子 - 4 位参与者 阅读完整话题

v2ex · 2026-05-29 15:43:22+08:00 · tech

PartyBox - 聚会桌游小工具 1.0 版本核心功能: 随机与游戏工具:掷骰子、抛硬币、大转盘、随机抽签 计分与计数:计分板、计数器 计时与团队分组:计时器、随机分组 上架到 Google Play 了,纯离线小工具,感觉有时候玩点桌游或者线下聚会的时候还有点用,做着玩的哈哈!大部分功能都不需要兑换码其实~ Promotion code: EXLD5BHD3QWSHBHYEAVHCRQ 1X6988TBQJABP9DG142X5FM 7HLGZ2UTLJ8C00S184P28KR 9VY18CDPF9PD8KPBGX4H4G7 XH4XSEFAT92WCJ7JJ5Z7VEB P4ZQWTPMGHKX0A8DM9KUNAQ Q4ELPED8NEZSCH12194213S S3LFR51G9DX0MZY9RYF3U2M AS90B32N5W1MJ6B105L55YY EHM7UQKS1UYEQ7S4CR5ENMJ VQMFRJNTTFZ7T7GR1PP0RZN UHV7UEM1SCCHNYRJHW02BGT E429EEAW9EKWD8HF6SLEK78 JX2WGMHVZ8KDTP14ZCUG0HC QD9ZM620MD3U5MBDB7GKKFN NYBV4XYCTQE3C9C08H79DQN TPWBGUH8WXGSUR56WGYY1WT WDLKXD467J5PTQM59RHVKZB CKQ7USZ8BK1AQ5MMQCL8K8Y QJ6MMP1BTXCPVASVGF4QHDT YR24M32GA0LD7ZVJ0XXF3BK UX9YD3AQH7PP926CWNTXCBT Y4DNZ3A5XET1TKE576D43FP N9CUZBT86ZBGG9JDVVQWPMR 46PG1VUZBCN0Y4Q2K93VEET LZDU31GFM9AC1778E3PPARJ PZE6BX2AX5EYL0ZQM5FVZ9F SNLB3R66HW7VQGQGZX71EHJ J2KEN37SM3ZJH53MQAD3WKX WEGH04511T2DGRXVCMSPCB5 9WDEV5FXW2HNQU8TGUPWS82 D5CYAV46W1QR8BU47Y3VD9K 58EH4Y3D2RCVHQHLDU50T96 6KALN32B2CSDM7VGFW601G4 4MZG7ASYJ4EBY0X7VH0JQ7Y Z7WZ1RD051X92RQ1M0EWST5 PB2UP7GA9VLQS77V00D4UQD 35YVK8AQRVFX22D3PU26MLW 515P2L46GCWESR30P6BFWU1 U7XTUTW91K2NK8042Q535N4 5P7ASGT6F8UQ8YVGUG6XZ8N ES3M7QK6J7WQF155MBM9WQ9 3GRQ1X3B36B8DLGEG0NCJ91 50UX0GRRECJ8L0BTV6Q7Y8B 78YNRVJC2YC8474T0413ZHH KXTZG1P7PS4WLES7UCFYM0Y JDEJ7EXKHSEC3KBDJA8YB08 RQHPUBTTU6DWQC1D4YBZFSR Y3340P4Z0ELY9C8MNE3761D TYQAK92BY70NQ0A7KEKBAD4

v2ex · 2026-05-29 15:41:05+08:00 · tech

PartyBox - 聚会桌游小工具 1.0 版本核心功能: 随机与游戏工具:掷骰子、抛硬币、大转盘、随机抽签 计分与计数:计分板、计数器 计时与团队分组:计时器、随机分组 上架到 Google Play 了,纯离线小工具,感觉有时候玩点桌游或者线下聚会的时候还有点用,做着玩的哈哈!大部分功能都不需要兑换码其实~ Promotion code: EXLD5BHD3QWSHBHYEAVHCRQ 1X6988TBQJABP9DG142X5FM 7HLGZ2UTLJ8C00S184P28KR 9VY18CDPF9PD8KPBGX4H4G7 XH4XSEFAT92WCJ7JJ5Z7VEB P4ZQWTPMGHKX0A8DM9KUNAQ Q4ELPED8NEZSCH12194213S S3LFR51G9DX0MZY9RYF3U2M AS90B32N5W1MJ6B105L55YY EHM7UQKS1UYEQ7S4CR5ENMJ VQMFRJNTTFZ7T7GR1PP0RZN UHV7UEM1SCCHNYRJHW02BGT E429EEAW9EKWD8HF6SLEK78 JX2WGMHVZ8KDTP14ZCUG0HC QD9ZM620MD3U5MBDB7GKKFN NYBV4XYCTQE3C9C08H79DQN TPWBGUH8WXGSUR56WGYY1WT WDLKXD467J5PTQM59RHVKZB CKQ7USZ8BK1AQ5MMQCL8K8Y QJ6MMP1BTXCPVASVGF4QHDT YR24M32GA0LD7ZVJ0XXF3BK UX9YD3AQH7PP926CWNTXCBT Y4DNZ3A5XET1TKE576D43FP N9CUZBT86ZBGG9JDVVQWPMR 46PG1VUZBCN0Y4Q2K93VEET LZDU31GFM9AC1778E3PPARJ PZE6BX2AX5EYL0ZQM5FVZ9F SNLB3R66HW7VQGQGZX71EHJ J2KEN37SM3ZJH53MQAD3WKX WEGH04511T2DGRXVCMSPCB5 9WDEV5FXW2HNQU8TGUPWS82 D5CYAV46W1QR8BU47Y3VD9K 58EH4Y3D2RCVHQHLDU50T96 6KALN32B2CSDM7VGFW601G4 4MZG7ASYJ4EBY0X7VH0JQ7Y Z7WZ1RD051X92RQ1M0EWST5 PB2UP7GA9VLQS77V00D4UQD 35YVK8AQRVFX22D3PU26MLW 515P2L46GCWESR30P6BFWU1 U7XTUTW91K2NK8042Q535N4 5P7ASGT6F8UQ8YVGUG6XZ8N ES3M7QK6J7WQF155MBM9WQ9 3GRQ1X3B36B8DLGEG0NCJ91 50UX0GRRECJ8L0BTV6Q7Y8B 78YNRVJC2YC8474T0413ZHH KXTZG1P7PS4WLES7UCFYM0Y JDEJ7EXKHSEC3KBDJA8YB08 RQHPUBTTU6DWQC1D4YBZFSR Y3340P4Z0ELY9C8MNE3761D TYQAK92BY70NQ0A7KEKBAD4

v2ex · 2026-05-29 15:26:44+08:00 · tech

PartyBox - 聚会桌游小工具 1.0 版本核心功能: 随机与游戏工具:掷骰子、抛硬币、大转盘、随机抽签 计分与计数:计分板、计数器 计时与团队分组:计时器、随机分组 上架到 Google Play 了,纯离线小工具,感觉有时候玩点桌游或者线下聚会的时候还有点用,做着玩的哈哈!大部分功能都不需要兑换码其实~ Promotion code: EXLD5BHD3QWSHBHYEAVHCRQ 1X6988TBQJABP9DG142X5FM 7HLGZ2UTLJ8C00S184P28KR 9VY18CDPF9PD8KPBGX4H4G7 XH4XSEFAT92WCJ7JJ5Z7VEB P4ZQWTPMGHKX0A8DM9KUNAQ Q4ELPED8NEZSCH12194213S S3LFR51G9DX0MZY9RYF3U2M AS90B32N5W1MJ6B105L55YY EHM7UQKS1UYEQ7S4CR5ENMJ VQMFRJNTTFZ7T7GR1PP0RZN UHV7UEM1SCCHNYRJHW02BGT E429EEAW9EKWD8HF6SLEK78 JX2WGMHVZ8KDTP14ZCUG0HC QD9ZM620MD3U5MBDB7GKKFN NYBV4XYCTQE3C9C08H79DQN TPWBGUH8WXGSUR56WGYY1WT WDLKXD467J5PTQM59RHVKZB CKQ7USZ8BK1AQ5MMQCL8K8Y QJ6MMP1BTXCPVASVGF4QHDT YR24M32GA0LD7ZVJ0XXF3BK UX9YD3AQH7PP926CWNTXCBT Y4DNZ3A5XET1TKE576D43FP N9CUZBT86ZBGG9JDVVQWPMR 46PG1VUZBCN0Y4Q2K93VEET LZDU31GFM9AC1778E3PPARJ PZE6BX2AX5EYL0ZQM5FVZ9F SNLB3R66HW7VQGQGZX71EHJ J2KEN37SM3ZJH53MQAD3WKX WEGH04511T2DGRXVCMSPCB5 9WDEV5FXW2HNQU8TGUPWS82 D5CYAV46W1QR8BU47Y3VD9K 58EH4Y3D2RCVHQHLDU50T96 6KALN32B2CSDM7VGFW601G4 4MZG7ASYJ4EBY0X7VH0JQ7Y Z7WZ1RD051X92RQ1M0EWST5 PB2UP7GA9VLQS77V00D4UQD 35YVK8AQRVFX22D3PU26MLW 515P2L46GCWESR30P6BFWU1 U7XTUTW91K2NK8042Q535N4 5P7ASGT6F8UQ8YVGUG6XZ8N ES3M7QK6J7WQF155MBM9WQ9 3GRQ1X3B36B8DLGEG0NCJ91 50UX0GRRECJ8L0BTV6Q7Y8B 78YNRVJC2YC8474T0413ZHH KXTZG1P7PS4WLES7UCFYM0Y JDEJ7EXKHSEC3KBDJA8YB08 RQHPUBTTU6DWQC1D4YBZFSR Y3340P4Z0ELY9C8MNE3761D TYQAK92BY70NQ0A7KEKBAD4

v2ex · 2026-05-29 14:47:58+08:00 · tech

PartyBox - 聚会桌游小工具 1.0 版本核心功能: 随机与游戏工具:掷骰子、抛硬币、大转盘、随机抽签 计分与计数:计分板、计数器 计时与团队分组:计时器、随机分组 上架到 Google Play 了,纯离线小工具,感觉有时候玩点桌游或者线下聚会的时候还有点用,做着玩的哈哈!大部分功能都不需要兑换码其实~ Promotion code: EXLD5BHD3QWSHBHYEAVHCRQ 1X6988TBQJABP9DG142X5FM 7HLGZ2UTLJ8C00S184P28KR 9VY18CDPF9PD8KPBGX4H4G7 XH4XSEFAT92WCJ7JJ5Z7VEB P4ZQWTPMGHKX0A8DM9KUNAQ Q4ELPED8NEZSCH12194213S S3LFR51G9DX0MZY9RYF3U2M AS90B32N5W1MJ6B105L55YY EHM7UQKS1UYEQ7S4CR5ENMJ VQMFRJNTTFZ7T7GR1PP0RZN UHV7UEM1SCCHNYRJHW02BGT E429EEAW9EKWD8HF6SLEK78 JX2WGMHVZ8KDTP14ZCUG0HC QD9ZM620MD3U5MBDB7GKKFN NYBV4XYCTQE3C9C08H79DQN TPWBGUH8WXGSUR56WGYY1WT WDLKXD467J5PTQM59RHVKZB CKQ7USZ8BK1AQ5MMQCL8K8Y QJ6MMP1BTXCPVASVGF4QHDT YR24M32GA0LD7ZVJ0XXF3BK UX9YD3AQH7PP926CWNTXCBT Y4DNZ3A5XET1TKE576D43FP N9CUZBT86ZBGG9JDVVQWPMR 46PG1VUZBCN0Y4Q2K93VEET LZDU31GFM9AC1778E3PPARJ PZE6BX2AX5EYL0ZQM5FVZ9F SNLB3R66HW7VQGQGZX71EHJ J2KEN37SM3ZJH53MQAD3WKX WEGH04511T2DGRXVCMSPCB5 9WDEV5FXW2HNQU8TGUPWS82 D5CYAV46W1QR8BU47Y3VD9K 58EH4Y3D2RCVHQHLDU50T96 6KALN32B2CSDM7VGFW601G4 4MZG7ASYJ4EBY0X7VH0JQ7Y Z7WZ1RD051X92RQ1M0EWST5 PB2UP7GA9VLQS77V00D4UQD 35YVK8AQRVFX22D3PU26MLW 515P2L46GCWESR30P6BFWU1 U7XTUTW91K2NK8042Q535N4 5P7ASGT6F8UQ8YVGUG6XZ8N ES3M7QK6J7WQF155MBM9WQ9 3GRQ1X3B36B8DLGEG0NCJ91 50UX0GRRECJ8L0BTV6Q7Y8B 78YNRVJC2YC8474T0413ZHH KXTZG1P7PS4WLES7UCFYM0Y JDEJ7EXKHSEC3KBDJA8YB08 RQHPUBTTU6DWQC1D4YBZFSR Y3340P4Z0ELY9C8MNE3761D TYQAK92BY70NQ0A7KEKBAD4

LinuxDo 最新话题 · 2026-05-15 21:11:24+08:00 · tech

突然看到了这个方案(参见原帖),之前一直默认配置,导致要使用本地docker部署的Sub2Api里的free号池时,总是要去切换节点(日常习惯用HK)。但是原帖是FIClash的,我也不清楚有多大区别,但是Clash Party用不了,可能Clash Verge也用不了,故让Claude 搓了一下。Clash Party具体使用方式: 导入步骤 : 打开 Clash Party → 左侧"覆写" 点"+"→ 类型选 JavaScript → 导入本地文件 sing-mix.js (即下面的js源码) 回到"订阅"→ 目标订阅 → “编辑信息”→ 在"覆写"里勾选刚导入的脚本 原帖: [Mihomo覆写] DNS泄露 & AI分流 の 最终解决方案 —— sing-mix 开发调优 终于找到了究极配置,佬太强了,这份配置超级棒,感谢分享! // Clash Party / Mihomo Party 覆写脚本 // 入口: function main(config) { ... return config } // 导入方式: Clash Party → 覆写 → 导入(本地文件 / URL)→ 在订阅"编辑信息"里挂载 // ==================== // 0. 特殊域名处理(手动添加) // ==================== // 强制直连 const BYPASS_DOMAINS = [ "example.com", "example.org" ]; // 强制代理 const FORCE_PROXY_DOMAINS = [ "test.com", "test.org" ]; // ==================== // 1. 常量配置 // ==================== const SETTINGS = { ICON_BASE: "https://cdn.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/", GEOIP_URL: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat", GEOSITE_URL: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat", REGION_ORDER: ["HK", "TW", "SG", "JP", "KR", "AS", "US"], URL_TEST_EXTRA: { hidden: true, url: "https://www.g.cn/generate_204", interval: 900, tolerance: 50, lazy: true, timeout: 1000 }, BETTER_FB_EXTRA: { hidden: true, url: "https://www.g.cn/generate_204", interval: 900, tolerance: 750, lazy: true, timeout: 1000, "max-failed-times": 1 }, FILTER_REGEX: /群|邀请|返利|官网|官方|网址|订阅|购买|续费|剩余|到期|过期|流量|备用|邮箱|客服|联系|工单|倒卖|防止|梯子|tg|telegram|电报|发布|重置/i }; // ==================== // 2. 基础工具 // ==================== const uniq = (arr = []) => [...new Set(arr.filter(Boolean))]; const escapeRegex = (s = "") => String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const normalizeName = (name = "") => String(name) .replace(/(IEPL|IPLC|BGP|RELAY|PRO|V\d+)/ig, " $1 ") .replace(/[【】\[\]()()|_\-.,/:~]/g, " ") .replace(/🇭🇰/g, " HK ") .replace(/🇹🇼/g, " TW ") .replace(/🇸🇬/g, " SG ") .replace(/🇯🇵/g, " JP ") .replace(/🇰🇷/g, " KR ") .replace(/🇻🇳|🇹🇭|🇲🇾|🇮🇩|🇵🇭/g, " AS ") .replace(/🇺🇸/g, " US ") .toUpperCase() .replace(/\s+/g, " ") .trim(); const buildRegex = (arr = []) => new RegExp( arr .map((raw) => { const token = String(raw).trim().toUpperCase(); const escaped = escapeRegex(token); return /^[A-Z]{2,3}$/.test(token) ? `(?:^|[^A-Z])${escaped}(?:[^A-Z]|$)` : escaped; }) .join("|"), "i" ); const buildRegions = () => ([ { name: "HK", pattern: ["香港", "HK", "HKG", "HONGKONG", "HONG KONG"], icon: "Hong_Kong.png" }, { name: "TW", pattern: ["台湾", "台北", "新北", "TW", "TWN", "TAIWAN", "TAIPEI", "TPE"], icon: "Taiwan.png" }, { name: "SG", pattern: ["新加坡", "狮城", "SG", "SGP", "SINGAPORE", "SIN"], icon: "Singapore.png" }, { name: "JP", pattern: ["日本", "东京", "大阪", "JP", "JPN", "JAPAN", "TOKYO", "OSAKA", "NRT", "HND", "TYO"], icon: "Japan.png" }, { name: "KR", pattern: ["韩国", "首尔", "KR", "KOR", "KOREA", "SEOUL", "ICN"], icon: "Korea.png" }, { name: "AS", pattern: [ "越南", "泰国", "马来西亚", "印尼", "菲律宾", "VN", "TH", "MY", "ID", "PH", "VIETNAM", "THAILAND", "MALAYSIA", "INDONESIA", "PHILIPPINES", "MANILA" ], icon: "Available.png" }, { name: "US", pattern: [ "美国", "纽约", "洛杉矶", "旧金山", "圣何塞", "西雅图", "芝加哥", "达拉斯", "硅谷", "US", "USA", "UNITEDSTATES", "UNITED STATES", "NEWYORK", "NEW YORK", "LOSANGELES", "LOS ANGELES", "SANFRANCISCO", "SAN FRANCISCO", "SANJOSE", "SAN JOSE", "SEATTLE", "CHICAGO", "DALLAS", "LAX", "SJC", "SFO" ], icon: "United_States.png" } ]).map((r) => ({ ...r, regex: buildRegex(r.pattern) })); const REGIONS = buildRegions(); const buildFakeIpFilter = (bypass = []) => uniq([ "geosite:private", "geosite:google-cn", "geosite:synology", "geosite:cn", ...uniq( bypass.flatMap((domain) => { const d = String(domain || "").trim(); if (!d) return []; return d.includes("*") || d.startsWith("+.") ? [d] : [`+.${d}`]; }) ) ]); const mergeRules = (baseRules = [], extraRules = []) => { const extra = Array.isArray(extraRules) ? extraRules.filter(Boolean) : []; if (!extra.length) return baseRules.slice(); const matchIndex = baseRules.findIndex( (rule) => String(rule).trim().toUpperCase() === "MATCH,MAIN" ); if (matchIndex === -1) return uniq([...baseRules, ...extra]); return uniq([ ...baseRules.slice(0, matchIndex), ...extra, ...baseRules.slice(matchIndex) ]); }; const pickDirectRules = (rules = []) => rules.filter((rule) => { const r = String(rule || "").trim(); if (!r || r.startsWith("#")) return false; return /,DIRECT(?:,|$)/i.test(r); }); // ==================== // 3. 固定规则 // ==================== const STATIC_RULES = [ "GEOSITE,category-ads-all,REJECT", ...uniq(FORCE_PROXY_DOMAINS).map((d) => `DOMAIN,${d},main`), "GEOSITE,private,DIRECT", "GEOIP,private,DIRECT,no-resolve", "GEOSITE,google-cn,DIRECT", "GEOSITE,synology,DIRECT", "DOMAIN-SUFFIX,sharepoint.com,DIRECT", ...uniq(BYPASS_DOMAINS).map((d) => `DOMAIN-SUFFIX,${d},DIRECT`), "GEOSITE,category-ai-!cn,ai", "GEOSITE,telegram,tg", "GEOSITE,gfw,main", "GEOSITE,cn,DIRECT", "GEOIP,CN,DIRECT,no-resolve", "MATCH,main" ]; const STATIC_FAKE_IP_FILTER = buildFakeIpFilter(BYPASS_DOMAINS); // ==================== // 4. 节点处理 // ==================== const ensureConfigObject = (input) => input && typeof input === "object" ? input : {}; const getOriginalProxies = (input) => Array.isArray(input.proxies) ? input.proxies : []; const makeProxyNamesUnique = (proxies = []) => { const used = new Set(); const nextIdx = new Map(); proxies.forEach((p) => { if (!p || !p.name) return; const base = String(p.name); if (!used.has(base)) { used.add(base); nextIdx.set(base, 1); return; } let idx = nextIdx.get(base) ?? 1; let candidate = `${base}_${idx}`; while (used.has(candidate)) candidate = `${base}_${++idx}`; p.name = candidate; used.add(candidate); nextIdx.set(base, idx + 1); }); }; const splitInfoAndNormalProxies = (proxies = [], filterRegex) => proxies.reduce( (acc, proxy) => { if (!proxy || !proxy.name) return acc; (filterRegex.test(proxy.name) ? acc.infoProxies : acc.normalProxies).push(proxy); return acc; }, { infoProxies: [], normalProxies: [] } ); const classifyProxiesByRegion = (normalProxies = [], regions = []) => { const regionGroupsData = regions.map((r) => ({ name: r.name, icon: r.icon, proxies: [] })); const regionGroupMap = new Map(regionGroupsData.map((r) => [r.name, r])); const regionSeen = new Map(regionGroupsData.map((r) => [r.name, new Set()])); const otherProxyNames = []; const otherSeen = new Set(); normalProxies.forEach((proxy) => { const proxyName = proxy.name; const normName = normalizeName(proxyName); const matchedRegion = regions.find((r) => r.regex.test(normName)); if (matchedRegion) { const group = regionGroupMap.get(matchedRegion.name); const seen = regionSeen.get(matchedRegion.name); if (group && seen && !seen.has(proxyName)) { group.proxies.push(proxyName); seen.add(proxyName); } } else if (!otherSeen.has(proxyName)) { otherProxyNames.push(proxyName); otherSeen.add(proxyName); } }); const activeRegions = regionGroupsData .map((r) => ({ ...r, proxies: uniq(r.proxies) })) .filter((r) => r.proxies.length > 0); const activeRegionNameSet = new Set(activeRegions.map((r) => r.name)); const activeRegionMap = new Map(activeRegions.map((r) => [r.name, r])); return { activeRegions, activeRegionNameSet, activeRegionMap, otherProxyNames: uniq(otherProxyNames) }; }; const buildAiProxyList = (activeRegions = [], otherProxyNames = [], allNames = []) => { const nonHk = uniq([ ...activeRegions.filter((r) => r.name !== "HK").flatMap((r) => r.proxies), ...otherProxyNames ]); return nonHk.length ? nonHk : allNames; }; // ==================== // 5. 策略组 // ==================== const buildProxyGroups = ({ allNames, aiNames, activeRegionMap, activeRegionNameSet, otherProxyNames, infoNames }) => { const groups = []; const add = (name, type, proxies, icon = "Available.png", extra = {}) => { proxies = uniq(proxies); if (name && proxies.length) { groups.push({ name, type, proxies, icon: SETTINGS.ICON_BASE + icon, ...extra }); } }; const regionEntries = SETTINGS.REGION_ORDER.filter((rName) => activeRegionNameSet.has(rName)); if (allNames.length) { const mainEntries = ["All", ...regionEntries]; if (otherProxyNames.length) mainEntries.push("Other"); add("main", "select", mainEntries, "Available.png"); add("URL Test - All", "url-test", allNames, "Available.png", SETTINGS.URL_TEST_EXTRA); add("BetterFB - All", "url-test", allNames, "Available.png", SETTINGS.BETTER_FB_EXTRA); add("All", "select", ["BetterFB - All", "URL Test - All", ...allNames], "Available.png"); } if (aiNames.length) { add("URL Test - ai", "url-test", aiNames, "ChatGPT.png", SETTINGS.URL_TEST_EXTRA); add("BetterFB - ai", "url-test", aiNames, "ChatGPT.png", SETTINGS.BETTER_FB_EXTRA); add("ai", "select", ["BetterFB - ai", "URL Test - ai", ...aiNames], "ChatGPT.png"); } if (allNames.length) { const hasSG = activeRegionNameSet.has("SG"); const tgProxies = hasSG ? ["SG", "main"] : ["main"]; add("tg", "select", tgProxies, "Telegram.png"); } SETTINGS.REGION_ORDER.forEach((rName) => { const region = activeRegionMap.get(rName); if (!region) return; add(`URL Test - ${region.name}`, "url-test", region.proxies, region.icon, SETTINGS.URL_TEST_EXTRA); add(`BetterFB - ${region.name}`, "url-test", region.proxies, region.icon, SETTINGS.BETTER_FB_EXTRA); add(region.name, "select", [`BetterFB - ${region.name}`, `URL Test - ${region.name}`, ...region.proxies], region.icon); }); if (otherProxyNames.length) { add("URL Test - Other", "url-test", otherProxyNames, "Available.png", SETTINGS.URL_TEST_EXTRA); add("BetterFB - Other", "url-test", otherProxyNames, "Available.png", SETTINGS.BETTER_FB_EXTRA); add("Other", "select", ["BetterFB - Other", "URL Test - Other", ...otherProxyNames], "Available.png"); } if (infoNames.length) { add("info", "select", infoNames, "Available.png"); } add( "GLOBAL", "select", [ ...(allNames.length ? ["main", "All"] : []), ...(aiNames.length ? ["ai"] : []), ...(allNames.length ? ["tg"] : []), ...regionEntries, ...(otherProxyNames.length ? ["Other"] : []), ...(infoNames.length ? ["info"] : []) ], "Global.png" ); return groups; }; // ==================== // 6. 网络配置 // ==================== const applyGeoData = (cfg) => { cfg["geodata-mode"] = true; cfg["geo-auto-update"] = true; cfg["geo-update-interval"] = 24; cfg["geox-url"] = { ...(cfg["geox-url"] || {}), geoip: SETTINGS.GEOIP_URL, geosite: SETTINGS.GEOSITE_URL }; }; const applySniffer = (cfg) => { cfg.sniffer = { ...(cfg.sniffer || {}), enable: true, "force-dns-mapping": true, "parse-pure-ip": true, "override-destination": true, sniff: { HTTP: { ports: [80, "8080-8880"], "override-destination": true }, TLS: { ports: [443, 8443] }, QUIC: { ports: [443, 8443] } } }; }; const applyTun = (cfg) => { cfg.tun = { ...(cfg.tun || {}), enable: true, stack: "system", "auto-route": true, "auto-detect-interface": true, "strict-route": true, "dns-hijack": ["any:53", "tcp://any:53"] }; }; const applyDns = (cfg) => { const dns = cfg.dns || {}; const fakeIpFilterFromCfg = Array.isArray(dns["fake-ip-filter"]) ? dns["fake-ip-filter"] : []; const nameserverPolicyFromCfg = dns["nameserver-policy"] && typeof dns["nameserver-policy"] === "object" ? dns["nameserver-policy"] : {}; const chinaDNS = [ "system", "https://dns.alidns.com/dns-query", "https://doh.pub/dns-query" ]; const foreignDNS = ["https://1.1.1.1/dns-query#main"]; const directGeoForChinaDNS = [ "geosite:cn", "geosite:google-cn", "geosite:synology", "geosite:googlefcm", "geosite:epicgames", "geosite:nvidia@cn", "geosite:microsoft@cn", "geosite:cloudflare@cn", "geosite:steam@cn", "geosite:category-ntp", "geosite:connectivity-check", "geosite:apple", "geosite:spotify", "geosite:microsoft" ]; const directPolicy = {}; directGeoForChinaDNS.forEach(geo => { directPolicy[geo] = chinaDNS; }); directPolicy["geosite:private"] = "system"; const forceProxyPolicy = {}; uniq(FORCE_PROXY_DOMAINS).forEach(d => { const domain = String(d || "").trim(); if (domain) forceProxyPolicy[domain] = foreignDNS; }); const fullFakeIpFilter = uniq([ "+.cn", "geosite:private", ...directGeoForChinaDNS, ...STATIC_FAKE_IP_FILTER, ...fakeIpFilterFromCfg ]); cfg.dns = { ...dns, enable: true, listen: "0.0.0.0:1053", ipv6: false, "cache-algorithm": "arc", "prefer-h3": false, "use-hosts": true, "use-system-hosts": true, "respect-rules": false, "enhanced-mode": "fake-ip", "fake-ip-filter-mode": "blacklist", "fake-ip-filter": fullFakeIpFilter, "default-nameserver": ["223.5.5.5", "119.29.29.29"], "nameserver-policy": { ...nameserverPolicyFromCfg, ...directPolicy, "*": "system", "+.arpa": "system", ...forceProxyPolicy }, nameserver: foreignDNS, "proxy-server-nameserver": [ "https://doh.pub/dns-query#DIRECT", "https://dns.alidns.com/dns-query#DIRECT" ], "direct-nameserver": ["system", "223.5.5.5", "119.29.29.29"], "direct-nameserver-follow-policy": true }; cfg.hosts = { ...(cfg.hosts || {}), "dns.alidns.com": ["223.5.5.5", "223.6.6.6"], "doh.pub": ["1.12.12.12", "120.53.53.53"], // 解决谷歌商店无法下载的问题 "services.googleapis.cn": ["services.googleapis.com"], // 屏蔽哔哩哔哩 PCDN "+.mcdn.bilivideo.com": ["0.0.0.0"], "+.mcdn.bilivideo.cn": ["0.0.0.0"] }; }; const applyProfile = (cfg) => { cfg.profile = { ...(cfg.profile || {}), "store-selected": true, "store-fake-ip": false }; }; const applyRuntime = (cfg) => { cfg.mode = "rule"; cfg["log-level"] = "warning"; }; // ==================== // 7. 主流程 — Clash Party 入口 // ==================== function main(config) { config = ensureConfigObject(config); const originalProxies = getOriginalProxies(config); const existingRules = Array.isArray(config.rules) ? config.rules : []; delete config["rule-providers"]; config.rules = mergeRules(STATIC_RULES, pickDirectRules(existingRules)); if (originalProxies.length) { makeProxyNamesUnique(originalProxies); const { infoProxies, normalProxies } = splitInfoAndNormalProxies( originalProxies, SETTINGS.FILTER_REGEX ); const baseProxies = normalProxies; const allNames = uniq(baseProxies.map((p) => p.name)); const infoNames = uniq(infoProxies.map((p) => p.name)); const { activeRegions, activeRegionNameSet, activeRegionMap, otherProxyNames } = classifyProxiesByRegion(baseProxies, REGIONS); const aiNames = buildAiProxyList(activeRegions, otherProxyNames, allNames); config["proxy-groups"] = buildProxyGroups({ allNames, aiNames, activeRegionMap, activeRegionNameSet, otherProxyNames, infoNames }); config.proxies = originalProxies; } applyGeoData(config); applyRuntime(config); applySniffer(config); applyTun(config); applyDns(config); applyProfile(config); return config; } 1 个帖子 - 1 位参与者 阅读完整话题

LinuxDo 最新话题 · 2026-05-12 10:11:48+08:00 · tech

求助佬友们,在L站找了个配置文件搭建链式代理(webshare),使用的是clash party,后来发现机场订阅失效需要更新单独时候,这个自定义配置无法更新,甚至我将机场订阅地址换成其他机场,实际读取的仍然是之前的机场节点,尝试过重启clash,删除配置文件重新导入,修改配置文件的interval为1,也不行。GPT也给不出解决方案,人麻了。 这里是配置: mixed-port: 7890 allow-lan: true bind-address: '*' mode: rule log-level: info ipv6: false external-controller: 127.0.0.1:9090 external-ui: ui external-ui-url: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip" geox-url: geoip: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat" geosite: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat" mmdb: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/country.mmdb" profile: store-selected: true store-fake-ip: true dns: enable: true ipv6: false listen: 0.0.0.0:1053 enhanced-mode: fake-ip fake-ip-range: 198.18.0.1/16 fake-ip-filter: - "*.lan" - "*.local" - "geosite:cn" nameserver: - 223.5.5.5 - 119.29.29.29 - 8.8.8.8 nameserver-policy: "geosite:cn,private": - 223.5.5.5 - 119.29.29.29 proxies: # 👇 核心改动1:dialer-proxy 指向新的手动组名 - name: "🇺🇸 静态家宽(落地)" type: socks5 server: 这里填你的家宽ip port: 这里填你的家宽ip端口号 username: blur1997 password: chenchen8023 skip-cert-verify: true # 🔗 链式逻辑:指向手动选择的组 dialer-proxy: "🖐️ 手选落地入口" proxy-groups: # 1. AI 专用组 (强制走链式) - name: AI-PROXY type: select proxies: - "🇺🇸 静态家宽(落地)" # 2. 👇 核心改动2:类型改为 select,你现在是手动挡驾驶员了 - name: "🖐️ 手选落地入口" type: select use: - Provider1 # select 类型不需要 interval 和 tolerance,保留 url 仅用于你在面板上手动测延迟 url: "http://www.gstatic.com/generate_204" # 3. 普通上网 (直连机场 或 走家宽) - name: PROXY type: select proxies: - "🖐️ 手选落地入口" # 这里也改个名,逻辑清晰点,直接选机场节点 - "🇺🇸 静态家宽(落地)" # use: - Provider1 # 不需要了,上面那个组已经包含了 Provider1 url: "http://www.gstatic.com/generate_204" proxy-providers: Provider1: type: http url: "这里填机场订阅链接" interval: 3600 path: ./profiles/Provider1 health-check: enable: true interval: 600 url: "http://www.gstatic.com/generate_204" override: skip-cert-verify: true rules: # ============================================ # 1. 调试与IP检测 # ============================================ - DOMAIN-SUFFIX,ping0.cc,AI-PROXY - DOMAIN-SUFFIX,whoer.net,AI-PROXY - DOMAIN-SUFFIX,ipdata.co,AI-PROXY # ============================================ # 2. 核心 AI 服务 # ============================================ - GEOSITE,openai,AI-PROXY - DOMAIN-SUFFIX,chatgpt.com,AI-PROXY - GEOSITE,anthropic,AI-PROXY - DOMAIN-SUFFIX,gemini.google.com,AI-PROXY - DOMAIN-SUFFIX,bard.google.com,AI-PROXY - DOMAIN-SUFFIX,generativeai.google.com,AI-PROXY - GEOSITE,google,AI-PROXY - DOMAIN-SUFFIX,x.ai,AI-PROXY - DOMAIN-SUFFIX,grok.com,AI-PROXY - GEOSITE,twitter,AI-PROXY - DOMAIN-SUFFIX,copilot.microsoft.com,AI-PROXY - DOMAIN-SUFFIX,bing.com,AI-PROXY - GEOSITE,category-ai-!cn,AI-PROXY # ============================================ # 3. 常用国外服务 # ============================================ - GEOSITE,github,PROXY - GEOSITE,telegram,PROXY # ============================================ # 4. 国内直连 # ============================================ - GEOSITE,cn,DIRECT - GEOIP,cn,DIRECT - GEOIP,private,DIRECT # ============================================ # 5. 兜底规则 # ============================================ - MATCH,PROXY 2 个帖子 - 2 位参与者 阅读完整话题