Files
warp/warp-go.sh
T
fscarmen 790a238d62 feat: Enhance WARP registration reliability and GitHub connectivity
- Implement retry mechanism for WARP API registration failures (Error 1015)
- Provide fallback to shared WARP account after 60 failed attempts
- Use multiple GitHub CDN proxies with direct connection priority
2025-12-03 18:31:05 +00:00

1818 lines
83 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 当前脚本版本号和新增功能
VERSION='1.2.4'
# 判断 Teams token 最少字符数
TOKEN_LENGTH=800
# 环境变量用于在Debian或Ubuntu操作系统中设置非交互式(noninteractive)安装模式
export DEBIAN_FRONTEND=noninteractive
# Github 反代加速代理
GITHUB_PROXY=('https://v6.gh-proxy.org/' 'https://gh-proxy.com/' 'https://hub.glowp.xyz/' 'https://proxy.vvvv.ee/' 'https://ghproxy.lvedong.eu.org/')
trap cleanup_resources EXIT INT TERM
E[0]="Language:\n 1.English (default) \n 2.简体中文"
C[0]="${E[0]}"
E[1]="Remove best endpoint feature to adapt to official adjustments"
C[1]="删除最优 Endpoint 功能以适应官方调整"
E[2]="warp-go h (help)\n warp-go o (temporary warp-go switch)\n warp-go u (uninstall WARP web interface and warp-go)\n warp-go v (sync script to latest version)\n warp-go i (replace IP with Netflix support)\n warp-go 4/6 ( WARP IPv4/IPv6 single-stack)\n warp-go d (WARP dual-stack)\n warp-go n (WARP IPv4 non-global)\n warp-go g (WARP global/non-global switching)\n warp-go e (output wireguard and sing-box configuration file)\n warp-go a (Change to Free, WARP+ or Teams account)"
C[2]="warp-go h (帮助)\n warp-go o (临时 warp-go 开关)\n warp-go u (卸载 WARP 网络接口和 warp-go)\n warp-go v (同步脚本至最新版本)\n warp-go i (更换支持 Netflix 的IP)\n warp-go 4/6 (WARP IPv4/IPv6 单栈)\n warp-go d (WARP 双栈)\n warp-go n (WARP IPv4 非全局)\n warp-go g (WARP 全局 / 非全局相互切换)\n warp-go e (输出 wireguard 和 sing-box 配置文件)\n warp-go a (更换到 FreeWARP+ 或 Teams 账户)"
E[3]="This project is designed to add WARP network interface for VPS, using warp-go core, using various interfaces of CloudFlare-WARP, integrated wireguard-go, can completely replace WGCF. Save Hong Kong, Toronto and other VPS, can also get WARP IP. Thanks again @CoiaPrant and his team. Project address: https://gitlab.com/ProjectWARP/warp-go/-/tree/master/"
C[3]="本项目专为 VPS 添加 WARP 网络接口,使用 wire-go 核心程序,利用CloudFlare-WARP 的各类接口,集成 wireguard-go,可以完全替代 WGCF。 救活了香港、多伦多等 VPS 也可以获取 WARP IP。再次感谢 @CoiaPrant 及其团队。项目地址: https://gitlab.com/ProjectWARP/warp-go/-/tree/master/"
E[4]="Choose:"
C[4]="请选择:"
E[5]="You must run the script as root. You can type sudo -i and then download and run it again. Feedback:[https://github.com/fscarmen/warp-sh/issues]"
C[5]="必须以root方式运行脚本,可以输入 sudo -i 后重新下载运行,问题反馈:[https://github.com/fscarmen/warp-sh/issues]"
E[6]="This script only supports Debian, Ubuntu, CentOS, Arch or Alpine systems, Feedback: [https://github.com/fscarmen/warp-sh/issues]"
C[6]="本脚本只支持 Debian、Ubuntu、CentOS、Arch 或 Alpine 系统,问题反馈:[https://github.com/fscarmen/warp-sh/issues]"
E[7]="Curren operating system is \$SYS.\\\n The system lower than \$SYSTEM \${MAJOR[int]} is not supported. Feedback: [https://github.com/fscarmen/warp-sh/issues]"
C[7]="当前操作是 \$SYS\\\n 不支持 \$SYSTEM \${MAJOR[int]} 以下系统,问题反馈:[https://github.com/fscarmen/warp-sh/issues]"
E[8]="Install dependence-list:"
C[8]="安装依赖列表:"
E[9]="Step 3/3: Best MTU found."
C[9]="进度 3/3: 已找到最佳 MTU"
E[10]="No suitable solution was found for modifying the warp-go configuration file warp.conf and the script aborted. When you see this message, please send feedback on the bug to:[https://github.com/fscarmen/warp-sh/issues]"
C[10]="没有找到适合的方案用于修改 warp-go 配置文件 warp.conf,脚本中止。当你看到此信息,请把该 bug 反馈至:[https://github.com/fscarmen/warp-sh/issues]"
E[11]="Warp-go is not installed yet."
C[11]="还没有安装 warp-go"
E[12]="To install, press [y] and other keys to exit:"
C[12]="如需安装,请按[y],其他键退出:"
E[13]="\$(date +'%F %T') Try \${i}. Failed. IPv\$NF: \$WAN \$COUNTRY \$ASNORG. Retry after \${l} seconds. Brush ip runing time:\$DAY days \$HOUR hours \$MIN minutes \$SEC seconds"
C[13]="\$(date +'%F %T') 尝试第\${i}次,解锁失败,IPv\$NF: \$WAN \$COUNTRY \$ASNORG\${l}秒后重新测试,刷 IP 运行时长: \$DAY 天 \$HOUR 时 \$MIN 分 \$SEC 秒"
E[14]="1. Brush WARP IPv4 (default)\n 2. Brush WARP IPv6"
C[14]="1. 刷 WARP IPv4 (默认)\n 2. 刷 WARP IPv6"
E[15]="The current Netflix region is:\$REGION. To unlock the current region please press [y]. For other addresses please enter two regional abbreviations \(e.g. hk,sg, default:\$REGION\):"
C[15]="当前 Netflix 地区是:\$REGION,需要解锁当前地区请按 y , 如需其他地址请输入两位地区简写 \(如 hk ,sg,默认:\$REGION\):"
E[16]="\$(date +'%F %T') Region: \$REGION Done. IPv\$NF: \$WAN \$COUNTRY \$ASNORG. Retest after 1 hour. Brush ip runing time:\$DAY days \$HOUR hours \$MIN minutes \$SEC seconds"
C[16]="\$(date +'%F %T') 区域 \$REGION 解锁成功,IPv\$NF: \$WAN \$COUNTRY \$ASNORG1 小时后重新测试,刷 IP 运行时长: \$DAY 天 \$HOUR 时 \$MIN 分 \$SEC 秒"
E[17]="WARP network interface and warp-go have been completely removed!"
C[17]="WARP 网络接口及 warp-go 已彻底删除!"
E[18]="Successfully synchronized the latest version"
C[18]="成功!已同步最新脚本,版本号"
E[19]="New features"
C[19]="功能新增"
E[20]="Maximum \${j} attempts to get WARP IP..."
C[20]="后台获取 WARP IP 中, 最大尝试\${j}次……"
E[21]="Can't find the account file: /opt/warp-go/warp.conf.You can uninstall and reinstall it."
C[21]="找不到账户文件:/opt/warp-go/warp.conf,可以卸载后重装"
E[22]="Current Teams account is not available. Switch back to free account automatically."
C[22]="当前 Teams 账户不可用,自动切换回免费账户"
E[23]="Failed more than \${j} times, script aborted. Feedback: [https://github.com/fscarmen/warp-sh/issues]"
C[23]="失败已超过\${j}次,脚本中止,问题反馈:[https://github.com/fscarmen/warp-sh/issues]"
E[24]="non-"
C[24]="非"
E[25]="Successfully got WARP \$ACCOUNT_TYPE network.\\\n Running in \${GLOBAL_TYPE}global mode."
C[25]="已成功获取 WARP \$ACCOUNT_TYPE 网络\\\n 运行在 \${GLOBAL_TYPE}全局 模式"
E[26]="WARP+ quota"
C[26]="剩余流量"
E[27]="WARP is turned off. It could be turned on again by [warp-go o]"
C[27]="已暂停 WARP,再次开启可以用 warp-go o"
E[28]="WARP Non-global mode cannot switch between single and double stacks."
C[28]="WARP 非全局模式下不能切换单双栈"
E[29]="To switch to global mode, press [y] and other keys to exit:"
C[29]="如需更换为全局模式,请按[y],其他键退出:"
E[30]="Cannot switch to the same form as the current one."
C[30]="不能切换为当前一样的形态"
E[31]="Switch \${WARP_BEFORE[m]} to \${WARP_AFTER1[m]}"
C[31]="\${WARP_BEFORE[m]} 转为 \${WARP_AFTER1[m]}"
E[32]="Switch \${WARP_BEFORE[m]} to \${WARP_AFTER2[m]}"
C[32]="\${WARP_BEFORE[m]} 转为 \${WARP_AFTER2[m]}"
E[33]="WARP network interface can be switched as follows:\\\n 1. \${OPTION[1]}\\\n 2. \${OPTION[2]}\\\n 0. Exit script"
C[33]="WARP 网络接口可以切换为以下方式:\\\n 1. \${OPTION[1]}\\\n 2. \${OPTION[2]}\\\n 0. 退出脚本"
E[34]="Please enter the correct number"
C[34]="请输入正确数字"
E[35]="Checking VPS infomation..."
C[35]="检查环境中……"
E[36]="The TUN module is not loaded. You should turn it on in the control panel. Ask the supplier for more help. Feedback: [https://github.com/fscarmen/warp-sh/issues]"
C[36]="没有加载 TUN 模块,请在管理后台开启或联系供应商了解如何开启,问题反馈:[https://github.com/fscarmen/warp-sh/issues]"
E[37]="Curren architecture \$(uname -m) is not supported. Feedback: [https://github.com/fscarmen/warp-sh/issues]"
C[37]="当前架构 \$(uname -m) 暂不支持,问题反馈:[https://github.com/fscarmen/warp-sh/issues]"
E[38]="If there is a WARP+ License, please enter it, otherwise press Enter to continue:"
C[38]="如有 WARP+ License 请输入,没有可回车继续:"
E[39]="Input errors up to 5 times.The script is aborted."
C[39]="输入错误达5次,脚本退出"
E[40]="License should be 26 characters, please re-enter WARP+ License. Otherwise press Enter to continue. \(\${i} times remaining\):"
C[40]="License 应为26位字符,请重新输入 WARP+ License,没有可回车继续\(剩余\${i}次\):"
E[41]="Please customize the device name (Default is [warp-go] if left blank):"
C[41]="请自定义设备名 (如果不输入,默认为 [warp-go]):"
E[42]="Please Input WARP+ license:"
C[42]="请输入WARP+ License:"
E[43]="License should be 26 characters, please re-enter WARP+ License. Otherwise press Enter to continue. \(\${i} times remaining\): "
C[43]="License 应为26位字符,请重新输入 WARP+ License \(剩余\${i}次\): "
E[44]="Your organization"
C[44]="组织名:"
E[45]="Token error, please re-enter Teams token \(remaining \${i} times\):"
C[45]="Token 错误,请重新输入 Teams token \(剩余\${i}次\):"
E[46]="Current account type is: \$ACCOUNT_TYPE\\\t \$PLUS_QUOTA\\\n \$CHANGE_TYPE"
C[46]="当前账户类型是: \$ACCOUNT_TYPE\\\t \$PLUS_QUOTA\\\n \$CHANGE_TYPE"
E[47]="1. Continue using the free account without changing.\n 2. Change to WARP+ account.\n 3. Change to Teams account. (Enter the organization name and email verification code to get it. Or use the one provided by the script if left blank)\n 0. Return to the main menu."
C[47]="1. 继续使用 free 账户,不变更\n 2. 变更为 WARP+ 账户\n 3. 变更为 Teams 账户 (输入组织名和邮箱验证码获取,如果留空,则使用脚本提供的)\n 0. 返回主菜单"
E[48]="1. Change to free account.\n 2. Change to WARP+ account.\n 3. Change to another WARP Teams account. (Enter the organization name and email verification code to get it. Or use the one provided by the script if left blank)\n 0. Return to the main menu."
C[48]="1. 变更为 free 账户\n 2. 变更为 WARP+ 账户\n 3. 更换为另一个 Teams 账户 (输入组织名和邮箱验证码获取,如果留空,则使用脚本提供的)\n 0. 返回主菜单"
E[49]="1. Change to free account.\n 2. Change to another WARP+ account.\n 3. Change to Teams account. (Enter the organization name and email verification code to get it. Or use the one provided by the script if left blank)\n 0. Return to the main menu."
C[49]="1. 变更为 free 账户\n 2. 变更为另一个 WARP+ 账户\n 3. 变更为 Teams 账户 (输入组织名和邮箱验证码获取,如果留空,则使用脚本提供的)\n 0. 返回主菜单"
E[50]="Registration of WARP\${k} account failed, script aborted. Feedback: [https://github.com/fscarmen/warp-sh/issues]"
C[50]="注册 WARP\${k} 账户失败,脚本中止,问题反馈: [https://github.com/fscarmen/warp-sh/issues]"
E[51]="Warp-go not yet installed. No account registered. Script aborted. Feedback: [https://github.com/fscarmen/warp-sh/issues]"
C[51]="warp-go 还没有安装,没有注册账户,脚本中止,问题反馈: [https://github.com/fscarmen/warp-sh/issues]"
E[52]="Wireguard configuration file: /opt/warp-go/wgcf.conf\n"
C[52]="Wireguard 配置文件: /opt/warp-go/wgcf.conf\n"
E[53]="Warp-go installed. Script aborted. Feedback: [https://github.com/fscarmen/warp-sh/issues]"
C[53]="warp-go 已安装,脚本中止,问题反馈: [https://github.com/fscarmen/warp-sh/issues]"
E[54]="Is there a WARP+ or Teams account?\n 1. WARP+\n 2. Teams\n 3. Use free account (default)"
C[54]="如有 WARP+ 或 Teams 账户请选择\n 1. WARP+\n 2. Teams\n 3. 使用免费账户 (默认)"
E[55]="Please choose the priority:\n 1. IPv4\n 2. IPv6\n 3. Use initial settings (default)"
C[55]="请选择优先级别:\n 1. IPv4\n 2. IPv6\n 3. 使用 VPS 初始设置 (默认)"
E[56]="Download warp-go zip file unsuccessful. Script exits. Feedback: [https://github.com/fscarmen/warp-sh/issues]"
C[56]="下载 warp-go 压缩文件不成功,脚本退出,问题反馈: [https://github.com/fscarmen/warp-sh/issues]"
E[57]="Warp-go file does not exist, script exits. Feedback: [https://github.com/fscarmen/warp-sh/issues]"
C[57]="Warp-go 文件不存在,脚本退出,问题反馈: [https://github.com/fscarmen/warp-sh/issues]"
E[58]="Maximum \${j} attempts to register WARP\${k} account..."
C[58]="注册 WARP\${k} 账户中, 最大尝试\${j}次……"
E[59]="Try \${i}"
C[59]="第\${i}次尝试"
E[60]="Step 1/3: Install dependencies..."
C[60]="进度 1/3: 安装系统依赖……"
E[61]="Step 2/3: Install warp-go..."
C[61]="进度 2/3: 已安装 warp-go"
E[62]="Congratulations! WARP \$ACCOUNT_TYPE has been turn on. Total time spent:\$(( end - start )) seconds.\\\n Number of script runs in the day: \$TODAY. Total number of runs: \$TOTAL."
C[62]="恭喜!WARP \$ACCOUNT_TYPE 已开启,总耗时:\$(( end - start ))秒\\\n 脚本当天运行次数: \$TODAY,累计运行次数:\$TOTAL"
E[63]="Warp-go installation failed. Feedback: [https://github.com/fscarmen/warp-sh/issues]"
C[63]="warp-go 安装失败,问题反馈: [https://github.com/fscarmen/warp-sh/issues]"
E[64]="Add WARP IPv4 global network interface for \${NATIVE[n]}, IPv4 priority \(bash warp-go.sh 4\)"
C[64]="为 \${NATIVE[n]} 添加 WARP IPv4 全局 网络接口,IPv4 优先 \(bash warp-go.sh 4\)"
E[65]="Add WARP IPv4 global network interface for \${NATIVE[n]}, IPv6 priority \(bash warp-go.sh 4\)"
C[65]="为 \${NATIVE[n]} 添加 WARP IPv4 全局 网络接口,IPv6 优先 \(bash warp-go.sh 4\)"
E[66]="Add WARP IPv6 global network interface for \${NATIVE[n]}, IPv4 priority \(bash warp-go.sh 6\)"
C[66]="为 \${NATIVE[n]} 添加 WARP IPv6 全局 网络接口,IPv4 优先 \(bash warp-go.sh 6\)"
E[67]="Add WARP IPv6 global network interface for \${NATIVE[n]}, IPv6 priority \(bash warp-go.sh 6\)"
C[67]="为 \${NATIVE[n]} 添加 WARP IPv6 全局 网络接口,IPv6 优先 \(bash warp-go.sh 6\)"
E[68]="Add WARP dual-stacks global network interface for \${NATIVE[n]}, IPv4 priority \(bash warp-go.sh d\)"
C[68]="为 \${NATIVE[n]} 添加 WARP 双栈 全局 网络接口,IPv4 优先 \(bash warp-go.sh d\)"
E[69]="Add WARP dual-stacks global network interface for \${NATIVE[n]}, IPv6 priority \(bash warp-go.sh d\)"
C[69]="为 \${NATIVE[n]} 添加 WARP 双栈 全局 网络接口,IPv6 优先 \(bash warp-go.sh d\)"
E[70]="Add WARP dual-stacks non-global network interface for \${NATIVE[n]}, IPv4 priority \(bash warp-go.sh n\)"
C[70]="为 \${NATIVE[n]} 添加 WARP 双栈 非全局 网络接口,IPv4 优先 \(bash warp-go.sh n\)"
E[71]="Add WARP dual-stacks non-global network interface for \${NATIVE[n]}, IPv6 priority \(bash warp-go.sh n\)"
C[71]="为 \${NATIVE[n]} 添加 WARP 双栈 非全局 网络接口,IPv6 优先 \(bash warp-go.sh n\)"
E[72]="Turn off warp-go (warp-go o)"
C[72]="关闭 warp-go (warp-go o)"
E[73]="Turn on warp-go (warp-go o)"
C[73]="打开 warp-go (warp-go o)"
E[74]="\${WARP_BEFORE[m]} switch to \${WARP_AFTER1[m]} \${SHORTCUT1[m]}"
C[74]="\${WARP_BEFORE[m]} 转为 \${WARP_AFTER1[m]} \${SHORTCUT1[m]}"
E[75]="\${WARP_BEFORE[m]} switch to \${WARP_AFTER2[m]} \${SHORTCUT2[m]}"
C[75]="\${WARP_BEFORE[m]} 转为 \${WARP_AFTER2[m]} \${SHORTCUT2[m]}"
E[76]="Switch to WARP \${GLOBAL_AFTER}global network interface \(warp-go g\)"
C[76]="转为 WARP \${GLOBAL_AFTER}全局 网络接口 \(warp-go g\)"
E[77]="Change to Free, WARP+ or Teams account \(warp-go a\)"
C[77]="更换为 FreeWARP+ 或 Teams 账户 \(warp-go a\)"
E[78]="Change the WARP IP to support Netflix (warp-go i)"
C[78]="更换支持 Netflix 的 IP (warp-go i)"
E[79]="Export wireguard and sing-box configuration file (warp-go e)"
C[79]="输出 wireguard 和 sing-box 配置文件 (warp-go e)"
E[80]="Uninstall the WARP interface and warp-go (warp-go u)"
C[80]="卸载 WARP 网络接口和 warp-go (warp-go u)"
E[81]="Exit"
C[81]="退出脚本"
E[82]="Sync the latest version"
C[82]="同步最新版本"
E[83]="Device Name"
C[83]="设备名"
E[84]="Version"
C[84]="脚本版本"
E[85]="New features"
C[85]="功能新增"
E[86]="System infomation"
C[86]="系统信息"
E[87]="Operating System"
C[87]="当前操作系统"
E[88]="Kernel"
C[88]="内核"
E[89]="Architecture"
C[89]="处理器架构"
E[90]="Virtualization"
C[90]="虚拟化"
E[91]="WARP \$TYPE Interface is on"
C[91]="WARP \$TYPE 网络接口已开启"
E[92]="Running in \${GLOBAL_TYPE}global mode"
C[92]="运行在 \${GLOBAL_TYPE}全局 模式"
E[93]="WARP network interface is not turned on"
C[93]="WARP 网络接口未开启"
E[94]="Native dualstack"
C[94]="原生双栈"
E[95]="Run again with warp-go [option] [lisence], such as"
C[95]="再次运行用 warp-go [option] [lisence],如"
E[96]="dualstack"
C[96]="双栈"
E[97]="The account type is Teams and does not support changing IP\n 1. Change to free (default)\n 2. Change to plus\n 3. Quit"
C[97]="账户类型为 Teams,不支持更换 IP\n 1. 更换为 free (默认)\n 2. 更换为 plus\n 3. 退出"
E[98]="Non-global"
C[98]="非全局"
E[99]="global"
C[99]="全局"
E[100]="IPv\$PRIO priority"
C[100]="IPv\$PRIO 优先"
E[101]="Sing-box configuration file: /opt/warp-go/singbox.json\n"
C[101]="Sing-box 配置文件: /opt/warp-go/singbox.json\n"
E[102]="WAN interface network protocol must be [static] on OpenWrt."
C[102]="OpenWrt 系统的 WAN 接口的网络传输协议必须为 [静态地址]"
E[103]="Unlimited"
C[103]="无限制"
E[104]="Failed to get the registration information from API. Script exits. Feedback: [https://github.com/fscarmen/warp-sh/issues]"
C[104]="API 获取不到注册信息,脚本退出,问题反馈: [https://github.com/fscarmen/warp-sh/issues]"
E[105]="upgrade successful."
C[105]="升级成功"
E[106]="upgrade failed. The free account will remain in use."
C[106]="升级失败,将保持使用 free 账户。"
E[107]=""
C[107]=""
E[108]="Cannot detect any IPv4 or IPv6. The script is aborted. Feedback: [https://github.com/fscarmen/warp-sh/issues]"
C[108]="检测不到任何 IPv4 或 IPv6。脚本中止,问题反馈:[https://github.com/fscarmen/warp-sh/issues]"
E[109]="E-mail address to receive the verification code:"
C[109]="接收验证码的邮箱:"
E[110]="Verification code:"
C[110]="验证码:"
E[111]="Organization does not exist, please re-enter:"
C[111]="组织名不存在,请重新输入:"
E[112]="The verification code is wrong, please re-enter:"
C[112]="验证码错误,请重新输入:"
E[113]="1. input the organization and email verification code\n 2. share teams account (default)"
C[113]="1. 输入组织名和邮箱验证码获取\n 2. 共享 teams 账户 (默认)"
# 自定义字体彩色,read 函数
warning() { echo -e "\033[31m\033[01m$*\033[0m"; } # 红色
error() { echo -e "\033[31m\033[01m$*\033[0m"; rm -f /tmp/warp-go*; exit 1; } # 红色
info() { echo -e "\033[32m\033[01m$*\033[0m"; } # 绿色
hint() { echo -e "\033[33m\033[01m$*\033[0m"; } # 黄色
reading() { read -rp "$(info "$1")" "$2"; }
text() { eval echo "\${${L}[$*]}"; }
text_eval() { eval echo "\$(eval echo "\${${L}[$*]}")"; }
# 清理函数
cleanup_resources() {
rm -f /tmp/{statistics,warp-go*} 2>/dev/null; exit 0
}
# 检测是否需要启用 Github CDN,如能直接连通,则不使用
check_cdn() {
if ! wget --server-response --quiet --output-document=/dev/null --no-check-certificate --tries=2 --timeout=3 https://raw.githubusercontent.com/fscarmen/warp-sh/main/README.md >/dev/null 2>&1; then
for GH_PROXY in "${GITHUB_PROXY[@]}"; do
wget --server-response --quiet --output-document=/dev/null --no-check-certificate --tries=2 --timeout=3 "${GH_PROXY}https://raw.githubusercontent.com/fscarmen/warp-sh/main/README.md" >/dev/null 2>&1 && break || unset GH_PROXY
done
fi
}
# 脚本当天及累计运行次数统计
statistics_of_run-times() {
local UPDATE_OR_GET=$1
local SCRIPT=$2
if grep -q 'update' <<< "$UPDATE_OR_GET"; then
{ wget --no-check-certificate -qO- --timeout=3 "https://stat.cloudflare.now.cc/api/updateStats?script=${SCRIPT}" > /tmp/statistics; }&
elif grep -q 'get' <<< "$UPDATE_OR_GET"; then
[ -s /tmp/statistics ] && [[ $(cat /tmp/statistics) =~ \"todayCount\":([0-9]+),\"totalCount\":([0-9]+) ]] && TODAY="${BASH_REMATCH[1]}" && TOTAL="${BASH_REMATCH[2]}" && rm -f /tmp/statistics
fi
}
# 选择语言,先判断 /opt/warp-go/language 里的语言选择,没有的话再让用户选择,默认英语。处理中文显示的问题
select_language() {
UTF8_LOCALE=$(locale -a 2>/dev/null | grep -iEm1 "UTF-8|utf8")
[ -n "$UTF8_LOCALE" ] && export LC_ALL="$UTF8_LOCALE" LANG="$UTF8_LOCALE" LANGUAGE="$UTF8_LOCALE"
if [ -s /opt/warp-go/language ]; then
L=$(cat /opt/warp-go/language)
else
L=E && [[ -z "$OPTION" || "$OPTION" = [ahvi46d] ]] && hint " $(text 0) \n" && reading " $(text 4) " LANGUAGE
[ "$LANGUAGE" = 2 ] && L=C
fi
}
# 必须以root运行脚本
check_root_virt() {
[ "$(id -u)" != 0 ] && error " $(text 5) "
# 判断虚拟化,选择 Wireguard内核模块 还是 Wireguard-Go
if [ "$1" = Alpine ]; then
VIRT=$(virt-what)
else
[ $(type -p systemd-detect-virt) ] && VIRT=$(systemd-detect-virt)
[[ -z "$VIRT" && $(type -p hostnamectl) ]] && VIRT=$(hostnamectl | awk '/Virtualization:/{print $NF}')
fi
}
# 多方式判断操作系统,试到有值为止。只支持 Debian 9/10/11、Ubuntu 18.04/20.04/22.04 或 CentOS 7/8 ,如非上述操作系统,退出脚本
check_operating_system() {
if [ -s /etc/os-release ]; then
SYS="$(grep -i pretty_name /etc/os-release | cut -d \" -f2)"
elif [ $(type -p hostnamectl) ]; then
SYS="$(hostnamectl | grep -i system | cut -d : -f2)"
elif [ $(type -p lsb_release) ]; then
SYS="$(lsb_release -sd)"
elif [ -s /etc/lsb-release ]; then
SYS="$(grep -i description /etc/lsb-release | cut -d \" -f2)"
elif [ -s /etc/redhat-release ]; then
SYS="$(grep . /etc/redhat-release)"
elif [ -s /etc/issue ]; then
SYS="$(grep . /etc/issue | cut -d '\' -f1 | sed '/^[ ]*$/d')"
fi
# 自定义 Alpine 系统若干函数
alpine_warp_restart() {
kill -15 $(pgrep warp-go) 2>/dev/null
/opt/warp-go/warp-go --config=/opt/warp-go/warp.conf 2>&1 &
}
alpine_wgcf_enable() { echo -e "/opt/warp-go/tun.sh\n/opt/warp-go/warp-go --config=/opt/warp-go/warp.conf 2>&1 &" > /etc/local.d/warp-go.start; chmod +x /etc/local.d/warp-go.start; rc-update add local; }
openwrt_wgcf_enable() { echo -e "@reboot /opt/warp-go/warp-go --config=/opt/warp-go/warp.conf" >> /etc/crontabs/root; }
REGEX=("debian" "ubuntu" "centos|red hat|kernel|alma|rocky|amazon linux" "alpine" "arch linux" "openwrt")
RELEASE=("Debian" "Ubuntu" "CentOS" "Alpine" "Arch" "OpenWrt")
EXCLUDE=("---")
MAJOR=("9" "16" "7" "" "" "")
PACKAGE_UPDATE=("apt -y update" "apt -y update" "yum -y update" "apk update -f" "pacman -Sy" "opkg update")
PACKAGE_INSTALL=("apt -y install" "apt -y install" "yum -y install" "apk add -f" "pacman -S --noconfirm" "opkg install")
PACKAGE_UNINSTALL=("apt -y autoremove" "apt -y autoremove" "yum -y autoremove" "apk del -f" "pacman -Rcnsu --noconfirm" "opkg remove --force-depends")
SYSTEMCTL_START=("systemctl start warp-go" "systemctl start warp-go" "systemctl start warp-go" "/opt/warp-go/warp-go --config=/opt/warp-go/warp.conf" "systemctl start warp-go" "/opt/warp-go/warp-go --config=/opt/warp-go/warp.conf")
SYSTEMCTL_STOP=("systemctl stop warp-go" "systemctl stop warp-go" "systemctl stop warp-go" "kill -15 $(pgrep warp-go)" "systemctl stop warp-go" "kill -15 $(pgrep warp-go)")
SYSTEMCTL_RESTART=("systemctl restart warp-go" "systemctl restart warp-go" "systemctl restart warp-go" "alpine_warp_restart" "systemctl restart wg-quick@wgcf" "alpine_warp_restart")
SYSTEMCTL_ENABLE=("systemctl enable --now warp-go" "systemctl enable --now warp-go" "systemctl enable --now warp-go" "alpine_wgcf_enable" "systemctl enable --now warp-go")
for int in "${!REGEX[@]}"; do
[[ "${SYS,,}" =~ ${REGEX[int]} ]] && SYSTEM="${RELEASE[int]}" && break
done
# 针对各厂运的订制系统
if [ -z "$SYSTEM" ]; then
[ $(type -p yum) ] && int=2 && SYSTEM='CentOS' || error " $(text 6) "
fi
[ "$SYSTEM" = OpenWrt ] && [[ ! $(uci show network.wan.proto 2>/dev/null | cut -d \' -f2)$(uci show network.lan.proto 2>/dev/null | cut -d \' -f2) =~ 'static' ]] && error " $(text 102) "
# 先排除 EXCLUDE 里包括的特定系统,其他系统需要作大发行版本的比较
for ex in "${EXCLUDE[@]}"; do [[ ! "${SYS,,}" =~ $ex ]]; done &&
[[ "$(echo "$SYS" | sed "s/[^0-9.]//g" | cut -d. -f1)" -lt "${MAJOR[int]}" ]] && error " $(text_eval 7) "
}
check_arch() {
# 判断处理器架构
case "$(uname -m)" in
aarch64 )
ARCHITECTURE=arm64
;;
x86)
ARCHITECTURE=386
;;
x86_64 )
CPU_FLAGS=$(cat /proc/cpuinfo | grep flags | head -n 1 | cut -d: -f2)
case "$CPU_FLAGS" in
*avx512* )
ARCHITECTURE=amd64v4
;;
*avx2* )
ARCHITECTURE=amd64v3
;;
*sse3* )
ARCHITECTURE=amd64v2
;;
* )
ARCHITECTURE=amd64
esac
;;
s390x )
ARCHITECTURE=s390x
;;
* )
error " $(text_eval 37) "
esac
}
# 安装系统依赖及定义 ping 指令
check_dependencies() {
# 对于 Alpine 和 OpenWrt 系统,升级库并重新安装依赖
if [[ "$SYSTEM" =~ Alpine|OpenWrt ]]; then
DEPS_CHECK=("ping" "curl" "wget" "grep" "bash" "ip" "tar" "virt-what" "xxd" "openssl")
DEPS_INSTALL=("iputils-ping" "curl" "wget" "grep" "bash" "iproute2" "tar" "virt-what" "xxd" "openssl")
else
# 对于三大系统需要的依赖
[ "${SYSTEM}" = 'CentOS' ] && ${PACKAGE_INSTALL[int]} vim-common
DEPS_CHECK=("ping" "wget" "curl" "systemctl" "ip" "xxd" "openssl")
DEPS_INSTALL=("iputils-ping" "wget" "curl" "systemctl" "iproute2" "xxd" "openssl")
fi
for c in "${!DEPS_CHECK[@]}"; do
[ ! $(type -p ${DEPS_CHECK[c]}) ] && [[ ! "${DEPS[@]}" =~ "${DEPS_INSTALL[c]}" ]] && DEPS+=(${DEPS_INSTALL[c]})
done
# 检查 JSON 格式化工具
if [ -x "$(type -p python3)" ]; then
JSON_TOOL="python3 -m json.tool"
elif [ -x "$(type -p python)" ]; then
JSON_TOOL="python -m json.tool"
elif [ -x "$(type -p jq)" ]; then
JSON_TOOL="jq"
else
JSON_TOOL="jq"
DEPS+=("jq")
fi
if [ "${#DEPS[@]}" -ge 1 ]; then
info "\n $(text 8) ${DEPS[@]} \n"
${PACKAGE_UPDATE[int]} >/dev/null 2>&1
${PACKAGE_INSTALL[int]} ${DEPS[@]} >/dev/null 2>&1
fi
PING6='ping -6' && [ $(type -p ping6) ] && PING6='ping6'
}
# 获取 warp 账户信息
warp_api(){
local WARP_API_URL="warp.cloudflare.now.cc"
local RUN=$1
local FILE_PATH=$2
local WARP_LICENSE=$3
local WARP_DEVICE_NAME=$4
local WARP_TEAM_TOKEN=$5
local WARP_CONVERT=$6
local WARP_CONVERT_MODE=$7
local TEAM_AUTH=$8
local TEAM_ORGANIZATION=$9
local TEAM_EMAIL=${10}
local TEAM_CODE=${11}
if [ -s "$FILE_PATH" ]; then
# Teams 账户文件
if grep -q 'xml version' $FILE_PATH; then
local WARP_DEVICE_ID=$(grep 'correlation_id' $FILE_PATH | sed "s#.*>\(.*\)<.*#\1#")
local WARP_TOKEN=$(grep 'warp_token' $FILE_PATH | sed "s#.*>\(.*\)<.*#\1#")
local WARP_CLIENT_ID=$(grep 'client_id' $FILE_PATH | sed "s#.*client_id&quot;:&quot;\([^&]\{4\}\)&.*#\1#")
# 官方 api 文件
elif grep -q 'client_id' $FILE_PATH; then
local WARP_DEVICE_ID=$(grep -m1 '"id' "$FILE_PATH" | cut -d\" -f4)
local WARP_TOKEN=$(grep '"token' "$FILE_PATH" | cut -d\" -f4)
local WARP_CLIENT_ID=$(grep 'client_id' "$FILE_PATH" | cut -d\" -f4)
# client 文件,默认存放路径为 /var/lib/cloudflare-warp/reg.json
elif grep -q 'registration_id' $FILE_PATH; then
local WARP_DEVICE_ID=$(cut -d\" -f4 "$FILE_PATH")
local WARP_TOKEN=$(cut -d\" -f8 "$FILE_PATH")
# wgcf 文件,默认存放路径为 /etc/wireguard/wgcf-account.toml
elif grep -q 'access_token' $FILE_PATH; then
local WARP_DEVICE_ID=$(grep 'device_id' "$FILE_PATH" | cut -d\' -f2)
local WARP_TOKEN=$(grep 'access_token' "$FILE_PATH" | cut -d\' -f2)
# warp-go 文件,默认存放路径为 /opt/warp-go/warp.conf
elif grep -q 'PrivateKey' $FILE_PATH; then
local WARP_DEVICE_ID=$(awk -F' *= *' '/^Device/{print $2}' "$FILE_PATH")
local WARP_TOKEN=$(awk -F' *= *' '/^Token/{print $2}' "$FILE_PATH")
fi
fi
case "$RUN" in
register )
# 生成 wireguard 公私钥,并且补上 private key
if [[ -x "$(type -p openssl)" && -x "$(type -p xxd)" && -x "$(type -p base64)" ]]; then
local KEY_PAIR=$(openssl genpkey -algorithm X25519 | openssl pkey -text -noout)
local PRIVATE_KEY=$(echo $KEY_PAIR | sed 's/.*priv:\(.*\)pub.*/\1/; s/ //g' | xxd -r -p | base64)
local PUBLIC_KEY=$(echo $KEY_PAIR | sed 's/.*pub://; s/ //g'| xxd -r -p | base64)
else
local WG_API=$(curl -m5 -sSL https://wg-key.forvps.gq/)
local PRIVATE_KEY=$(awk 'NR==2 {print $2}' <<< "$WG_API")
local PUBLIC_KEY=$(awk 'NR==1 {print $2}' <<< "$WG_API")
fi
if grep -q '.' <<< "$PRIVATE_KEY" && grep -q '.' <<< "$PUBLIC_KEY"; then
local INSTALL_ID=$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 22)
local FCM_TOKEN="${INSTALL_ID}:APA91b$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 134)"
# 由于某些 IP 存在被限制注册,所以使用不停的注册来处理,超过一定次数则使用预设账户
grep -q '.' <<< "$WARP_TEAM_TOKEN" && local TEAM_HEADER="--header \"Cf-Access-Jwt-Assertion: $(sed 's/.*?token=//' <<< "$WARP_TEAM_TOKEN")\""
until grep -q 'account' <<< "$ACCOUNT"; do
((REGISTER_ERROR_TIME++))
if [ "$REGISTER_ERROR_TIME" -gt 30 ]; then
break
elif [ "$REGISTER_ERROR_TIME" -gt 1 ]; then
sleep 5
fi
local ACCOUNT=$(curl --request POST 'https://api.cloudflareclient.com/v0a2158/reg' \
--silent \
--location \
--tlsv1.3 \
--header 'User-Agent: okhttp/3.12.1' \
--header 'CF-Client-Version: a-6.10-2158' \
--header 'Content-Type: application/json' \
$TEAM_HEADER \
--data '{"key":"'${PUBLIC_KEY}'","install_id":"'${INSTALL_ID}'","fcm_token":"'${FCM_TOKEN}'","tos":"'$(date +"%Y-%m-%dT%H:%M:%S.000Z")'","model":"PC","serial_number":"'${INSTALL_ID}'","locale":"zh_CN"}')
done
if grep -q 'account' <<< "$ACCOUNT"; then
local CLIENT_ID=$(sed 's/.*"client_id":"\([^\"]\+\)\".*/\1/' <<<"$ACCOUNT")
local RESERVED=$(echo "$CLIENT_ID" | base64 -d | xxd -p | fold -w2 | while read HEX; do printf '%d ' "0x${HEX}"; done | awk '{print "["$1", "$2", "$3"]"}')
$JSON_TOOL <<< "$ACCOUNT" 2>&1 | sed "/\"key\"/a\ \"private_key\": \"$PRIVATE_KEY\"," | sed "/\"client_id\"/a\ \"reserved\": $RESERVED,"
else
echo '{
"id": "b0fe9b24-3396-486e-a12d-c194dbbb7bfb",
"type": "a",
"model": "PC",
"name": "",
"key": "rizJSrjeCO51ck8Rmj9YwstFnf6M9rJKZIXFQo3y8j8=",
"private_key": "hTk06uwwXhZx3RVqtug3MQ0RSodzdM/U5z/M5NIbh4c=",
"account": {
"id": "5a43e4b3-2e13-46b9-9437-2abe55cd5f4b",
"account_type": "free",
"created": "2025-12-02T16:44:10.752518443Z",
"updated": "2025-12-02T16:44:10.752518443Z",
"premium_data": 0,
"quota": 0,
"usage": 0,
"warp_plus": true,
"referral_count": 0,
"referral_renewal_countdown": 0,
"role": "child",
"license": "36L7Pg9E-j6Jp2x04-I40UQ39C",
"ttl": "2026-03-02T16:44:10.752514723Z"
},
"config": {
"client_id": "lzaY",
"reserved": [
151,
54,
152
],
"peers": [
{
"public_key": "bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=",
"endpoint": {
"v4": "162.159.192.5:0",
"v6": "[2606:4700:d0::a29f:c005]:0",
"host": "engage.cloudflareclient.com:2408",
"ports": [
2408,
500,
1701,
4500
]
}
}
],
"interface": {
"addresses": {
"v4": "172.16.0.2",
"v6": "2606:4700:110:8921:bf06:c4d7:40b7:8afd"
}
},
"services": {
"http_proxy": "172.16.0.1:2480"
}
},
"token": "50d988c2-b5fb-c829-42dd-a33a960ea734",
"warp_enabled": false,
"waitlist_enabled": false,
"created": "2025-12-02T16:44:10.327083841Z",
"updated": "2025-12-02T16:44:10.327083841Z",
"tos": "2025-12-02T16:44:10.272Z",
"place": 0,
"locale": "zh-CN",
"enabled": true,
"install_id": "095iylvdl1trz7ukonr00g",
"fcm_token": "095iylvdl1trz7ukonr00g:APA91ba32nwi5zphdi3ercafxodyjr6iwlrrgb919l2gcm4h5irun8y8nsuhbdmc0kufcxhopvonqql4gllld8nsjaavi17hf7yfl5qhdpz03oq4u69ngu0s5hyo6wxiy4luk8xeenf1",
"serial_number": "095iylvdl1trz7ukonr00g",
"policy": {
"always_include": [
{
"ip": "162.159.197.4"
},
{
"ip": "2606:4700:102::4"
}
],
"always_exclude": [
{
"ip": "162.159.197.3"
},
{
"ip": "2606:4700:102::3"
}
],
"post_quantum": "enabled_with_downgrades",
"tunnel_protocol": "masque"
}
}'
fi
fi
;;
device )
curl -m5 -sL "https://${WARP_API_URL}/?run=device&device_id=${WARP_DEVICE_ID}&token=${WARP_TOKEN}"
;;
name )
curl -m5 -sL "https://${WARP_API_URL}/?run=name&device_id=${WARP_DEVICE_ID}&token=${WARP_TOKEN}&device_name=${WARP_DEVICE_NAME}"
;;
license )
curl -m5 -sL "https://${WARP_API_URL}/?run=license&device_id=${WARP_DEVICE_ID}&token=${WARP_TOKEN}&license=${WARP_LICENSE}"
;;
cancel )
# 只保留 Teams 或者预设账户,删除其他账户
if ! grep -oqE '"id":[ ]+("(t.[A-F0-9a-f]{8}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{12})|b0fe9b24-3396-486e-a12d-c194dbbb7bfb")' $FILE_PATH; then
curl --request DELETE "https://api.cloudflareclient.com/v0a2158/reg/${WARP_DEVICE_ID}" \
--head \
--silent \
--location \
--header 'User-Agent: okhttp/3.12.1' \
--header 'CF-Client-Version: a-6.10-2158' \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer ${WARP_TOKEN}" | awk '/HTTP/{print $(NF-1)}'
fi
;;
convert )
if [ "$WARP_CONVERT_MODE" = decode ]; then
curl -m5 -sL "https://${WARP_API_URL}/?run=id&convert=${WARP_CONVERT}" | grep -A4 'reserved' | sed 's/.*\(\[.*\)/\1/g; s/],/]/' | tr -d '[:space:]'
elif [ "$WARP_CONVERT_MODE" = encode ]; then
curl -m5 -sL "https://${WARP_API_URL}/?run=id&convert=${WARP_CONVERT//[ \[\]]}" | awk -F '"' '/client_id/{print $(NF-1)}'
elif [ "$WARP_CONVERT_MODE" = file ]; then
if grep -sq '"reserved"' $FILE_PATH; then
grep -A4 'reserved' $FILE_PATH | sed 's/.*\(\[.*\)/\1/g; s/],/]/' | tr -d '[:space:]'
else
local WARP_CONVERT=$(awk -F '"' '/"client_id"/{print $(NF-1)}' $FILE_PATH)
curl -m5 -sL "https://${WARP_API_URL}/?run=id&convert=${WARP_CONVERT}" | grep -A4 'reserved' | sed 's/.*\(\[.*\)/\1/g; s/],/]/' | tr -d '[:space:]'
fi
fi
;;
token-step1 )
curl -m5 -sL "https://${WARP_API_URL}/?run=token&organization=${TEAM_ORGANIZATION}&email=${TEAM_EMAIL}"
;;
token-step2 )
local TEAM_ORGANIZATION=$(sed "s/.*organization=\([^&]\+\)&.*/\1/" <<< "$TEAM_AUTH")
local A=$(sed "s/.*A=\([^&]\+\)&.*/\1/" <<< "$TEAM_AUTH")
local S=$(sed "s/.*S=\([^&]\+\)&.*/\1/" <<< "$TEAM_AUTH")
local N=$(sed "s/.*N=\([^&]\+\)&.*/\1/" <<< "$TEAM_AUTH")
curl -m5 -sL "https://${WARP_API_URL}/?run=token&organization=${TEAM_ORGANIZATION}&A=${A}&S=${S}&N=${N}&code=${TEAM_CODE}"
;;
esac
}
# 检测 warp-go 的安装状态。STATUS: 0-未安装; 1-已安装未启动; 2-已安装启动中; 3-脚本安装中
check_install() {
if [ -s /opt/warp-go/warp.conf ]; then
[[ "$(ip link show | awk -F': ' '{print $2}')" =~ "WARP" ]] && STATUS=2 || STATUS=1
else
STATUS=0
{
# 预下载 warp-go,并添加执行权限,如因 gitlab 接口问题未能获取,默认 v1.0.8
latest=$(wget -qO- -T2 -t1 https://gitlab.com/api/v4/projects/ProjectWARP%2Fwarp-go/releases | awk -F '"' '{for (i=0; i<NF; i++) if ($i=="tag_name") {print $(i+2); exit}}' | sed "s/v//")
latest=${latest:-'1.0.8'}
wget --no-check-certificate -T5 -qO- https://gitlab.com/fscarmen/warp/-/raw/main/warp-go/warp-go_"$latest"_linux_"$ARCHITECTURE".tar.gz | tar xz -C /tmp/ warp-go
chmod +x /tmp/warp-go
}&
fi
}
# 检测 IPv4 IPv6 信息,WARP Ineterface 开启,普通还是 Plus账户 和 IP 信息。由于 ip.sb 会对某些 ip 访问报 error code: 1015,所以使用备用 IP api: ifconfig.co
ip4_info() {
unset IP4_JSON COUNTRY4 ASNORG4 TRACE4 IS_UNINSTALL
IS_UNINSTALL="$1"
[ "$L" = 'C' ] && IS_CHINESE=${IS_CHINESE:-'?lang=zh-CN'}
TRACE4=$(curl --retry 2 -ks4m5 https://www.cloudflare.com/cdn-cgi/trace $INTERFACE_4 | awk -F '=' '/^warp=/{print $NF}')
if [ -n "$TRACE4" ]; then
[ "$IS_UNINSTALL" = 'is_uninstall' ] && WAN4=$(curl -4 --retry 2 -ksm5 --user-agent Mozilla https://api.ip.sb/ip) || WAN4=$(curl --retry 2 -ks4m5 -A Mozilla https://ipinfo.io/ip $INTERFACE_4)
[[ -n "$WAN4" && ! "$WAN4" =~ error[[:space:]]+code:[[:space:]]+1015 ]] && IP4_JSON=$(curl --retry 2 -ksm5 --user-agent Mozilla https://ip.forvps.gq/${WAN4}${IS_CHINESE}) || unset WAN4
IP4_JSON=${IP4_JSON:-"$(curl --retry 2 -ks4m3 --user-agent Mozilla https://ifconfig.co/json $INTERFACE_4)"}
if [ -n "$IP4_JSON" ]; then
WAN4=${WAN4:-"$(sed -En 's/.*"ip":[ ]*"([^"]+)".*/\1/p' <<< "$IP4_JSON")"}
COUNTRY4=$(sed -En 's/.*"country":[ ]*"([^"]+)".*/\1/p' <<< "$IP4_JSON")
ASNORG4=$(sed -En 's/.*"(isp|asn_org)":[ ]*"([^"]+)".*/\2/p' <<< "$IP4_JSON")
fi
fi
}
ip6_info() {
unset IP6_JSON COUNTRY6 ASNORG6 TRACE6 IS_UNINSTALL
IS_UNINSTALL="$1"
[ "$L" = 'C' ] && IS_CHINESE=${IS_CHINESE:-'?lang=zh-CN'}
TRACE6=$(curl --retry 5 -ks6m5 https://www.cloudflare.com/cdn-cgi/trace $INTERFACE_6 | awk -F '=' '/^warp=/{print $NF}')
if [ -n "$TRACE6" ]; then
[ "$IS_UNINSTALL" = 'is_uninstall' ] && WAN6=$(curl -6 --retry 2 -ksm5 --user-agent Mozilla https://api.ip.sb/ip) || WAN6=$(curl --retry 5 -ks6m5 -A Mozilla https://api-ipv6.ip.sb/geoip $INTERFACE_6 | sed 's/.*"ip":"\([^"]\+\)".*/\1/')
[[ -n "$WAN6" && ! "$WAN6" =~ error[[:space:]]+code:[[:space:]]+1015 ]] && IP6_JSON=$(curl --retry 2 -ksm5 --user-agent Mozilla https://ip.forvps.gq/${WAN6}${IS_CHINESE}) || unset WAN6
IP6_JSON=${IP6_JSON:-"$(curl --retry 2 -ks6m3 --user-agent Mozilla https://ifconfig.co/json $INTERFACE_6)"}
if [ -n "$IP6_JSON" ]; then
WAN6=${WAN6:-"$(sed -En 's/.*"ip":[ ]*"([^"]+)".*/\1/p' <<< "$IP6_JSON")"}
COUNTRY6=$(sed -En 's/.*"country":[ ]*"([^"]+)".*/\1/p' <<< "$IP6_JSON")
ASNORG6=$(sed -En 's/.*"(isp|asn_org)":[ ]*"([^"]+)".*/\2/p' <<< "$IP6_JSON")
fi
fi
}
# 帮助说明
help() { hint " $(text 2) "; }
# IPv4 / IPv6 优先设置
stack_priority() {
if [ "$SYSTEM" != OpenWrt ]; then
[ "$OPTION" = s ] && case "$PRIORITY_SWITCH" in
4 )
PRIORITY=1
;;
6 )
PRIORITY=2
;;
d )
:
;;
* )
hint "\n $(text 55) \n" && reading " $(text 4) " PRIORITY
esac
[ -s /etc/gai.conf ] && sed -i '/^precedence \:\:ffff\:0\:0/d;/^label 2002\:\:\/16/d' /etc/gai.conf
case "$PRIORITY" in
1 )
echo "precedence ::ffff:0:0/96 100" >> /etc/gai.conf
;;
2 )
echo "label 2002::/16 2" >> /etc/gai.conf
esac
fi
}
# IPv4 / IPv6 优先结果
result_priority() {
PRIO=(0 0)
if [ -s /etc/gai.conf ]; then
grep -qsE "^precedence[ ]+::ffff:0:0/96[ ]+100" /etc/gai.conf && PRIO[0]=1
grep -qsE "^label[ ]+2002::/16[ ]+2" /etc/gai.conf && PRIO[1]=1
fi
case "${PRIO[*]}" in
'1 0' )
PRIO=4
;;
'0 1' )
PRIO=6
;;
* )
[[ "$(curl -ksm8 -A Mozilla https://ifconfig.co/json | grep 'ip=' | cut -d= -f2)" =~ ^([0-9]{1,3}\.){3} ]] && PRIO=4 || PRIO=6
esac
PRIORITY_NOW=$(text_eval 100)
# 如是快捷方式切换优先级别的话,显示结果
[ "$OPTION" = s ] && hint "\n $PRIORITY_NOW \n"
}
need_install() {
[ "$STATUS" = 0 ] && warning " $(text 11) " && reading " $(text 12) " TO_INSTALL
[[ $TO_INSTALL = [Yy] ]] && install
}
# 更换支持 Netflix WARP IP 改编自 [luoxue-bot] 的成熟作品,地址[https://github.com/luoxue-bot/warp_auto_change_ip]
change_ip() {
need_install
warp_restart() {
warning " $(text_eval 13) "
cp -f /opt/warp-go/warp.conf{,.tmp1}
[ -s /opt/warp-go/License ] && k='+' || k=' free'
register_api warp.conf.tmp2
sed -i '1,6!d' /opt/warp-go/warp.conf.tmp2
tail -n +7 /opt/warp-go/warp.conf.tmp1 >> /opt/warp-go/warp.conf.tmp2
mv /opt/warp-go/warp.conf.tmp2 /opt/warp-go/warp.conf
warp_api "cancel" "/opt/warp-go/warp.conf.tmp1" >/dev/null 2>&1
rm -f /opt/warp-go/warp.conf.tmp*
${SYSTEMCTL_RESTART[int]}
sleep $l
}
# 检测账户类型为 Team 的不能更换
if grep -qE 'Type[ ]+=[ ]+team' /opt/warp-go/warp.conf; then
hint "\n $(text 97) \n" && reading " $(text 4) " CHANGE_ACCOUNT
case "$CHANGE_ACCOUNT" in
2 )
update_license
echo "$LICENSE" > /opt/warp-go/License
echo "$NAME" > /opt/warp-go/Device_Name
;;
3 ) exit 0
esac
fi
# 设置时区,让时间戳时间准确,显示脚本运行时长,中文为 GMT+8,英文为 UTC; 设置 UA
ip_start=$(date +%s)
echo "$SYSTEM" | grep -qE "Alpine" && ( [ "$L" = C ] && timedatectl set-timezone Asia/Shanghai || timedatectl set-timezone UTC )
UA_Browser="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36"
# 根据 lmc999 脚本检测 Netflix Title,如获取不到,使用兜底默认值
local LMC999=($(curl -sSLm4 ${GH_PROXY}https://raw.githubusercontent.com/lmc999/RegionRestrictionCheck/main/check.sh | sed -n 's#.*/title/\([0-9]\+\).*#\1#gp'))
RESULT_TITLE=(${LMC999[*]:0:2})
REGION_TITLE=${LMC999[2]}
[[ ! "${RESULT_TITLE[0]}" =~ ^[0-9]+$ ]] && RESULT_TITLE[0]='81280792'
[[ ! "${RESULT_TITLE[1]}" =~ ^[0-9]+$ ]] && RESULT_TITLE[1]='70143836'
[[ ! "$REGION_TITLE" =~ ^[0-9]+$ ]] && REGION_TITLE=${RESULT_TITLE[1]}
# 检测 WARP 单双栈服务
unset T4 T6
if grep -q "#AllowedIPs" /opt/warp-go/warp.conf; then
T4=1; T6=1
else
grep -q "0\.\0\/0" 2>/dev/null /opt/warp-go/warp.conf && T4=1 || T4=0
grep -q "\:\:\/0" 2>/dev/null /opt/warp-go/warp.conf && T6=1 || T6=0
fi
case "$T4$T6" in
01 )
NF='6'
;;
10 )
NF='4'
;;
11 )
hint "\n $(text 14) \n" && reading " $(text 4) " NETFLIX
NF='4' && [ "$NETFLIX" = 2 ] && NF='6'
esac
# 输入解锁区域
if [ -z "$EXPECT" ]; then
[ -n "$NF" ] && REGION=$(tr 'a-z' 'A-Z' <<< "$(curl --user-agent "${UA_Browser}" --interface WARP -$NF -fs --max-time 10 --write-out %{redirect_url} --output /dev/null "https://www.netflix.com/title/$REGION_TITLE" | sed 's/.*com\/\([^-/]\{1,\}\).*/\1/g')")
REGION=${REGION:-'US'}
reading " $(text_eval 15) " EXPECT
until [[ -z "$EXPECT" || "$EXPECT" = [Yy] || "$EXPECT" =~ ^[A-Za-z]{2}$ ]]; do
reading " $(text_eval 15) " EXPECT
done
[[ -z "$EXPECT" || "$EXPECT" = [Yy] ]] && EXPECT="$REGION"
fi
# 解锁检测程序。 i=尝试次数; b=当前账户注册次数; j=注册账户失败的最大次数; l=账户注册失败后等待重试时间;
i=0; j=10; l=8
while true; do
b=0
(( i++ )) || true
ip_now=$(date +%s); RUNTIME=$((ip_now - ip_start)); DAY=$(( RUNTIME / 86400 )); HOUR=$(( (RUNTIME % 86400 ) / 3600 )); MIN=$(( (RUNTIME % 86400 % 3600) / 60 )); SEC=$(( RUNTIME % 86400 % 3600 % 60 ))
ip${NF}_info
WAN=$(eval echo \$WAN$NF) && ASNORG=$(eval echo \$ASNORG$NF)
COUNTRY=$(eval echo \$COUNTRY$NF)
unset RESULT REGION
for p in ${!RESULT_TITLE[@]}; do
RESULT[p]=$(curl --user-agent "${UA_Browser}" --interface WARP -$NF -fsL --write-out %{http_code} --output /dev/null --max-time 10 "https://www.netflix.com/title/${RESULT_TITLE[p]}")
[ "${RESULT[p]}" = 200 ] && break
done
if [[ "${RESULT[@]}" =~ 200 ]]; then
REGION=$(tr 'a-z' 'A-Z' <<< "$(curl --user-agent "${UA_Browser}" --interface WARP -$NF -fs --max-time 10 --write-out %{redirect_url} --output /dev/null "https://www.netflix.com/title/$REGION_TITLE" | sed 's/.*com\/\([^-/]\{1,\}\).*/\1/g')")
REGION=${REGION:-'US'}
echo "$REGION" | grep -qi "$EXPECT" && info " $(text_eval 16) " && rm -f /opt/warp-go/warp.conf.tmp1 && i=0 && sleep 1h || warp_restart
else
warp_restart
fi
done
}
# 关闭 WARP 网络接口,并删除 warp-go
uninstall() {
unset IP4 IP6 WAN4 WAN6 COUNTRY4 COUNTRY6 ASNORG4 ASNORG6 INTERFACE_4 INTERFACE_6
# 如已安装 warp_unlock 项目,先行卸载
[ -s /usr/bin/warp_unlock.sh ] && bash <(curl -sSL https://gitlab.com/fscarmen/warp_unlock/-/raw/main/unlock.sh) -U -$L
# 卸载
systemctl disable --now warp-go >/dev/null 2>&1
kill -15 $(pgrep warp-go) >/dev/null 2>&1
warp_api "cancel" "/opt/warp-go/warp.conf" >/dev/null 2>&1
rm -rf /opt/warp-go /lib/systemd/system/warp-go.service /usr/bin/warp-go /tmp/warp-go*
[ -s /opt/warp-go/tun.sh ] && rm -f /opt/warp-go/tun.sh && sed -i '/tun.sh/d' /etc/crontab
# 显示卸载结果
ip4_info is_uninstall
ip6_info is_uninstall
info " $(text 17)\n IPv4: $WAN4 $COUNTRY4 $ASNORG4\n IPv6: $WAN6 $COUNTRY6 $ASNORG6 "
}
# 同步脚本至最新版本
ver() {
mkdir -p /tmp; rm -f /tmp/warp-go.sh
wget -T2 -O /tmp/warp-go.sh https://gitlab.com/fscarmen/warp/-/raw/main/warp-go.sh
if [ -s /tmp/warp-go.sh ]; then
mv /tmp/warp-go.sh /opt/warp-go/
chmod +x /opt/warp-go/warp-go.sh
ln -sf /opt/warp-go/warp-go.sh /usr/bin/warp-go
info " $(text 18): $(grep ^VERSION /opt/warp-go/warp-go.sh | sed "s/.*=//g") $(text 19): $(grep "${L}\[1\]" /opt/warp-go/warp-go.sh | cut -d \" -f2) "
fi
exit
}
# i=当前尝试次数,j=要尝试的次数
net() {
unset IP4 IP6 WAN4 WAN6 COUNTRY4 COUNTRY6 ASNORG4 ASNORG6
i=1; j=5
grep -qE "^AllowedIPs[ ]+=.*0\.\0\/0|#AllowedIPs" 2>/dev/null /opt/warp-go/warp.conf && INTERFACE_4='--interface WARP'
grep -qE "^AllowedIPs[ ]+=.*\:\:\/0|#AllowedIPs" 2>/dev/null /opt/warp-go/warp.conf && INTERFACE_6='--interface WARP'
hint " $(text_eval 20)\n $(text_eval 59) "
[ "$KEEP_FREE" != 1 ] && ${SYSTEMCTL_RESTART[int]}
grep -q "#AllowedIPs" /opt/warp-go/warp.conf && sleep 8 || sleep 1
ip4_info; ip6_info
until [[ "$TRACE4$TRACE6" =~ on|plus ]]; do
(( i++ )) || true
hint " $(text_eval 59) "
${SYSTEMCTL_RESTART[int]}
grep -q "#AllowedIPs" /opt/warp-go/warp.conf && sleep 8 || sleep 1
ip4_info; ip6_info
if [[ "$i" = "$j" ]]; then
if [ -s /opt/warp-go/warp.conf.tmp1 ]; then
i=0 && info " $(text 22) " &&
mv -f /opt/warp-go/warp.conf.tmp1 /opt/warp-go/warp.conf
else
${SYSTEMCTL_STOP[int]} >/dev/null 2>&1
error " $(text_eval 23) "
fi
fi
done
ACCOUNT_TYPE=$(grep "Type" /opt/warp-go/warp.conf | cut -d= -f2 | sed "s# ##g")
[ "$ACCOUNT_TYPE" = 'plus' ] && check_quota
grep -q '#AllowedIPs' /opt/warp-go/warp.conf && GLOBAL_TYPE="$(text 24)"
info " $(text_eval 25) "
[ "$OPTION" = o ] && info " IPv4: $WAN4 $COUNTRY4 $ASNORG4\n IPv6: $WAN6 $COUNTRY6 $ASNORG6 "
[ -n "$QUOTA" ] && info " $(text 26): $QUOTA "
}
# api 注册账户, 使用官方 api 脚本
register_api() {
local REGISTER_FILE="$1"
local i=0; local j=5
[ -n "$2" ] && hint " $(text_eval $2) "
until [ -s /opt/warp-go/$REGISTER_FILE ]; do
((i++)) || true
[ "$i" -gt "$j" ] && rm -f /opt/warp-go/warp.conf.tmp* && error " $(text_eval 50) "
[ -n "$3" ] && hint " $(text_eval $3) "
if ! grep -sq 'PrivateKey' /opt/warp-go/$REGISTER_FILE; then
unset CF_API_REGISTER API_DEVICE_ID API_ACCESS_TOKEN API_PRIVATEKEY API_TYPE
rm -f /opt/warp-go/$REGISTER_FILE
CF_API_REGISTER="$(warp_api "register" "" "" "" "$TOKEN" 2>/dev/null)"
[[ -n "$NF" && -n "$EXPECT" && -s /opt/warp-go/License ]] && LICENSE=$(cat /opt/warp-go/License) && NAME=$(cat /opt/warp-go/Device_Name)
[[ -z "$LICENSE" && -s /opt/warp-go/License ]] && rm -f /opt/warp-go/License /opt/warp-go/Device_Name
if grep -q 'private_key' <<< "$CF_API_REGISTER"; then
local API_DEVICE_ID=$(expr "$CF_API_REGISTER " | grep -m1 'id' | cut -d\" -f4)
local API_ACCESS_TOKEN=$(expr "$CF_API_REGISTER " | grep '"token' | cut -d\" -f4)
local API_PRIVATEKEY=$(expr "$CF_API_REGISTER " | grep 'private_key' | cut -d\" -f4)
local API_TYPE=$(expr "$CF_API_REGISTER " | grep 'account_type' | cut -d\" -f4)
[[ -z "$NF" && -z "$EXPECT" && -n "$TOKEN" ]] && ( [ "$API_TYPE" = 'team' ] && info "\n teams $(text_eval 105) \n" || warning "\n teams $(text_eval 106) \n" )
cat > /opt/warp-go/$REGISTER_FILE << EOF
[Account]
Device = $API_DEVICE_ID
PrivateKey = $API_PRIVATEKEY
Token = $API_ACCESS_TOKEN
Type = $API_TYPE
[Device]
Name = WARP
MTU = 1280
[Peer]
PublicKey = bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=
Endpoint = engage.cloudflareclient.com:2408
KeepAlive = 30
# AllowedIPs = 0.0.0.0/0
# AllowedIPs = ::/0
EOF
fi
fi
if grep -sq 'Account' /opt/warp-go/$REGISTER_FILE; then
echo -e "\n[Script]\nPostUp =\nPostDown =" >> /opt/warp-go/$REGISTER_FILE && sed -i 's/\r//' /opt/warp-go/$REGISTER_FILE
if [ -n "$LICENSE" ]; then
local RESULT=$(warp_api "license" "/opt/warp-go/$REGISTER_FILE" "$LICENSE")
if [[ "$RESULT" =~ '"warp_plus": true' ]]; then
warp_api "name" "/opt/warp-go/$REGISTER_FILE" "" "$NAME" >/dev/null 2>&1
echo "$LICENSE" > /opt/warp-go/License
echo "$NAME" > /opt/warp-go/Device_Name
sed -i "s/Type =.*/Type = plus/g" /opt/warp-go/$REGISTER_FILE
[[ -z "$NF" && -z "$EXPECT" ]] && info "\n License: $LICENSE $(text_eval 105) \n"
else
warning "\n License: $LICENSE $(text_eval 106) \n"
fi
elif [[ -s /opt/warp-go/License && -s /opt/warp-go/Device_Name ]]; then
if [ -s /opt/warp-go/warp.conf.tmp ]; then
[ -s /opt/warp-go/License ] && warp_api "license" "/opt/warp-go/$REGISTER_FILE" "$(cat /opt/warp-go/License)" >/dev/null 2>&1
[ -s /opt/warp-go/Device_Name ] && warp_api "name" "/opt/warp-go/$REGISTER_FILE" "" "$(cat /opt/warp-go/Device_Name)" >/dev/null 2>&1
fi
fi
else
rm -f /opt/warp-go/$REGISTER_FILE
fi
done
}
# WARP 开关,先检查是否已安装,再根据当前状态转向相反状态
onoff() {
case "$STATUS" in
0 )
need_install
;;
1 )
net
;;
2 )
${SYSTEMCTL_STOP[int]}; info " $(text 27) "
esac
}
# 检查系统 WARP 单双栈情况。为了速度,先检查 warp-go 配置文件里的情况,再判断 trace
check_stack() {
if [ -s /opt/warp-go/warp.conf ]; then
if grep -q "^#AllowedIPs" /opt/warp-go/warp.conf; then
T4=2
else
grep -q ".*0\.\0\/0" 2>/dev/null /opt/warp-go/warp.conf && T4=1 || T4=0
grep -q ".*\:\:\/0" 2>/dev/null /opt/warp-go/warp.conf && T6=1 || T6=0
fi
else
case "$TRACE4" in
off )
T4='0'
;;
'on'|'plus' )
T4='1'
esac
case "$TRACE6" in
off )
T6='0'
;;
'on'|'plus' )
T6='1'
esac
fi
CASE=("@0" "0@" "0@0" "@1" "0@1" "1@" "1@0" "1@1" "2@" "@")
for m in ${!CASE[@]}; do
[ "$T4@$T6" = "${CASE[m]}" ] && break
done
WARP_BEFORE=("" "" "" "WARP $(text 99) IPv6 only" "WARP $(text 99) IPv6" "WARP $(text 99) IPv4 only" "WARP $(text 99) IPv4" "WARP $(text 99) $(text 96)" "WARP $(text 98) $(text 96)")
WARP_AFTER1=("" "" "" "WARP $(text 99) IPv4" "WARP $(text 99) IPv4" "WARP $(text 99) IPv6" "WARP $(text 99) IPv6" "WARP $(text 99) IPv4" "WARP $(text 99) IPv4")
WARP_AFTER2=("" "" "" "WARP $(text 99) $(text 96)" "WARP $(text 99) $(text 96)" "WARP $(text 99) $(text 96)" "WARP $(text 99) $(text 96)" "WARP $(text 99) IPv6" "WARP $(text 99) $(text 96)")
TO1=("" "" "" "014" "014" "106" "106" "114" "014")
TO2=("" "" "" "01D" "01D" "10D" "10D" "116" "01D")
SHORTCUT1=("" "" "" "(warp-go 4)" "(warp-go 4)" "(warp-go 6)" "(warp-go 6)" "(warp-go 4)" "(warp-go 4)")
SHORTCUT2=("" "" "" "(warp-go d)" "(warp-go d)" "(warp-go d)" "(warp-go d)" "(warp-go 6)" "(warp-go d)")
# 判断用于检测 NAT VSP,以选择正确配置文件
if [ "$m" -le 3 ]; then
NAT=("0@1@" "1@0@1" "1@1@1" "0@1@1")
for n in ${!NAT[@]}; do [ "$IPV4@$IPV6@$INET4" = "${NAT[n]}" ] && break; done
NATIVE=("IPv6 only" "IPv4 only" "$(text 94)" "NAT IPv4")
CONF1=("014" "104" "114" "11N4")
CONF2=("016" "106" "116" "11N6")
CONF3=("01D" "10D" "11D" "11ND")
elif [ "$m" = 9 ]; then
error "\n $(text 108) \n"
fi
}
# 检查全局状态
check_global() {
[ -s /opt/warp-go/warp.conf ] && grep -q '#AllowedIPs' /opt/warp-go/warp.conf && NON_GLOBAL=1
}
# 单双栈在线互换。先看菜单是否有选择,再看传参数值,再没有显示2个可选项
stack_switch() {
need_install
check_global
if [ "$NON_GLOBAL" = 1 ]; then
if [[ "$CHOOSE" != [12] ]]; then
warning " $(text 28) " && reading " $(text 29) " TO_GLOBAL
[[ "$TO_GLOBAL" != [Yy] ]] && exit 0 || global_switch
else
global_switch
fi
fi
# WARP 单双栈切换选项
SWITCH014="s#AllowedIPs.*#AllowedIPs = 0.0.0.0/0#g"
SWITCH01D="s#AllowedIPs.*#AllowedIPs = 0.0.0.0/0,::/0#g"
SWITCH106="s#AllowedIPs.*#AllowedIPs = ::/0#g"
SWITCH10D="s#AllowedIPs.*#AllowedIPs = 0.0.0.0/0,::/0#g"
SWITCH114="s#AllowedIPs.*#AllowedIPs = 0.0.0.0/0#g"
SWITCH116="s#AllowedIPs.*#AllowedIPs = ::/0#g"
check_stack
if [[ "$CHOOSE" = [12] ]]; then
TO=$(eval echo \${TO$CHOOSE[m]})
elif [[ "$SWITCHCHOOSE" = [46D] ]]; then
if [[ "$TO_GLOBAL" = [Yy] ]]; then
if [[ "$T4@$T6@$SWITCHCHOOSE" =~ '1@0@4'|'0@1@6'|'1@1@D' ]]; then
grep -q "^AllowedIPs.*0\.\0\/0" 2>/dev/null /opt/warp-go/warp.conf || unset INTERFACE_4 INTERFACE_6
OPTION=o && net
exit 0
else
TO="$T4$T6$SWITCHCHOOSE"
fi
else
[[ "$T4@$T6@$SWITCHCHOOSE" =~ '1@0@4'|'0@1@6'|'1@1@D' ]] && error " $(text 30) " || TO="$T4$T6$SWITCHCHOOSE"
fi
else
STACK_OPTION[1]="$(text_eval 31)"; STACK_OPTION[2]="$(text_eval 32)"
hint "\n $(text_eval 33) \n" && reading " $(text 4) " SWITCHTO
case "$SWITCHTO" in
1 )
TO=${TO1[m]}
;;
2 )
TO=${TO2[m]}
;;
0 )
exit
;;
* )
warning " $(text 34) [0-2] "; sleep 1; stack_switch
esac
fi
[ "${#TO}" != 3 ] && error " $(text 10) " || sed -i "$(eval echo "\$SWITCH$TO")" /opt/warp-go/warp.conf
case "$TO" in
014|114 )
INTERFACE_4='--interface WARP'; unset INTERFACE_6
;;
106|116 )
INTERFACE_6='--interface WARP'; unset INTERFACE_4
;;
01D|10D )
INTERFACE_4='--interface WARP'; INTERFACE_6='--interface WARP'
esac
OPTION=o && net
}
# 全局 / 非全局在线互换
global_switch() {
# 如状态不是安装中,则检测是否已安装 warp-go,如已安装,则停止 systemd
if [ "$STATUS" != 3 ]; then
need_install
${SYSTEMCTL_STOP[int]}
fi
if grep -q "^Allowed" /opt/warp-go/warp.conf; then
sed -i "s/^#//g; s/^AllowedIPs.*/#&/g" /opt/warp-go/warp.conf
sleep 2
else
sed -i "s/^#//g; s/.*NonGlobal/#&/g" /opt/warp-go/warp.conf
unset GLOBAL_TYPE
fi
# 如状态不是安装中,不是非全局转换到全局时的快捷或菜单选择情况。则开始 systemd,
if [[ "$STATUS" != 3 && "$TO_GLOBAL" != [Yy] && "$CHOOSE" != [12] ]]; then
${SYSTEMCTL_START[int]}
OPTION=o && net
fi
}
# 检测系统信息
check_system_info() {
info " $(text 35) "
# 由于 warp-go 内置了 wireguard-go ,而 wireguard-go 运行时会先判断 tun 设备,如果文件不存在,则马上退出
[ ! -e /dev/net/tun ] && error " $(text 36) "
# 必须加载 TUN 模块,先尝试在线打开 TUN。尝试成功放到启动项,失败作提示并退出脚本
TUN=$(cat /dev/net/tun 2>&1 | tr 'A-Z' 'a-z')
if [[ ! "$TUN" =~ 'in bad state'|'处于错误状态' ]]; then
mkdir -p /opt/warp-go/ >/dev/null 2>&1
cat >/opt/warp-go/tun.sh << EOF
#!/usr/bin/env bash
mkdir -p /dev/net
mknod /dev/net/tun c 10 200 2>/dev/null
[ ! -e /dev/net/tun ] && exit 1
chmod 0666 /dev/net/tun
EOF
bash /opt/warp-go/tun.sh
TUN=$(cat /dev/net/tun 2>&1 | tr 'A-Z' 'a-z')
if [[ ! "$TUN" =~ 'in bad state'|'处于错误状态' ]]; then
rm -f /opt/warp-go/tun.sh && error " $(text 36) "
else
chmod +x /opt/warp-go/tun.sh
echo "$SYSTEM" | grep -qvE "Alpine|OpenWrt" && echo "@reboot root bash /opt/warp-go/tun.sh" >> /etc/crontab
fi
fi
if [ "$STATUS" != 0 ]; then
if grep -qE "^AllowedIPs.*\.0/0,::/0|^#AllowedIPs" 2>/dev/null /opt/warp-go/warp.conf; then
INTERFACE_4='--interface WARP'; INTERFACE_6='--interface WARP'; local IP_INTERFACE_4='dev WARP'; local IP_INTERFACE_6='dev WARP'; local PING_INTERFACE_4='-I WARP'; local PING_INTERFACE_6='-I WARP'
elif grep -q '^AllowedIPs.*\.0/0$' 2>/dev/null /opt/warp-go/warp.conf; then
INTERFACE_4='--interface WARP'; unset INTERFACE_6; local IP_INTERFACE_4='dev WARP'; unset IP_INTERFACE_6; local PING_INTERFACE_4='-I WARP'; unset PING_INTERFACE_6
elif grep -q '^AllowedIPs.*::/0$' 2>/dev/null /opt/warp-go/warp.conf; then
INTERFACE_6='--interface WARP'; unset INTERFACE_4; unset IP_INTERFACE_4; local IP_INTERFACE_6='dev WARP'; unset PING_INTERFACE_4; local PING_INTERFACE_6='-I WARP'
fi
fi
# 判断机器原生状态类型
IPV4=0; IPV6=0
LAN4=$(ip route get 192.168.193.10 $IP_INTERFACE_4 2>/dev/null | awk '{for (i=0; i<NF; i++) if ($i=="src") {print $(i+1)}}')
LAN6=$(ip route get 2606:4700:d0::a29f:c001 $IP_INTERFACE_6 2>/dev/null | awk '{for (i=0; i<NF; i++) if ($i=="src") {print $(i+1)}}')
[[ "$LAN4" =~ ^([0-9]{1,3}\.){3} ]] && local INET4=1
[[ "$LAN6" != "::1" && "$LAN6" =~ ^[a-f0-9:]+$ ]] && local INET6=1
[ "$INET6" = 1 ] && $PING6 -c2 -w10 2606:4700:d0::a29f:c001 $PING_INTERFACE_4 >/dev/null 2>&1 && IPV6=1 && STACK=-6
[ "$INET4" = 1 ] && ping -c2 -W3 162.159.192.1 $PING_INTERFACE_6 >/dev/null 2>&1 && IPV4=1 && STACK=-4
[ "$IPV4" = 1 ] && ip4_info
[ "$IPV6" = 1 ] && ip6_info
}
# 输入 WARP+ 账户(如有),限制位数为空或者26位以防输入错误
input_license() {
[ -z "$LICENSE" ] && reading " $(text 38) " LICENSE
i=5
until [[ -z "$LICENSE" || "$LICENSE" =~ ^[A-Z0-9a-z]{8}-[A-Z0-9a-z]{8}-[A-Z0-9a-z]{8}$ ]]; do
(( i-- )) || true
[ "$i" = 0 ] && error " $(text 39) " || reading " $(text_eval 40) " LICENSE
done
[[ -n "$LICENSE" && -z "$NAME" ]] && reading " $(text 41) " NAME
[ -n "$NAME" ] && NAME="${NAME//[[:space:]]/_}" || NAME="${NAME:-warp-go}"
}
# 升级 WARP+ 账户(如有),限制位数为空或者26位以防输入错误,WARP interface 可以自定义设备名(不允许字符串间有空格,如遇到将会以_代替)
update_license() {
[ -z "$LICENSE" ] && reading " $(text 42) " LICENSE
i=5
until [[ "$LICENSE" =~ ^[A-Z0-9a-z]{8}-[A-Z0-9a-z]{8}-[A-Z0-9a-z]{8}$ ]]; do
(( i-- )) || true
[ "$i" = 0 ] && error " $(text 39) " || reading " $(text_eval 43) " LICENSE
done
[[ -n "$LICENSE" && -z "$NAME" ]] && reading " $(text 41) " NAME
[ -n "$NAME" ] && NAME="${NAME//[[:space:]]/_}" || NAME="${NAME:-warp-go}"
}
# 通过组织名和邮箱获取 Teams Token, 如果 Token 以 com.cloudflare.warp 开头,将自动删除多余部分
get_token() {
local ERROR_TIMES=0
until grep -sq 'organization=' <<< "$TEAM_AUTH"; do
unset TEAM_ORGANIZATION TEAM_AUTH
(( ERROR_TIMES++ ))
if [[ "$ERROR_TIMES" > 5 ]]; then
error "\n $(text 39) \n"
else
[ "$ERROR_TIMES" = 1 ] && reading "\n $(text 44) " TEAM_ORGANIZATION || reading "\n $(text 111) " TEAM_ORGANIZATION
[[ -n "$TEAM_ORGANIZATION" && -z "$TEAM_EMAIL" ]] && reading " $(text 109) " TEAM_EMAIL
[ -n "$TEAM_EMAIL" ] && local TEAM_AUTH=$(warp_api "token-step1" "" "" "" "" "" "" "" "$TEAM_ORGANIZATION" "$TEAM_EMAIL")
fi
done
if grep -sq 'organization=' <<< "$TEAM_AUTH"; then
local ERROR_TIMES=0
until grep -sq '^com.cloudflare.warp:' <<< "$TEAM_TOKEN"; do
unset TEAM_CODE TOKEN
(( ERROR_TIMES++ ))
if [[ "$ERROR_TIMES" > 5 ]]; then
error "\n $(text 39) \n"
else
[ "$ERROR_TIMES" = 1 ] && reading " $(text 110) " TEAM_CODE || reading " $(text 112) " TEAM_CODE
[[ "$TEAM_CODE" =~ ^[0-9]{6}$ ]] && local TEAM_TOKEN=$(warp_api "token-step2" "" "" "" "" "" "" "$TEAM_AUTH" "" "" "$TEAM_CODE")
fi
done
grep -sq '^com.cloudflare.warp:' <<< "$TEAM_TOKEN" && [ -z "$NAME" ] && TOKEN=${TEAM_TOKEN#*=} && reading " $(text 41) " NAME
[ -n "$NAME" ] && NAME="${NAME//[[:space:]]/_}" || NAME="${NAME:-warp-go}"
fi
}
# 免费 WARP 账户升级 WARP+ 或 Teams 账户
update() {
need_install
[ ! -s /opt/warp-go/warp.conf ] && error " $(text 21) "
ACCOUNT_TYPE=$(grep "Type" /opt/warp-go/warp.conf | cut -d= -f2 | sed "s# ##g")
case "$ACCOUNT_TYPE" in
free )
CHANGE_TYPE=$(text 47)
;;
team )
CHANGE_TYPE=$(text 48)
;;
plus )
CHANGE_TYPE=$(text 49)
check_quota
[[ "$QUOTA" =~ '.' ]] && PLUS_QUOTA="\\n $(text 26): $QUOTA"
esac
[ -z "$LICENSE_TYPE" ] && hint "\n $(text_eval 46) \n" && reading " $(text 4) " LICENSE_TYPE
case "$LICENSE_TYPE" in
1|2 )
unset QUOTA
case "$LICENSE_TYPE" in
1 )
k=' free'
[ "$ACCOUNT_TYPE" = free ] && KEEP_FREE='1'
[ -s /opt/warp-go/Device_Name ] && rm -f /opt/warp-go/Device_Name
if [ "$ACCOUNT_TYPE" = free ]; then
OPTION=o && net
exit 0
fi
;;
2 )
k='+'
update_license
esac
cp -f /opt/warp-go/warp.conf{,.tmp1}
warp_api "cancel" "/opt/warp-go/warp.conf" >/dev/null 2>&1
[ -s /opt/warp-go/warp.conf ] && rm -f /opt/warp-go/warp.conf
register_api warp.conf 58 59
head -n +6 /opt/warp-go/warp.conf > /opt/warp-go/warp.conf.tmp2
tail -n +7 /opt/warp-go/warp.conf.tmp1 >> /opt/warp-go/warp.conf.tmp2
rm -f /opt/warp-go/warp.conf.tmp1
mv -f /opt/warp-go/warp.conf.tmp2 /opt/warp-go/warp.conf
OPTION=o && net
;;
3 )
unset QUOTA
hint " $(text 113) " && reading " $(text 4) " TEAMS_TYPE
grep -qw "1" <<< "$TEAMS_TYPE" && get_token
if [ -n "$TOKEN" ]; then
k=' teams'
register_api warp.conf.tmp 58 59
for a in {2..5}; do
sed -i "${a}s#.*#$(sed -ne ${a}p /opt/warp-go/warp.conf.tmp)#" /opt/warp-go/warp.conf
done
[ -n "$NAME" ] && echo "$NAME" > /opt/warp-go/Device_Name && warp_api "name" "/opt/warp-go/warp.conf" "" "$NAME" >/dev/null 2>&1
rm -f /opt/warp-go/warp.conf.tmp
else
sed -i "s#^Device.*#Device = FSCARMEN-WARP-SHARE-TEAM#g; s#.*PrivateKey.*#PrivateKey = SHVqHEGI7k2+OQ/oWMmWY2EQObbRQjRBdDPimh0h1WY=#g; s#.*Token.*#Token = PROTECTED_PLACEHOLDER#g; s#.*Type.*#Type = team#g" /opt/warp-go/warp.conf
echo 'SHARE' > /opt/warp-go/Device_Name
fi
grep -qE 'Type[ ]+=[ ]+team' /opt/warp-go/warp.conf && echo "$NAME" > /opt/warp-go/Device_Name
OPTION=o && net
;;
0 )
unset LICENSE_TYPE
menu
;;
* )
warning " $(text 34) [0-3] "; sleep 1; unset LICENSE_TYPE; update
esac
}
# 输出 wireguard 和 sing-box 配置文件
export_file() {
if [ -s /opt/warp-go/warp-go ]; then
[ ! -s /opt/warp-go/warp.conf ] && register_api warp.conf
/opt/warp-go/warp-go --config=/opt/warp-go/warp.conf --export-wireguard=/opt/warp-go/wgcf.conf >/dev/null 2>&1
/opt/warp-go/warp-go --config=/opt/warp-go/warp.conf --export-singbox=/opt/warp-go/singbox.json >/dev/null 2>&1
else
error " $(text 51) "
fi
info "\n $(text 52) "
cat /opt/warp-go/wgcf.conf
echo -e "\n\n"
info " $(text 101) "
cat /opt/warp-go/singbox.json | $JSON_TOOL
echo -e "\n\n"
}
# warp-go 安装
install() {
# 已经状态码不为 0, 即已安装, 脚本退出
[ "$STATUS" != 0 ] && error " $(text 53) "
# CONF 参数如果不是3位或4位, 即检测不出正确的配置参数, 脚本退出
[[ "${#CONF}" != [34] ]] && error " $(text 10) "
# 先删除之前安装,可能导致失败的文件
rm -rf /opt/warp-go/warp-go /opt/warp-go/warp.conf
# 后台优选最佳 MTU
{
# 反复测试最佳 MTU。 Wireguard Header:IPv4=60 bytes,IPv6=80 bytes1280 ≤ MTU ≤ 1420。 ping = 8(ICMP回显示请求和回显应答报文格式长度) + 20(IP首部) 。
# 详细说明:<[WireGuard] Header / MTU sizes for Wireguard>:https://lists.zx2c4.com/pipermail/wireguard/2017-December/002201.html
# MTU 初始范围(适用于 WireGuard 等封装,IPv4/IPv6 都保守选 1280-1420
local MIN_MTU=1280
local MAX_MTU=1500
local TEST_IP
local PING_CMD
local BEST_MTU=1280
if [ "$IPV4$IPV6" = "01" ]; then
TEST_IP="2606:4700:d0::a29f:c001"
PING_CMD="$PING6"
else
TEST_IP="162.159.192.1"
PING_CMD="ping"
fi
# 二分查找能 ping 通的最大 MTU(不碎片)
while [ $((MIN_MTU <= MAX_MTU)) -eq 1 ]; do
local MID_MTU=$(( (MIN_MTU + MAX_MTU) / 2 ))
if $PING_CMD -c1 -W1 -s $MID_MTU -M do "$TEST_IP" >/dev/null 2>&1; then
BEST_MTU=$MID_MTU
MIN_MTU=$((MID_MTU + 1)) # 尝试更大值
else
MAX_MTU=$((MID_MTU - 1)) # 减小范围
fi
done
# 最终微调确认 BEST_MTU 是最大可用值
for (( i=BEST_MTU+1; i<=1420; i++ )); do
if $PING_CMD -c1 -W1 -s $i -M do "$TEST_IP" >/dev/null 2>&1; then
BEST_MTU=$i
else
break
fi
done
# 返回最终 MTU(按需减包头)——可自定义减多少
# WireGuard+28 是 IP+UDP-60 / -80 是安全包头(例如 wireguard + extra overhead
grep -q ':' <<< "$TEST_IP" && BEST_MTU=$((BEST_MTU + 28 - 80)) || BEST_MTU=$((BEST_MTU + 28 - 60))
# 确保范围安全
[ "$BEST_MTU" -lt 1280 ] && BEST_MTU=1280
[ "$BEST_MTU" -lt 1280 ] && BEST_MTU=1280
[ "$BEST_MTU" -gt 1420 ] && BEST_MTU=1420
echo "$BEST_MTU" > /tmp/warp-go-mtu
}&
# 后台优选优选 WARP Endpoint
{
# Removed best endpoint feature to adapt to official adjustments
# Use default endpoint: engage.cloudflareclient.com:2408
echo "engage.cloudflareclient.com:2408" > /tmp/warp-go-endpoint
}&
# 询问是否有 WARP+ 或 Teams 账户
[ -z "$LICENSE_TYPE" ] && hint "\n $(text 54) \n" && reading " $(text 4) " LICENSE_TYPE
case "$LICENSE_TYPE" in
1 )
input_license
;;
2 )
hint " $(text 113) " && reading " $(text 4) " TEAMS_TYPE
grep -qw "1" <<< "$TEAMS_TYPE" && get_token
;;
esac
# 选择优先使用 IPv4 /IPv6 网络
[ -z "$PRIORITY" ] && hint "\n $(text 55) \n" && reading " $(text 4) " PRIORITY
# 脚本开始时间
start=$(date +%s)
# 注册 Teams 账户 (将生成 warp 文件保存账户信息)
{
mkdir -p /opt/warp-go/ >/dev/null 2>&1
wait
[ ! -s /tmp/warp-go ] && error " $(text 56) " || mv -f /tmp/warp-go /opt/warp-go/
[ ! -s /opt/warp-go/warp-go ] && error " $(text 57) "
# 注册用户自定义 token 的 Teams 账户
if [ "$LICENSE_TYPE" = 2 ]; then
if [ -n "$TOKEN" ]; then
k=' teams'
register_api warp.conf 58
# 注册公用 token 的 Teams 账户
else
cat > /opt/warp-go/warp.conf << EOF
[Account]
Device = FSCARMEN-WARP-SHARE-TEAM
PrivateKey = SHVqHEGI7k2+OQ/oWMmWY2EQObbRQjRBdDPimh0h1WY=
Token = PROTECTED_PLACEHOLDER
Type = team
[Device]
Name = WARP
MTU = 1280
[Peer]
PublicKey = bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=
Endpoint = engage.cloudflareclient.com:2408
KeepAlive = 30
# AllowedIPs = 0.0.0.0/0
# AllowedIPs = ::/0
[Script]
#PostUp =
#PostDown =
EOF
echo 'SHARE' > /opt/warp-go/Device_Name
fi
# 注册免费和 Plus 账户
else
[ -n "$LICENSE" ] && k='+' || k=' free'
register_api warp.conf 58 59
fi
# 如为 Plus 或 Team 账户,把设备名记录到文件 /opt/warp-go/Device_Name; Plus 账户的话,把 License 保存到 /opt/warp-go/License;
grep -qE 'Type[ ]+=[ ]+plus' /opt/warp-go/warp.conf && [ -n "$NAME" ] && echo "$NAME" > /opt/warp-go/Device_Name && echo "$LICENSE" > /opt/warp-go/License
grep -qE 'Type[ ]+=[ ]+team' /opt/warp-go/warp.conf && [ -n "$NAME" ] && echo "$NAME" > /opt/warp-go/Device_Name && warp_api "name" "/opt/warp-go/warp.conf" "" "$NAME" >/dev/null 2>&1
# 生成非全局执行文件并赋权
cat > /opt/warp-go/NonGlobalUp.sh << EOF
sleep 5
ip -4 rule add oif WARP lookup 60000
ip -4 rule add table main suppress_prefixlength 0
ip -4 route add default dev WARP table 60000
ip -6 rule add oif WARP lookup 60000
ip -6 rule add table main suppress_prefixlength 0
ip -6 route add default dev WARP table 60000
EOF
cat > /opt/warp-go/NonGlobalDown.sh << EOF
ip -4 rule delete oif WARP lookup 60000
ip -4 rule delete table main suppress_prefixlength 0
ip -6 rule delete oif WARP lookup 60000
ip -6 rule delete table main suppress_prefixlength 0
EOF
chmod +x /opt/warp-go/NonGlobalUp.sh /opt/warp-go/NonGlobalDown.sh
info "\n $(text 61) \n"
}
# 对于 IPv4 only VPS 开启 IPv6 支持
{
[ "$IPV4$IPV6" = 10 ] && [[ $(sysctl -a 2>/dev/null | grep 'disable_ipv6.*=.*1') || $(grep -s "disable_ipv6.*=.*1" /etc/sysctl.{conf,d/*} ) ]] &&
(sed -i '/disable_ipv6/d' /etc/sysctl.{conf,d/*}
echo 'net.ipv6.conf.all.disable_ipv6 = 0' >/etc/sysctl.d/ipv6.conf
sysctl -w net.ipv6.conf.all.disable_ipv6=0)
}&
# 优先使用 IPv4 /IPv6 网络
{ stack_priority; }&
# 根据系统选择需要安装的依赖, 安装一些必要的网络工具包
info "\n $(text 60) \n"
case "$SYSTEM" in
Alpine )
${PACKAGE_INSTALL[int]} openrc
;;
Arch )
${PACKAGE_INSTALL[int]} openresolv
esac
wait
info "\n $(text 9) \n"
# 如有所有 endpoint 都不能连通的情况,脚本中止
[ ! -s /tmp/warp-go-endpoint ] && error " $(text 114) "
# warp-go 配置修改,其中用到的 162.159.192.1 和 2606:4700:d0::a29f:c001 均是 engage.cloudflareclient.com 的 IP
MTU=$(cat /tmp/warp-go-mtu) && rm -f /tmp/warp-go-mtu
MODIFY014="/Endpoint6/d; /PreUp/d; /::\/0/d; s#.*AllowedIPs.*#AllowedIPs = 0.0.0.0/0#g; s#.*PostUp.*#PostUp = ip -6 rule add from $LAN6 lookup main#g; s#.*PostDown.*#PostDown = ip -6 rule delete from $LAN6 lookup main\nPostUp = ip -4 rule add from 172.17.0.0\/24 lookup main\nPostDown = ip -4 rule delete from 172.17.0.0\/24 lookup main\n\#PostUp = /opt/warp-go/NonGlobalUp.sh\n\#PostDown = /opt/warp-go/NonGlobalDown.sh#g; s#\(MTU.*\)1280#\1$MTU#g"
MODIFY016="/Endpoint6/d; /PreUp/d; /::\/0/d; s#.*AllowedIPs.*#AllowedIPs = ::/0#g; s#.*PostUp.*#PostUp = ip -6 rule add from $LAN6 lookup main#g; s#.*PostDown.*#PostDown = ip -6 rule delete from $LAN6 lookup main\nPostUp = ip -4 rule add from 172.17.0.0\/24 lookup main\nPostDown = ip -4 rule delete from 172.17.0.0\/24 lookup main\n\#PostUp = /opt/warp-go/NonGlobalUp.sh\n\#PostDown = /opt/warp-go/NonGlobalDown.sh#g; s#\(MTU.*\)1280#\1$MTU#g"
MODIFY01D="/Endpoint6/d; /PreUp/d; /::\/0/d; s#.*AllowedIPs.*#AllowedIPs = 0.0.0.0/0,::/0#g; s#.*PostUp.*#PostUp = ip -6 rule add from $LAN6 lookup main#g; s#.*PostDown.*#PostDown = ip -6 rule delete from $LAN6 lookup main\nPostUp = ip -4 rule add from 172.17.0.0\/24 lookup main\nPostDown = ip -4 rule delete from 172.17.0.0\/24 lookup main\n\#PostUp = /opt/warp-go/NonGlobalUp.sh\n\#PostDown = /opt/warp-go/NonGlobalDown.sh#g; s#\(MTU.*\)1280#\1$MTU#g"
MODIFY104="/Endpoint6/d; /PreUp/d; /::\/0/d; s#.*AllowedIPs.*#AllowedIPs = 0.0.0.0/0#g; s#.*PostUp.*#PostUp = ip -4 rule add from $LAN4 lookup main#g; s#.*PostDown.*#PostDown = ip -4 rule delete from $LAN4 lookup main\nPostUp = ip -4 rule add from 172.17.0.0\/24 lookup main\nPostDown = ip -4 rule delete from 172.17.0.0\/24 lookup main\n\#PostUp = /opt/warp-go/NonGlobalUp.sh\n\#PostDown = /opt/warp-go/NonGlobalDown.sh#g; s#\(MTU.*\)1280#\1$MTU#g"
MODIFY106="/Endpoint6/d; /PreUp/d; /::\/0/d; s#.*AllowedIPs.*#AllowedIPs = ::/0#g; s#.*PostUp.*#PostUp = ip -4 rule add from $LAN4 lookup main#g; s#.*PostDown.*#PostDown = ip -4 rule delete from $LAN4 lookup main\nPostUp = ip -4 rule add from 172.17.0.0\/24 lookup main\nPostDown = ip -4 rule delete from 172.17.0.0\/24 lookup main\n\#PostUp = /opt/warp-go/NonGlobalUp.sh\n\#PostDown = /opt/warp-go/NonGlobalDown.sh#g; s#\(MTU.*\)1280#\1$MTU#g"
MODIFY10D="/Endpoint6/d; /PreUp/d; /::\/0/d; s#.*AllowedIPs.*#AllowedIPs = 0.0.0.0/0,::/0#g; s#.*PostUp.*#PostUp = ip -4 rule add from $LAN4 lookup main#g; s#.*PostDown.*#PostDown = ip -4 rule delete from $LAN4 lookup main\nPostUp = ip -4 rule add from 172.17.0.0\/24 lookup main\nPostDown = ip -4 rule delete from 172.17.0.0\/24 lookup main\n\#PostUp = /opt/warp-go/NonGlobalUp.sh\n\#PostDown = /opt/warp-go/NonGlobalDown.sh#g; s#\(MTU.*\)1280#\1$MTU#g"
MODIFY114="/Endpoint6/d; /PreUp/d; /::\/0/d; s#.*AllowedIPs.*#AllowedIPs = 0.0.0.0/0#g; s#.*PostUp.*#PostUp = ip -4 rule add from $LAN4 lookup main\nPostUp = ip -6 rule add from $LAN6 lookup main#g; s#.*PostDown.*#PostDown = ip -4 rule delete from $LAN4 lookup main\nPostDown = ip -6 rule delete from $LAN6 lookup main\nPostUp = ip -4 rule add from 172.17.0.0\/24 lookup main\nPostDown = ip -4 rule delete from 172.17.0.0\/24 lookup main\n\#PostUp = /opt/warp-go/NonGlobalUp.sh\n\#PostDown = /opt/warp-go/NonGlobalDown.sh#g; s#\(MTU.*\)1280#\1$MTU#g"
MODIFY116="/Endpoint6/d; /PreUp/d; /::\/0/d; s#.*AllowedIPs.*#AllowedIPs = ::/0#g; s#.*PostUp.*#PostUp = ip -4 rule add from $LAN4 lookup main\nPostUp = ip -6 rule add from $LAN6 lookup main#g; s#.*PostDown.*#PostDown = ip -4 rule delete from $LAN4 lookup main\nPostDown = ip -6 rule delete from $LAN6 lookup main\nPostUp = ip -4 rule add from 172.17.0.0\/24 lookup main\nPostDown = ip -4 rule delete from 172.17.0.0\/24 lookup main\n\#PostUp = /opt/warp-go/NonGlobalUp.sh\n\#PostDown = /opt/warp-go/NonGlobalDown.sh#g; s#\(MTU.*\)1280#\1$MTU#g"
MODIFY11D="/Endpoint6/d; /PreUp/d; /::\/0/d; s#.*AllowedIPs.*#AllowedIPs = 0.0.0.0/0,::/0#g; s#.*PostUp.*#PostUp = ip -4 rule add from $LAN4 lookup main\nPostUp = ip -6 rule add from $LAN6 lookup main#g; s#.*PostDown.*#PostDown = ip -4 rule delete from $LAN4 lookup main\nPostDown = ip -6 rule delete from $LAN6 lookup main\nPostUp = ip -4 rule add from 172.17.0.0\/24 lookup main\nPostDown = ip -4 rule delete from 172.17.0.0\/24 lookup main\n\#PostUp = /opt/warp-go/NonGlobalUp.sh\n\#PostDown = /opt/warp-go/NonGlobalDown.sh#g; s#\(MTU.*\)1280#\1$MTU#g"
MODIFY11N4="/Endpoint6/d; /PreUp/d; /::\/0/d; s#.*AllowedIPs.*#AllowedIPs = 0.0.0.0/0#g; s#.*PostUp.*#PostUp = ip -4 rule add from $LAN4 lookup main\nPostUp = ip -6 rule add from $LAN6 lookup main#g; s#.*PostDown.*#PostDown = ip -4 rule delete from $LAN4 lookup main\nPostDown = ip -6 rule delete from $LAN6 lookup main\nPostUp = ip -4 rule add from 172.17.0.0\/24 lookup main\nPostDown = ip -4 rule delete from 172.17.0.0\/24 lookup main\n\#PostUp = /opt/warp-go/NonGlobalUp.sh\n\#PostDown = /opt/warp-go/NonGlobalDown.sh#g; s#\(MTU.*\)1280#\1$MTU#g"
MODIFY11N6="/Endpoint6/d; /PreUp/d; /::\/0/d; s#.*AllowedIPs.*#AllowedIPs = ::/0#g; s#.*PostUp.*#PostUp = ip -4 rule add from $LAN4 lookup main\nPostUp = ip -6 rule add from $LAN6 lookup main#g; s#.*PostDown.*#PostDown = ip -4 rule delete from $LAN4 lookup main\nPostDown = ip -6 rule delete from $LAN6 lookup main\nPostUp = ip -4 rule add from 172.17.0.0\/24 lookup main\nPostDown = ip -4 rule delete from 172.17.0.0\/24 lookup main\n\#PostUp = /opt/warp-go/NonGlobalUp.sh\n\#PostDown = /opt/warp-go/NonGlobalDown.sh#g; s#\(MTU.*\)1280#\1$MTU#g"
MODIFY11ND="/Endpoint6/d; /PreUp/d; /::\/0/d; s#.*AllowedIPs.*#AllowedIPs = 0.0.0.0/0,::/0#g; s#.*PostUp.*#PostUp = ip -4 rule add from $LAN4 lookup main\nPostUp = ip -6 rule add from $LAN6 lookup main#g; s#.*PostDown.*#PostDown = ip -4 rule delete from $LAN4 lookup main\nPostDown = ip -6 rule delete from $LAN6 lookup main\nPostUp = ip -4 rule add from 172.17.0.0\/24 lookup main\nPostDown = ip -4 rule delete from 172.17.0.0\/24 lookup main\n\#PostUp = /opt/warp-go/NonGlobalUp.sh\n\#PostDown = /opt/warp-go/NonGlobalDown.sh#g; s#\(MTU.*\)1280#\1$MTU#g"
sed -i "$(eval echo "\$MODIFY$CONF")" /opt/warp-go/warp.conf
# 如为 WARP IPv4 非全局,修改配置文件,在路由表插入规则
[ "$OPTION" = n ] && STATUS=3 && global_switch
# 创建 warp-go systemd 进程守护(Alpine 系统除外)
if echo "$SYSTEM" | grep -qvE "Alpine|OpenWrt"; then
cat > /lib/systemd/system/warp-go.service << EOF
[Unit]
Description=warp-go service
After=network.target
Documentation=https://github.com/fscarmen/warp-sh
Documentation=https://gitlab.com/ProjectWARP/warp-go
[Service]
RestartSec=2s
WorkingDirectory=/opt/warp-go/
ExecStart=/opt/warp-go/warp-go --config=/opt/warp-go/warp.conf
Environment="LOG_LEVEL=verbose"
RemainAfterExit=yes
Restart=always
[Install]
WantedBy=multi-user.target
EOF
fi
# 运行 warp-go
net
# 设置开机启动
${SYSTEMCTL_ENABLE[int]} >/dev/null 2>&1
# 创建软链接快捷方式,再次运行可以用 warp-go 指令,设置默认语言
mv $0 /opt/warp-go/warp-go.sh
chmod +x /opt/warp-go/warp-go.sh
ln -sf /opt/warp-go/warp-go.sh /usr/bin/warp-go
echo "$L" > /opt/warp-go/language
# 结果提示,脚本运行时间,次数统计,IPv4 / IPv6 优先级别
[ "$(curl -ksm8 -A Mozilla https://ifconfig.co/json | grep 'ip=' | cut -d= -f2)" = "$WAN6" ] && PRIO=6 || PRIO=4
end=$(date +%s)
ACCOUNT_TYPE=$(grep "Type" /opt/warp-go/warp.conf | cut -d= -f2 | sed "s# ##g")
[ "$ACCOUNT_TYPE" = 'plus' ] && check_quota
result_priority
# 获取运行次数
# statistics_of_run-times get
echo -e "\n==============================================================\n"
info " IPv4: $WAN4 $COUNTRY4 $ASNORG4 "
info " IPv6: $WAN6 $COUNTRY6 $ASNORG6 "
info " $(text_eval 62) "
[ "$ACCOUNT_TYPE" = 'plus' ] && info " $(text 83): $(cat /opt/warp-go/Device_Name)\t $(text 26): $QUOTA "
[ "$ACCOUNT_TYPE" = 'team' ] && info " $(text 83): $(cat /opt/warp-go/Device_Name)\t $(text 26): $(text 103) "
info " $PRIORITY_NOW "
echo -e "\n==============================================================\n"
hint " $(text 95)\n " && help
[ "$TRACE4$TRACE6" = offoff ] && warning " $(text 63) "
exit
}
# 查 WARP+ 余额流量接口
check_quota() {
if [ -s /opt/warp-go/warp.conf ]; then
ACCESS_TOKEN=$(grep 'Token' /opt/warp-go/warp.conf | cut -d= -f2 | sed 's# ##g')
DEVICE_ID=$(grep -m1 'Device' /opt/warp-go/warp.conf | cut -d= -f2 | sed 's# ##g')
API=$(curl -s "https://api.cloudflareclient.com/v0a884/reg/$DEVICE_ID" -H "User-Agent: okhttp/3.12.1" -H "Authorization: Bearer $ACCESS_TOKEN")
QUOTA=$(sed 's/.*quota":\([^,]\+\).*/\1/g' <<< $API)
fi
# 部分系统没有依赖 bc,所以两个小数不能用 $(echo "scale=2; $QUOTA/1000000000000000" | bc),改为从右往左数字符数的方法
if [[ "$QUOTA" != 0 && "$QUOTA" =~ ^[0-9]+$ && "$QUOTA" -ge 1000000000 ]]; then
CONVERSION=("1000000000000000000" "1000000000000000" "1000000000000" "1000000000")
UNIT=("EB" "PB" "TB" "GB")
for o in ${!CONVERSION[*]}; do
[[ "$QUOTA" -ge "${CONVERSION[o]}" ]] && break
done
QUOTA_INTEGER=$(( $QUOTA / ${CONVERSION[o]} ))
QUOTA_DECIMALS=${QUOTA:0-$(( ${#CONVERSION[o]} - 1 )):2}
QUOTA="$QUOTA_INTEGER.$QUOTA_DECIMALS ${UNIT[o]}"
fi
}
# 判断当前 WARP 网络接口及 Client 的运行状态,并对应的给菜单和动作赋值
menu_setting() {
if [ "$STATUS" = 0 ]; then
MENU_OPTION[1]="$(text_eval 64)"
MENU_OPTION[2]="$(text_eval 65)"
MENU_OPTION[3]="$(text_eval 66)"
MENU_OPTION[4]="$(text_eval 67)"
MENU_OPTION[5]="$(text_eval 68)"
MENU_OPTION[6]="$(text_eval 69)"
MENU_OPTION[7]="$(text_eval 70)"
MENU_OPTION[8]="$(text_eval 71)"
ACTION[1]() { CONF=${CONF1[n]}; PRIORITY=1; install; }
ACTION[2]() { CONF=${CONF1[n]}; PRIORITY=2; install; }
ACTION[3]() { CONF=${CONF2[n]}; PRIORITY=1; install; }
ACTION[4]() { CONF=${CONF2[n]}; PRIORITY=2; install; }
ACTION[5]() { CONF=${CONF3[n]}; PRIORITY=1; install; }
ACTION[6]() { CONF=${CONF3[n]}; PRIORITY=2; install; }
ACTION[7]() { CONF=${CONF3[n]}; PRIORITY=1; OPTION=n; install; }
ACTION[8]() { CONF=${CONF3[n]}; PRIORITY=2; OPTION=n; install; }
else
[ "$NON_GLOBAL" = 1 ] || GLOBAL_AFTER="$(text 24)"
[ "$STATUS" = 2 ] && ON_OFF="$(text 72)" || ON_OFF="$(text 73)"
MENU_OPTION[1]="$(text_eval 74)"
MENU_OPTION[2]="$(text_eval 75)"
MENU_OPTION[3]="$(text_eval 76)"
MENU_OPTION[4]="$ON_OFF"
MENU_OPTION[5]="$(text_eval 77)"
MENU_OPTION[6]="$(text 78)"
MENU_OPTION[7]="$(text 79)"
MENU_OPTION[8]="$(text 80)"
ACTION[1]() { stack_switch; }
ACTION[2]() { stack_switch; }
ACTION[3]() { global_switch; }
ACTION[4]() { OPTION=o; onoff; }
ACTION[5]() { update; }
ACTION[6]() { change_ip; }
ACTION[7]() { export_file; }
ACTION[8]() { uninstall; }
fi
MENU_OPTION[0]="$(text 81)"
MENU_OPTION[9]="$(text 82) (warp-go v)"
ACTION[0]() { rm -f /tmp/warp-go*; exit; }
ACTION[9]() { ver; }
[ -s /opt/warp-go/warp.conf ] && TYPE=$(grep "Type" /opt/warp-go/warp.conf | cut -d= -f2 | sed "s# ##g")
[ "$TYPE" = 'plus' ] && check_quota && PLUSINFO="$(text 83): $(cat /opt/warp-go/Device_Name)\t $(text 26): $QUOTA"
[ "$TYPE" = 'team' ] && PLUSINFO="$(text 83): $(cat /opt/warp-go/Device_Name)\t $(text 26): $(text 103)"
}
# 显示菜单
menu() {
clear
hint " $(text 3) "
echo -e "======================================================================================================================\n"
info " $(text 84): $VERSION\n $(text 85): $(text 1)\n $(text 86):\n\t $(text 87): $SYS\n\t $(text 88): $(uname -r)\n\t $(text 89): $ARCHITECTURE\n\t $(text 90): $VIRT "
info "\t IPv4: $WAN4 $COUNTRY4 $ASNORG4 "
info "\t IPv6: $WAN6 $COUNTRY6 $ASNORG6 "
if [ "$STATUS" = 2 ]; then
info "\t $(text_eval 91) "
grep -q '#AllowedIPs' /opt/warp-go/warp.conf && GLOBAL_TYPE="$(text 24)"
info "\t $(text_eval 92) "
else
info "\t $(text 93) "
fi
[ -n "$PLUSINFO" ] && info "\t $PLUSINFO "
echo -e "\n======================================================================================================================\n"
for ((d=1; d<=${#MENU_OPTION[*]}; d++)); do [ "$d" = "${#MENU_OPTION[*]}" ] && d=0 && hint " $d. ${MENU_OPTION[d]} " && break || hint " $d. ${MENU_OPTION[d]} "; done
reading "\n $(text 4) " CHOOSE
# 输入必须是数字且少于等于最大可选项
if [[ "$CHOOSE" =~ ^[0-9]+$ ]] && (( $CHOOSE >= 0 && $CHOOSE < ${#MENU_OPTION[*]} )); then
ACTION[$CHOOSE]
else
warning " $(text 34) [0-$((${#MENU_OPTION[*]}-1))] " && sleep 1 && menu
fi
}
# 传参选项 OPTION1=为 IPv4 或者 IPv6 补全另一栈WARP; 2=安装双栈 WARP; u=卸载 WARP
[ "$1" != '[option]' ] && OPTION=$(tr 'A-Z' 'a-z' <<< "$1")
# 参数选项 URL 或 License 或转换 WARP 单双栈
if [ "$2" != '[lisence]' ]; then
if [[ "$2" =~ ^[A-Z0-9a-z]{8}-[A-Z0-9a-z]{8}-[A-Z0-9a-z]{8}$ ]]; then
LICENSE_TYPE='2' && LICENSE="$2"
elif [[ "${#2}" -ge "$TOKEN_LENGTH" ]]; then LICENSE_TYPE='3' && TOKEN="$2"
elif [[ "$2" =~ ^[A-Za-z]{2}$ ]]; then EXPECT="$2"
elif [[ "$1" = s && "$2" = [46Dd] ]]; then PRIORITY_SWITCH=$(tr 'A-Z' 'a-z' <<< "$2")
fi
fi
# 自定义 WARP+ 设备名
NAME="$3"
# 主程序运行 1/3
check_cdn
# statistics_of_run-times update warp-go.sh 2>/dev/null
select_language
check_operating_system
check_arch
[[ "$OPTION" != "u" ]] && check_dependencies
check_install
# 设置部分后缀 1/3
case "$OPTION" in
h )
help; exit 0
;;
i )
change_ip; exit 0
;;
e )
export_file; exit 0
;;
s )
stack_priority; result_priority; exit 0
esac
# 主程序运行 2/3
check_root_virt $SYSTEM
# 设置部分后缀 2/3
case "$OPTION" in
u )
uninstall; exit 0
;;
v )
ver; exit 0
;;
o )
onoff; exit 0
;;
g )
global_switch; exit 0
esac
# 主程序运行 3/3
check_system_info
check_global
check_stack
menu_setting
# 设置部分后缀 3/3
case "$OPTION" in
[46dn] )
if [[ $STATUS != 0 ]]; then
SWITCHCHOOSE="$(tr 'a-z' 'A-Z' <<< "$OPTION")"
stack_switch
else
case "$OPTION" in
4 ) CONF=${CONF1[n]} ;;
6 ) CONF=${CONF2[n]} ;;
d|n ) CONF=${CONF3[n]} ;;
esac
install
fi
;;
a )
update
;;
* ) menu
esac