Compare commits

...

41 Commits

Author SHA1 Message Date
hotyue
fe2d38a6e5 feat(core): 🚀 升级 Agent 安装脚本,注入错峰时间戳与每日调度 Cron 2026-04-13 09:47:00 +00:00
hotyue
ca0c13c11a feat(core): 🔄 升级 updater 引入每日词库同步与指纹库 30 天错峰调度 2026-04-13 09:46:58 +00:00
hotyue
f29f21f274 Merge branch 'main' into legacy 2026-04-13 03:35:28 +00:00
hotyue
6b9563b858 fix(deploy): 🐛 [v3.2.3] 修复防火墙提示盲区,支持根据 IPv4/IPv6 动态下发 iptables/ip6tables 放行指令 2026-04-13 03:32:41 +00:00
hotyue
d5f1850dbf fix(legacy): 🐛 降级 Python socketserver 语法,修复 Debian 9 (Python 3.5) 无法启动 Webhook 的 Bug 2026-04-13 03:17:15 +00:00
hotyue
50da8352d4 chore(legacy): 🔄 跨分支融合主干 v3.2.2,并修正 REPO_RAW_URL 强制指向 legacy 分支 2026-04-13 02:39:45 +00:00
hotyue
a55d362be6 Merge branch 'main' into legacy 2026-04-13 02:33:22 +00:00
hotyue
2c7491449c docs: 📝 [v3.2.2] 更新 README,补充平滑热更新与多级容灾特性说明 2026-04-13 01:52:56 +00:00
hotyue
f698bc4b92 feat(deploy): 🚀 [v3.2.2] 引入全系平滑热更新引擎,支持 Agent 与 Master 状态机嗅探及无损覆盖安装 2026-04-13 01:43:03 +00:00
hotyue
c18a10dbd5 feat(core): 🚀 [v3.2.2] 重构 tg_report 多级容灾探针,植入底层协议自适应,并按共识原则清洗 AS 号 2026-04-13 00:51:49 +00:00
hotyue
69e7803c40 fix(core): 🚀 [v3.2.1] 植入双栈协议自适应逻辑修复 curl 绑定冲突,扩容 Trust 模块状态码容错区间 2026-04-12 13:51:11 +00:00
hotyue
b2a5afe562 feat(core): 🚀 升级 v3.2.2 高精度容错探针 (处理断网误判),并优化 .com 免签节点的战报显示 2026-04-12 13:21:16 +00:00
hotyue
45fda5f498 docs: 📝 更新 README.md,补充 v3.2.x 核心极客特性与存量节点升级指引 2026-04-12 12:19:35 +00:00
hotyue
1459d5efec docs: 📝 更新 README.md,补充 v3.2.x 核心极客特性与存量节点升级指引 2026-04-12 12:18:40 +00:00
hotyue
b7375e5e7d docs: 📝 更新 README.md,补充 v3.2.x 核心极客特性与存量节点升级指引 2026-04-12 12:16:28 +00:00
hotyue
4a7f88a0da docs: 📝 更新 README.md,补充 v3.2.x 核心极客特性与存量节点升级指引 2026-04-12 12:11:03 +00:00
hotyue
a0ec759dd7 fix(core): 🚑 紧急修复 v3.2.1 注入 --interface 参数强制锚定底层物理出口 IP 2026-04-12 06:57:03 +00:00
hotyue
fe1e4c0e6f feat(core): 🚀 升级 v3.1.5 引入哈希锚定法,实现边缘节点设备资产持久化 2026-04-12 06:01:33 +00:00
github-actions[bot]
62770ba50c chore(data): 🤖 自动机兵:刷新 4000 条绝对坐标指纹库 2026-04-12 05:52:11 +00:00
hotyue
11bdece9e2 feat: 部署 v3.1.5 自动化指纹兵工厂 2026-04-12 05:46:02 +00:00
hotyue
2c5df879c0 chore(data): 扩充并升级 UA 设备指纹池,提升网络拟真度与风控防御等级 2026-04-12 05:17:30 +00:00
hotyue
862de2e49e feat(core/runner): 引入 TTY 交互式终端探测,人工调试时自动跳过防并发休眠 (Jitter) 2026-04-12 04:37:05 +00:00
hotyue
26e3fd435d chore(core/uninstall): 升级无痕焦土卸载策略,引入 pkill 语法并彻底抹除 /tmp 系统缓存碎屑 2026-04-12 04:36:52 +00:00
hotyue
e279fc6e38 fix(core/updater): 重构 OTA 静默更新逻辑,引入 find 拓扑自适应拉取,并根除私库死链 2026-04-12 04:36:41 +00:00
hotyue
acff304a35 fix(core/daemon): 剥离全局统筹路由,修复单模块节点无法响应 /trigger_run 一键维护的逻辑死角 2026-04-12 04:18:16 +00:00
hotyue
9fe39fc274 fix(core/trust): 适配多级大区拓扑 (find 穿透抓取),修复私库直链死角,并恢复终端双向日志输出 2026-04-12 04:18:09 +00:00
hotyue
9eccace3bc fix(core/google): 修复多区域判定逻辑,兼容 Google 欧洲区无跳转新规,并解除香港节点误判 2026-04-12 03:44:10 +00:00
hotyue
cf410fce61 docs: 增加 Legacy 战区引导说明,收编 Debian 9 老旧节点 2026-04-11 13:56:28 +00:00
hotyue
0ffb173bb5 docs: 增加 Legacy 战区引导说明,收编 Debian 9 老旧节点 2026-04-11 13:55:08 +00:00
hotyue
044c7a9937 chore(legacy): 降级 Python 语法并抢修 Debian 9 APT 源,以兼容传家宝老旧系统 2026-04-11 13:51:20 +00:00
hotyue
edd6425fe5 docs: 修正内容 2026-04-11 13:42:57 +00:00
hotyue
16b6c3f20a feat(core/daemon): 适配 V3.1.3 协议,IP变动或重连时自动上报大区坐标 2026-04-11 11:51:24 +00:00
hotyue
e3750f4565 feat(core/install): 适配 V3.1.3 协议,新节点注册暗号自动注入国家大区 (REGION_CODE) 2026-04-11 11:51:19 +00:00
hotyue
d3face9621 feat(master): V3.1.3 面板重构,新增大区多级折叠目录与双列排版,平滑升级 DB 协议 2026-04-11 11:51:14 +00:00
hotyue
aeeb9e1a43 docs: 完善 README 文档,新增玻璃房透明遥测、丝滑交互特性说明及完整拓扑架构 2026-04-11 11:27:40 +00:00
hotyue
09c6bab04a fix(master): 收敛 offset 记录文件至私有目录,修复 /tmp 侧信道劫持隐患 2026-04-11 11:24:13 +00:00
hotyue
2e73aa8833 feat(data): 新增美西核心节点 - 加州圣何塞 (San Jose) LBS 与专属配置 2026-04-11 11:21:57 +00:00
hotyue
03d88f4856 feat(master): 优化 TG 交互体验,哨兵节点注册成功后自动呼出最新活跃节点面板 2026-04-11 08:15:16 +00:00
hotyue
3cab2b2379 chore(core): 将官方公共机器人安全网关 API 从测试环境切换为正式生产环境 2026-04-11 07:55:30 +00:00
hotyue
87faeae8f7 fix(core): 修复首次安装时 agent_daemon 抢跑导致的重复注册推送问题 2026-04-11 07:33:44 +00:00
hotyue
19f0bb7c36 docs: 全面刷新 README 文档,点亮 v3.1.2 透明装机量徽章并重写核心极客特性 2026-04-11 07:12:48 +00:00
17 changed files with 5010 additions and 375 deletions

33
.github/workflows/ua_factory.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Automated Massive UA Factory
on:
schedule:
# 每个月 1 号凌晨 00:00 执行
- cron: '0 0 1 * *'
workflow_dispatch: # 允许手动点击运行
jobs:
build-and-deploy:
runs-on: ubuntu-latest
permissions:
contents: write # 必须赋予写入权限,否则无法更新仓库文件
steps:
- name: 📥 Checkout Repository
uses: actions/checkout@v4
- name: ⚙️ Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: 🚀 Run UA Factory Generator
run: python scripts/ua_generator.py
- name: 📤 Commit and Push to Main
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add data/user_agents.txt
# 只有在文件内容确实发生变化时才执行提交
git diff --quiet && git diff --staged --quiet || (git commit -m "chore(data): 🤖 自动机兵:刷新 4000 条绝对坐标指纹库" && git push)

View File

@@ -1,20 +1,42 @@
# 🛡️ IP-Sentinel (分布式 IP 哨兵集群)
![Agent Installs](https://img.shields.io/endpoint?url=https://ip-sentinel-count.samanthaestime296.workers.dev/stats/agent)
![Master Commands](https://img.shields.io/endpoint?url=https://ip-sentinel-count.samanthaestime296.workers.dev/stats/master)
![License](https://img.shields.io/github/license/hotyue/IP-Sentinel)
> **一个极度轻量、零感知、支持中枢遥控的 VPS IP 自动化养护与区域纠偏引擎。**
📢 官方战术交流频道: 🛰️ [IP-Sentinel Matrix](https://t.me/IP_Sentinel_Matrix)
专为解决 VPS IPv4 被 Google 等数据库错误定位到中国大陆/香港俗称“送中”等问题而生。IP-Sentinel 已从单机脚本全面跃升为 **Master-Agent 分布式架构**。它像影子一样潜伏在全球各地的服务器后台,通过高度拟真的真实用户行为为你默默积累 IP 权重,并允许你通过 Telegram 随时随地对整个舰队进行毫秒级“点名”与“遥控”。
专为解决 VPS IP 被 Google 等数据库错误定位到中国大陆/香港俗称“送中”等问题而生。IP-Sentinel 已从单机脚本全面跃升为 **Master-Agent 分布式架构**。它像影子一样潜伏在全球各地的服务器后台,通过高度拟真的真实用户行为为你默默积累 IP 权重,并允许你通过 Telegram 随时随地对整个舰队进行毫秒级“点名”与“遥控”。
## ✨ 核心极客特性
* 🗺️ **全球拓扑矩阵 (Global Nexus)****v3.0.0 新特性**。引入动态 `map.json` 索引中心与多级地理架构 (国家-省州-城市)。安装脚本全自动解析云端地图,支持无限扩展全球节点,真正实现“按图索骥”。
* ☁️ **云端中枢 (Public Master)**:引入官方公共机器人 [@OmniBeacon_bot](https://t.me/OmniBeacon_bot),新手无需部署 Master 司令部,一键回车即可接入全球养护矩阵,极大降低入伍门槛。
* 🧠 **分布式中枢 (Master-Agent)**:对于硬核极客,支持私有化部署。一台 Master 主控集成 SQLite 数据库,统管无数台 Agent 边缘节点,确保数据绝对私有。
* 🎮 **TG 战术面板 (Command Center)**:无需记忆繁琐命令,原生 Inline Keyboard 按钮驱动。支持一键调出节点列表、一键下发伪装指令、一键索要精准战报、**毫秒级抓取实时运行日志**。
* 🛡️ **NAT 穿透与安全网关 (NAT-Friendly)**:边缘节点采用 Python3 极轻量 Webhook 监听,**完全自定义通信端口**,完美支持受限 NAT 小鸡。独创 TG 转发授权机制,杜绝野生节点恶意接入
* 👻 **高仿真人类行为 (Human-Like)**:摒弃死板的 Ping/Curl引入单次会话指纹锁定、10 米级 GPS 坐标微抖动、以及 60~150 秒的真实阅读停顿拉伸,完美避开 AI 封控。
* 📡 **OTA 静默进化 (Smart Updates)**:系统每周日凌晨自动从云端拉取最新的“热搜词汇”和“真实设备指纹池”,确保养护行为与时俱进、永不过时
- 🗺️ **全球拓扑矩阵 (Global Nexus)**v3.1 跨洲际跃升。守护版图现已横跨亚、欧、美三大洲(美、日、英、德、法、新、港)。为每个国家注入极其硬核的“原生本地化”搜索词库与本土高权重站点(如政府、权威媒体、高铁网),真正实现“拟真融入”。
- 👻 **设备资产持久化 (Hash-Seeded Persona)**v3.2 核心换代。彻底摒弃传统的“随机抽取指纹”,引入基于节点物理 IP 的哈希锚定引擎。利用不可变哈希种子,为您的每台 VPS 在千万级指纹库中永久锁定 3 个绝对专属设备(如固定表现为 1台 Mac、1台 iPhone、1台 PC 交替上网)。完美构建高权重真实家庭内网画像,根除“僵尸网络”同质化特征!
- 🏭 **自动化指纹兵工厂 (Automated UA Factory)**:依托 GitHub Actions CI/CD 流水线,每月 1 日无人值守全自动生成 4000+ 带绝对物理分区的真实终端设备数据。配合边缘节点的守护进程静默拉取,实现千万级指纹资产的“自动驾驶”级演进
- 🖧 **底层路由死锁 (Hard-Bind Routing)**v3.2.1 热修复升级。底层探测引擎强力接管 curl 核心参数 (--interface),强制将发出的每一滴伪装流量死死绑定在您设定的物理网卡或隧道 IP 上,彻底杜绝双栈或多网卡环境下的流量溢出漏洞
- 🎯 **多级容灾与高精度探针 (High-Precision Probe)**v3.2.2 底层重构。重写战报模块与底层协议自适应逻辑,植入多级 ISP 容灾探针链路,并按“底层数据共识原则”智能清洗冗余 AS 号。确保在纯 V6、隧道或弱网环境下数据获取依然 100% 精准畅通。
- 🔄 **平滑热更新装甲 (Smooth Upgrade Engine)**v3.2.2 体验进化。全系植入状态机嗅探逻辑。无论是 Master 司令部还是 Agent 边缘节点再次执行部署脚本时将自动识别并继承历史配置、SQLite 数据库与锚定 IP一键回车即可瞬间完成无损换代告别繁琐的重复配置。
- ☁️ **云端中枢 (Public Master)**:引入官方公共机器人 @OmniBeacon_bot,新手无需部署 Master 司令部,部署 Agent 时一键回车即可调用官方加密网关30 秒极速入伍!
- 🧠 **分布式中枢 (Master-Agent)**:对于硬核极客,支持私有化部署。一台 Master 主控集成 SQLite 数据库,统管无数台 Agent 边缘节点,确保数据绝对私有。
- 🔒 **叹息之墙 (Zero-Trust HMAC)**:全面废弃明文 Token底层通讯引入 时间戳 + HMAC-SHA256 军用级动态签名。指令有效期仅 60 秒(阅后即焚),彻底免疫中间人抓包、重放攻击与端口爆破。
- 🛡️ **工业级并发与自净引擎**:底层 Webhook 采用多线程模型彻底免疫慢速耗尽攻击;独创“智能清道夫”逻辑,覆盖安装/升级时自动绞杀僵尸进程与冗余定时任务,绝对纯净,告别玄学冲突。
- 🎮 **TG 战术面板 (Command Center)**:无需记忆繁琐命令,全 Inline Keyboard 交互。支持一键下发伪装指令、一键索要精准战报、毫秒级抓取边缘节点实时运行日志。
- 👁️‍🗨️ **玻璃房透明遥测 (Glasshouse Telemetry)**:引入基于 Cloudflare Workers 的全透明计数中枢,首页动态徽章实时展示全球真实装机与调用量。绝对零隐私收集,仅作原子累加,底层网关源码全开源,接受全网极客审计。
-**丝滑战术交互 (Seamless UI)**:司令部交互面板像素级打磨。新节点发送暗号入伍成功后,司令部将无缝零延迟自动呼出最新的活跃节点阵列面板,彻底免除重复输入命令的繁琐,掌控感拉满。
## 📂 项目架构 (Monorepo)
@@ -22,18 +44,21 @@
```text
📦 IP-Sentinel
┣ 📂 .github/workflows/ # 🏭 自动化兵工厂:每月定时触发指纹生成的 CI/CD 流水线
┣ 📂 master/ # 🧠 司令部SQLite 存储、TG 监听与 Webhook 调度中心
┣ 📂 core/ # 🛡️ 边缘哨兵Webhook 被动监听、高拟真养护引擎
📂 data/ # 🗂️ 全球数据规则库 (v3.0 全新拓扑)
┣ 📜 map.json # 🌐 全球区域索引大脑 (Master Index)
📂 regions/ # 🧊 冷数据:按 [国家/省州/城市] 深度细分的 LBS 锚点
┣ 📂 keywords/ # 🔥 热数据:按国家归类的动态搜索词库 (OTA 自动更新)
┗ 📜 user_agents.txt # 🔥 热数据:全局真实设备指纹池
┣ 📂 core/ # 🛡️ 边缘哨兵Webhook 被动监听、哈希锚定执行引擎
📂 scripts/ # 🐍 兵工厂引擎:基于 Python 的多物理分区 UA 生成器
┣ 📂 data/ # 🗂️ 全球数据规则库 (动态拓扑)
📜 map.json # 🌐 全球区域索引大脑 (Master Index)
┣ 📂 regions/ # 🧊 冷数据:按 [国家/省州/城市] 深度细分的 LBS 锚点
┣ 📂 keywords/ # 🔥 热数据:按国家归类的动态搜索词库 (OTA 自动更新)
┃ ┗ 📜 user_agents.txt # 🔥 热数据:由兵工厂每月锻造的绝对坐标专属设备库
┗ 📂 telemetry/ # 👁️‍🗨️ 玻璃房计划Cloudflare Workers 透明计数器网关源码
```
## 🚀 极速部署 (Quick Start)
v3.0.0 提供了两种接入模式,请根据您的战术需求选择:
v3.2.x 提供了两种接入模式,请根据您的战术需求选择:
### 🔹 模式 A官方公共模式 (最简、推荐)
**适合不想折腾、只想快速养护 IP 的新兵。**
@@ -53,7 +78,6 @@ bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/core/i
```Bash
bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/master/install_master.sh)
```
2. **部署 Agent**:在需要养护的机器上执行 Agent 脚本,输入您自建机器人的 Token 以及与 Master 一致的配置。
```Bash
@@ -62,15 +86,31 @@ bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/core/i
```
3. **激活节点**:同上,将暗号转发给您自己的机器人即可。
### ⚠️ 平滑升级指引 (Upgrade to v3.2.2)
得益于 **v3.2.2 全新引入的平滑热更新引擎 (Smooth Upgrade Engine)**,系统升级现已变得极其优雅与安全。
无需卸载旧版本,无论您是要升级 Agent 边缘节点还是 Master 控制中枢,只需在您的终端中**再次运行上方对应的官方部署指令**。
安装雷达会自动嗅探您的历史部署状态(包括您的 Token、区域设定、SQLite 数据库及物理网卡锚点)。当询问是否平滑升级时,您只需**一路回车 (默认选 y)**,脚本将在短短 3 秒内瞬间完成核心装甲的无损换脑手术,您的所有战术资产将得到 100% 保留!
🗑️ 一键无痕卸载
如果你需要清理某个边缘节点,只需重新运行 core/install.sh 并选择 [3],或直接在节点终端执行:
如果你需要清理某个边缘节点,只需重新运行 `core/install.sh` 并选择 **[2]**,或直接在节点终端执行:
```Bash
bash /opt/ip_sentinel/core/uninstall.sh
```
### 🧓 传家宝老旧系统专用通道 (Debian 9)
如果你的小鸡系统版本过低(如 Debian 9由于官方 APT 源已关闭且 Python 版本过旧,无法使用主线版本,请使用 **Legacy 兼容分支** 部署。
*(注意:该分支仅作基础维护,不享受新功能迭代,请尽可能升级你的系统)*
```bash
bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/legacy/core/install.sh)
```
📡 战术联络 (Community)
如果你在使用过程中遇到任何疑难杂症,或者想围观大佬们的养护战报,欢迎加入我们的基地:
- Telegram 频道: [@IP_Sentinel_Matrix](https://t.me/IP_Sentinel_Matrix)

View File

@@ -41,7 +41,8 @@ if [ -n "$AGENT_IP" ]; then
# 只有当这是第一次运行,或者公网 IP 发生变动时,才发送 Telegram 申请
if [ "$AGENT_IP" != "$LAST_IP" ]; then
REG_MSG="👋 **[边缘节点接入申请]**%0A节点: \`${NODE_NAME}\`%0A地址: \`${AGENT_IP}:${AGENT_PORT}\`%0A%0A⚠ **安全验证**: 为防止非法节点接入,请长按复制下方代码,并**发送给我**以完成最终授权录入:%0A%0A\`#REGISTER#|${NODE_NAME}|${AGENT_IP}|${AGENT_PORT}\`"
# V3.1.3 协议升级: 在底部暗号中精准嵌入 ${REGION_CODE} 大区标识
REG_MSG="👋 **[边缘节点接入申请]**%0A大区: \`${REGION_CODE}\`%0A节点: \`${NODE_NAME}\`%0A地址: \`${AGENT_IP}:${AGENT_PORT}\`%0A%0A⚠ **安全验证**: 为防止非法节点接入,请长按复制下方代码,并**发送给我**以完成最终授权录入:%0A%0A\`#REGISTER#|${REGION_CODE}|${NODE_NAME}|${AGENT_IP}|${AGENT_PORT}\`"
curl -s -m 5 -X POST "${TG_API_URL}" \
-d "chat_id=${CHAT_ID}" \
@@ -114,7 +115,7 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
return
# 校验 3HMAC 数据完整性与身份合法性校验
msg = f"{req_path}:{req_t}".encode('utf-8')
msg = "{}:{}".format(req_path, req_t).encode('utf-8')
expected_sign = hmac.new(AUTH_TOKEN.encode('utf-8'), msg, hashlib.sha256).hexdigest()
# 使用 compare_digest 防御时序攻击
@@ -126,8 +127,20 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
# ================== 路由分发 (恢复为安全的精确匹配) ==================
# 路由 0: 全局统筹调度 (处理 /trigger_run 一键全节点维护)
if req_path == '/trigger_run':
if os.path.exists('/opt/ip_sentinel/core/runner.sh'):
self.send_response(200)
self.send_header("Content-type", "text/plain")
self.end_headers()
self.wfile.write(b"Action Accepted: runner\n")
subprocess.Popen(['bash', '/opt/ip_sentinel/core/runner.sh'])
else:
self.send_response(404)
self.end_headers()
# 路由 1: Google 区域纠偏
if req_path == '/trigger_google' or req_path == '/trigger_run':
elif req_path == '/trigger_google':
if os.path.exists('/opt/ip_sentinel/core/mod_google.sh'):
self.send_response(200)
self.send_header("Content-type", "text/plain")
@@ -188,7 +201,7 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
log_data = html.escape("".join(lines[-15:]))
node_name = subprocess.check_output(['hostname']).decode('utf-8').strip()[:15]
text_msg = f"📄 <b>[{node_name}] 实时运行日志:</b>\n<pre><code>{log_data}</code></pre>"
text_msg = "📄 <b>[{}] 实时运行日志:</b>\n<pre><code>{}</code></pre>".format(node_name, log_data)
data = urllib.parse.urlencode({
'chat_id': config.get('CHAT_ID', ''),
@@ -204,7 +217,7 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
urllib.request.urlopen(req, timeout=10)
except Exception as e:
print(f"Log transmission failed: {e}")
print("Log transmission failed: {}".format(e))
else:
self.send_response(404)
@@ -221,8 +234,8 @@ class ThreadedDualStackServer(socketserver.ThreadingMixIn, socketserver.TCPServe
try:
bind_addr = "::" if socket.has_ipv6 else ""
with ThreadedDualStackServer((bind_addr, PORT), AgentHandler) as httpd:
httpd.serve_forever()
httpd = ThreadedDualStackServer((bind_addr, PORT), AgentHandler)
httpd.serve_forever()
except Exception as e:
sys.exit(1)
# ====================================================================================

View File

@@ -1,12 +1,12 @@
#!/bin/bash
# ==========================================================
# 脚本名称: install.sh (IP-Sentinel 分布式边缘节点部署脚本 v3.0.3 - Global Nexus)
# 核心功能: 区域选择、模块按需开启、官方机器人一键配置
# 脚本名称: install.sh (IP-Sentinel 分布式边缘节点部署脚本 v3.3.0 - OTA 活体引擎)
# 核心功能: 区域选择、模块按需开启、官方机器人一键配置、平滑热更新、分频错峰调度
# ==========================================================
# 你的 GitHub 仓库 Raw 数据直链前缀
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/legacy"
# 临时改为私库地址用于测试
# REPO_RAW_URL="https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main"
INSTALL_DIR="/opt/ip_sentinel"
@@ -18,6 +18,17 @@ echo "========================================================"
# 1. 依赖检查与安装 (新增 python3 用于轻量级 Webhook 服务)
echo -e "\n[1/7] 正在安装必要环境依赖 (curl, jq, cron, procps, python3)..."
# ================== [Legacy: Debian 9 APT 源抢修补丁] ==================
if [ -f /etc/debian_version ] && grep -q -E "^9\." /etc/debian_version; then
echo -e "\033[33m⚠ 检测到 Debian 9 (Stretch),正在抢修已停用的 APT 档案馆源...\033[0m"
echo "deb http://archive.debian.org/debian stretch main" > /etc/apt/sources.list
echo "deb http://archive.debian.org/debian-security stretch/updates main" >> /etc/apt/sources.list
sed -i '/stretch-updates/d' /etc/apt/sources.list
echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99no-check-valid-until
fi
# =======================================================================
if [ -f /etc/debian_version ]; then
apt-get update -y >/dev/null 2>&1
apt-get install -y curl jq cron procps python3 >/dev/null 2>&1
@@ -51,8 +62,31 @@ if [ "$ACTION_CHOICE" == "2" ]; then
exit 0
fi
# ================== [v3.1.1 新增: 安装前环境纯净度清理 (严格保留日志)] ==================
echo -e "\n⏳ 正在清理旧版守护进程与冗余任务 (保留历史日志)..."
# ================== [v3.2.2 新增: 平滑升级模式嗅探] ==================
UPGRADE_MODE="false"
KEEP_LOGS="true"
if [ "$ACTION_CHOICE" == "1" ] && [ -f "$CONFIG_FILE" ]; then
echo -e "\n\033[33m💡 哨兵雷达提示:检测到本机已部署过 IP-Sentinel。\033[0m"
read -p "👉 是否按原配置直接进行平滑升级?(y/n, 默认y): " UPGRADE_CHOICE
if [[ -z "$UPGRADE_CHOICE" || "$UPGRADE_CHOICE" =~ ^[Yy]$ ]]; then
UPGRADE_MODE="true"
read -p "👉 是否保留历史运行日志?(y/n, 默认y): " LOG_CHOICE
if [[ "$LOG_CHOICE" =~ ^[Nn]$ ]]; then
KEEP_LOGS="false"
fi
# 将原配置读入环境变量,为后续跳过配置步骤提供燃料
source "$CONFIG_FILE"
echo -e "\033[32m✅ 已激活 [平滑升级模式],即将跳过基础配置,直接更新核心装甲...\033[0m"
else
echo -e "\033[33m🔄 您选择了重新配置,旧的哨兵数据将被彻底抹除。\033[0m"
fi
fi
# ====================================================================
# ================== [v3.1.1/v3.2.2 优化: 安装前环境纯净度清理] ==================
echo -e "\n⏳ 正在清理旧版守护进程与冗余任务..."
# 1. 强制超度可能存活的 Webhook 及各类看门狗进程,释放端口
pkill -9 -f "webhook.py" >/dev/null 2>&1 || true
pkill -9 -f "agent_daemon.sh" >/dev/null 2>&1 || true
@@ -65,236 +99,253 @@ if crontab -l >/dev/null 2>&1; then
rm -f /tmp/cron_clean
fi
# 3. 抹除旧版核心代码与配置文件,杜绝代码冲突 (精准避开 logs 目录)
if [ -d "$INSTALL_DIR" ]; then
rm -rf "${INSTALL_DIR}/core" "${INSTALL_DIR}/data" "${INSTALL_DIR}/config.conf" "${INSTALL_DIR}/.last_ip" 2>/dev/null
# 3. 抹除旧版核心代码,杜绝代码冲突 (根据模式分流)
if [ "$UPGRADE_MODE" == "true" ]; then
# 升级模式:仅销毁核心引擎,严格保留 config 与 data
rm -rf "${INSTALL_DIR}/core" 2>/dev/null
if [ "$KEEP_LOGS" == "false" ]; then
rm -rf "${INSTALL_DIR}/logs" 2>/dev/null
echo -e "🗑️ 历史日志已按指令清空。"
else
echo -e "📦 历史配置与战地日志已妥善保留。"
fi
else
# 全新安装模式:焦土政策,彻底抹除
if [ -d "$INSTALL_DIR" ]; then
rm -rf "${INSTALL_DIR}/core" "${INSTALL_DIR}/data" "${INSTALL_DIR}/config.conf" "${INSTALL_DIR}/.last_ip" 2>/dev/null
fi
fi
echo -e "\033[32m✅ 环境清理完毕,幽灵进程已肃清!\033[0m"
# ========================================================================================
# 📍 动态一级菜单:国家选择
echo -e "\n\033[36m📍 【第一级】请选择目标国家/地区:\033[0m"
jq -r '.countries[] | "\(.id)|\(.name)|\(.keyword_file)"' /tmp/map.json > /tmp/countries.txt
i=1; COUNTRY_MAP=(); KEYWORD_MAP=()
while IFS="|" read -r c_id c_name k_file; do
echo " $i) $c_name"
COUNTRY_MAP[$i]="$c_id"
KEYWORD_MAP[$i]="$k_file"
((i++))
done < /tmp/countries.txt
# ==========================================================
# 🛑 如果是全新部署,才执行以下所有交互逻辑;否则直接跳过
# ==========================================================
if [ "$UPGRADE_MODE" == "false" ]; then
read -p "请输入选择 [1-$((i-1))] (默认1): " C_SEL
C_SEL=${C_SEL:-1}
COUNTRY_ID="${COUNTRY_MAP[$C_SEL]}"
KEYWORD_FILE="${KEYWORD_MAP[$C_SEL]}"
REGION_CODE="$COUNTRY_ID" # 兼容旧版的 config.conf
# 📍 动态二级菜单:省/州选择
echo -e "\n\033[36m📍 【第二级】正在检索 [$COUNTRY_ID] 的行政区数据...\033[0m"
jq -r ".countries[] | select(.id==\"$COUNTRY_ID\") | .states[] | \"\(.id)|\(.name)\"" /tmp/map.json > /tmp/states.txt
STATE_COUNT=$(wc -l < /tmp/states.txt)
if [ "$STATE_COUNT" -eq 1 ]; then
IFS="|" read -r STATE_ID STATE_NAME < /tmp/states.txt
echo -e "\033[32m💡 该国家下仅有单一配置 [$STATE_NAME],已自动跃迁。\033[0m"
else
i=1; STATE_MAP=()
while IFS="|" read -r s_id s_name; do
echo " $i) $s_name"
STATE_MAP[$i]="$s_id"
((i++))
done < /tmp/states.txt
read -p "请输入选择 [1-$((i-1))] (默认1): " S_SEL
S_SEL=${S_SEL:-1}
STATE_ID="${STATE_MAP[$S_SEL]}"
fi
# 📍 动态三级菜单:城市选择
echo -e "\n\033[36m📍 【第三级】请锁定具体城市节点:\033[0m"
jq -r ".countries[] | select(.id==\"$COUNTRY_ID\") | .states[] | select(.id==\"$STATE_ID\") | .cities[] | \"\(.id)|\(.name)\"" /tmp/map.json > /tmp/cities.txt
CITY_COUNT=$(wc -l < /tmp/cities.txt)
if [ "$CITY_COUNT" -eq 1 ]; then
IFS="|" read -r CITY_ID CITY_NAME < /tmp/cities.txt
echo -e "\033[32m💡 该区域下仅有单一城市 [$CITY_NAME],已自动锁定。\033[0m"
else
i=1; CITY_MAP=()
while IFS="|" read -r c_id c_name; do
# 📍 动态一级菜单:国家选择
echo -e "\n\033[36m📍 【第一级】请选择目标国家/地区:\033[0m"
jq -r '.countries[] | "\(.id)|\(.name)|\(.keyword_file)"' /tmp/map.json > /tmp/countries.txt
i=1; COUNTRY_MAP=(); KEYWORD_MAP=()
while IFS="|" read -r c_id c_name k_file; do
echo " $i) $c_name"
CITY_MAP[$i]="$c_id"
COUNTRY_MAP[$i]="$c_id"
KEYWORD_MAP[$i]="$k_file"
((i++))
done < /tmp/cities.txt
read -p "请输入选择 [1-$((i-1))] (默认1): " CI_SEL
CI_SEL=${CI_SEL:-1}
CITY_ID="${CITY_MAP[$CI_SEL]}"
fi
done < /tmp/countries.txt
# 清理临时文件
rm -f /tmp/map.json /tmp/countries.txt /tmp/states.txt /tmp/cities.txt
read -p "请输入选择 [1-$((i-1))] (默认1): " C_SEL
C_SEL=${C_SEL:-1}
COUNTRY_ID="${COUNTRY_MAP[$C_SEL]}"
KEYWORD_FILE="${KEYWORD_MAP[$C_SEL]}"
REGION_CODE="$COUNTRY_ID" # 兼容旧版的 config.conf
# 本地工作目录初始化 (支持 v3.0 的深度层级)
mkdir -p "${INSTALL_DIR}/core"
mkdir -p "${INSTALL_DIR}/data/keywords"
mkdir -p "${INSTALL_DIR}/data/regions/${COUNTRY_ID}/${STATE_ID}"
mkdir -p "${INSTALL_DIR}/logs"
# 📍 动态二级菜单:省/州选择
echo -e "\n\033[36m📍 【第二级】正在检索 [$COUNTRY_ID] 的行政区数据...\033[0m"
jq -r ".countries[] | select(.id==\"$COUNTRY_ID\") | .states[] | \"\(.id)|\(.name)\"" /tmp/map.json > /tmp/states.txt
STATE_COUNT=$(wc -l < /tmp/states.txt)
# 3. 功能模块前置开关 (按需加载)
echo -e "\n[3/7] 请选择需要开启的养护模块 (按需开启,节省资源):"
echo " 1) 📍 仅开启 [Google 区域纠偏] (默认,适合流媒体解锁机位漂移)"
echo " 2) 🛡️ 仅开启 [IP 信用净化] (适合高风险机房 IP 降低 Scamalytics 分数)"
echo " 3) 🔥 双管齐下 (同时开启以上两项)"
read -p "请输入选择 [1-3] (默认1): " MODULE_CHOICE
ENABLE_GOOGLE="true"
ENABLE_TRUST="false"
case ${MODULE_CHOICE:-1} in
2) ENABLE_GOOGLE="false"; ENABLE_TRUST="true" ;;
3) ENABLE_GOOGLE="true"; ENABLE_TRUST="true" ;;
*) ENABLE_GOOGLE="true"; ENABLE_TRUST="false" ;;
esac
# 4. 接入 Master 中枢配置
echo -e "\n[4/7] 是否接入 Master 司令部?(需要配置与主控相同的 TG 机器人) (y/n)"
read -p "请输入选择 [y/n] (默认n): " TG_CHOICE
TG_TOKEN=""
CHAT_ID=""
AGENT_PORT="9527"
if [[ "$TG_CHOICE" =~ ^[Yy]$ ]]; then
echo -e "\n\033[33m💡 提示:您可以选择使用自己的机器人,或者直接回车使用官方公共机器人。\033[0m"
echo -e "\033[33m⚠ 注意:若使用官方机器人,请务必先在 TG 中关注 @OmniBeacon_bot 并发送 /start\033[0m"
read -p "请输入您的 Telegram Bot Token (回车使用官方默认): " USER_TOKEN
if [ -z "$USER_TOKEN" ]; then
TG_TOKEN="OFFICIAL_GATEWAY_MODE"
TG_API_URL="https://omni-gateway.yuezhongjun.workers.dev"
echo -e "\033[32m✅ 已自动连接官方安全网关 (@OmniBeacon_bot)。\033[0m"
echo -e "\033[33m👉 请确保您已关注官方机器人并发送过 /start否则将无法接收消息。\033[0m"
if [ "$STATE_COUNT" -eq 1 ]; then
IFS="|" read -r STATE_ID STATE_NAME < /tmp/states.txt
echo -e "\033[32m💡 该国家下仅有单一配置 [$STATE_NAME],已自动跃迁。\033[0m"
else
TG_TOKEN="$USER_TOKEN"
TG_API_URL="https://api.telegram.org/bot${TG_TOKEN}/sendMessage"
echo -e "\033[32m✅ 已记录您的私有机器人 Token。\033[0m"
i=1; STATE_MAP=()
while IFS="|" read -r s_id s_name; do
echo " $i) $s_name"
STATE_MAP[$i]="$s_id"
((i++))
done < /tmp/states.txt
read -p "请输入选择 [1-$((i-1))] (默认1): " S_SEL
S_SEL=${S_SEL:-1}
STATE_ID="${STATE_MAP[$S_SEL]}"
fi
echo -e "\033[33m💡 提示:如果您不知道自己的 Chat ID可以关注 @userinfobot 获取。\033[0m"
read -p "请输入你的 Chat ID (与主控一致): " CHAT_ID
# ================== [v3.0.3 变更: 智能随机高位端口生成系统] ==================
echo -e "\n\033[36m[4.2/7] 正在构建 Webhook 安全通信隧道...\033[0m"
echo -n "🎲 正在探测可用随机端口..."
while true; do
RANDOM_PORT=$((RANDOM % 55536 + 10000))
# 同时兼容 ss (新) 和 netstat (旧) 检查端口占用
if ! (ss -tuln 2>/dev/null | grep -q ":$RANDOM_PORT " || netstat -tuln 2>/dev/null | grep -q ":$RANDOM_PORT "); then
break
fi
echo -n "."
done
echo -e " 完成!"
echo -e "💡 系统为您生成的推荐随机高位端口为: \033[32m$RANDOM_PORT\033[0m"
echo -e "\033[33m(该端口已通过本地占用校验,可直接使用)\033[0m"
while true; do
read -p "请输入 Webhook 监听端口 (回车采用推荐, 或手动输入): " INPUT_PORT
# 📍 动态三级菜单:城市选择
echo -e "\n\033[36m📍 【第三级】请锁定具体城市节点:\033[0m"
jq -r ".countries[] | select(.id==\"$COUNTRY_ID\") | .states[] | select(.id==\"$STATE_ID\") | .cities[] | \"\(.id)|\(.name)\"" /tmp/map.json > /tmp/cities.txt
CITY_COUNT=$(wc -l < /tmp/cities.txt)
if [ "$CITY_COUNT" -eq 1 ]; then
IFS="|" read -r CITY_ID CITY_NAME < /tmp/cities.txt
echo -e "\033[32m💡 该区域下仅有单一城市 [$CITY_NAME],已自动锁定。\033[0m"
else
i=1; CITY_MAP=()
while IFS="|" read -r c_id c_name; do
echo " $i) $c_name"
CITY_MAP[$i]="$c_id"
((i++))
done < /tmp/cities.txt
read -p "请输入选择 [1-$((i-1))] (默认1): " CI_SEL
CI_SEL=${CI_SEL:-1}
CITY_ID="${CITY_MAP[$CI_SEL]}"
fi
# 清理临时文件
rm -f /tmp/map.json /tmp/countries.txt /tmp/states.txt /tmp/cities.txt
# 本地工作目录初始化 (支持 v3.0 的深度层级)
mkdir -p "${INSTALL_DIR}/core"
mkdir -p "${INSTALL_DIR}/data/keywords"
mkdir -p "${INSTALL_DIR}/data/regions/${COUNTRY_ID}/${STATE_ID}"
mkdir -p "${INSTALL_DIR}/logs"
# 3. 功能模块前置开关 (按需加载)
echo -e "\n[3/7] 请选择需要开启的养护模块 (按需开启,节省资源):"
echo " 1) 📍 仅开启 [Google 区域纠偏] (默认,适合流媒体解锁机位漂移)"
echo " 2) 🛡️ 仅开启 [IP 信用净化] (适合高风险机房 IP 降低 Scamalytics 分数)"
echo " 3) 🔥 双管齐下 (同时开启以上两项)"
read -p "请输入选择 [1-3] (默认1): " MODULE_CHOICE
ENABLE_GOOGLE="true"
ENABLE_TRUST="false"
case ${MODULE_CHOICE:-1} in
2) ENABLE_GOOGLE="false"; ENABLE_TRUST="true" ;;
3) ENABLE_GOOGLE="true"; ENABLE_TRUST="true" ;;
*) ENABLE_GOOGLE="true"; ENABLE_TRUST="false" ;;
esac
# 4. 接入 Master 中枢配置
echo -e "\n[4/7] 是否接入 Master 司令部?(需要配置与主控相同的 TG 机器人) (y/n)"
read -p "请输入选择 [y/n] (默认n): " TG_CHOICE
TG_TOKEN=""
CHAT_ID=""
AGENT_PORT="9527"
if [[ "$TG_CHOICE" =~ ^[Yy]$ ]]; then
echo -e "\n\033[33m💡 提示:您可以选择使用自己的机器人,或者直接回车使用官方公共机器人。\033[0m"
echo -e "\033[33m⚠ 注意:若使用官方机器人,请务必先在 TG 中关注 @OmniBeacon_bot 并发送 /start\033[0m"
if [ -z "$INPUT_PORT" ]; then
AGENT_PORT="$RANDOM_PORT"
break
read -p "请输入您的 Telegram Bot Token (回车使用官方默认): " USER_TOKEN
if [ -z "$USER_TOKEN" ]; then
TG_TOKEN="OFFICIAL_GATEWAY_MODE"
TG_API_URL="https://omni-gateway.samanthaestime296.workers.dev"
echo -e "\033[32m✅ 已自动连接官方安全网关 (@OmniBeacon_bot)。\033[0m"
echo -e "\033[33m👉 请确保您已关注官方机器人并发送过 /start否则将无法接收消息。\033[0m"
else
# 校验手动输入的合法性与可用性
if [[ "$INPUT_PORT" =~ ^[0-9]+$ ]] && [ "$INPUT_PORT" -ge 1 ] && [ "$INPUT_PORT" -le 65535 ]; then
if (ss -tuln 2>/dev/null | grep -q ":$INPUT_PORT " || netstat -tuln 2>/dev/null | grep -q ":$INPUT_PORT "); then
echo -e "\033[31m❌ 端口 $INPUT_PORT 已被占用,请重新输入或使用推荐端口。\033[0m"
else
AGENT_PORT="$INPUT_PORT"
break
fi
else
echo -e "\033[31m❌ 输入非法!端口范围应为 1-65535。\033[0m"
TG_TOKEN="$USER_TOKEN"
TG_API_URL="https://api.telegram.org/bot${TG_TOKEN}/sendMessage"
echo -e "\033[32m✅ 已记录您的私有机器人 Token。\033[0m"
fi
echo -e "\033[33m💡 提示:如果您不知道自己的 Chat ID可以关注 @userinfobot 获取。\033[0m"
read -p "请输入你的 Chat ID (与主控一致): " CHAT_ID
# ================== [v3.0.3 变更: 智能随机高位端口生成系统] ==================
echo -e "\n\033[36m[4.2/7] 正在构建 Webhook 安全通信隧道...\033[0m"
echo -n "🎲 正在探测可用随机端口..."
while true; do
RANDOM_PORT=$((RANDOM % 55536 + 10000))
# 同时兼容 ss (新) 和 netstat (旧) 检查端口占用
if ! (ss -tuln 2>/dev/null | grep -q ":$RANDOM_PORT " || netstat -tuln 2>/dev/null | grep -q ":$RANDOM_PORT "); then
break
fi
fi
done
echo -e "✅ 已锁定 Webhook 通讯端口: \033[32m$AGENT_PORT\033[0m"
# ====================================================================
fi
echo -n "."
done
echo -e " 完成!"
echo -e "💡 系统为您生成的推荐随机高位端口为: \033[32m$RANDOM_PORT\033[0m"
echo -e "\033[33m(该端口已通过本地占用校验,可直接使用)\033[0m"
while true; do
read -p "请输入 Webhook 监听端口 (回车采用推荐, 或手动输入): " INPUT_PORT
if [ -z "$INPUT_PORT" ]; then
AGENT_PORT="$RANDOM_PORT"
break
else
# 校验手动输入的合法性与可用性
if [[ "$INPUT_PORT" =~ ^[0-9]+$ ]] && [ "$INPUT_PORT" -ge 1 ] && [ "$INPUT_PORT" -le 65535 ]; then
if (ss -tuln 2>/dev/null | grep -q ":$INPUT_PORT " || netstat -tuln 2>/dev/null | grep -q ":$INPUT_PORT "); then
echo -e "\033[31m❌ 端口 $INPUT_PORT 已被占用,请重新输入或使用推荐端口。\033[0m"
else
AGENT_PORT="$INPUT_PORT"
break
fi
else
echo -e "\033[31m❌ 输入非法!端口范围应为 1-65535。\033[0m"
fi
fi
done
echo -e "✅ 已锁定 Webhook 通讯端口: \033[32m$AGENT_PORT\033[0m"
# ====================================================================
fi
# ================== [v3.0.1新增修改 1: 冗余网络栈探测与锚点锁定] ==================
echo -e "\n\033[36m[4.5/7] 正在探测本机网络栈与可用出口 (多节点雷达扫描中)...\033[0m"
# ================== [v3.0.1新增修改 1: 冗余网络栈探测与锚点锁定] ==================
echo -e "\n\033[36m[4.5/7] 正在探测本机网络栈与可用出口 (多节点雷达扫描中)...\033[0m"
# 引入容灾机制:依次尝试三个不同的 API拿到有效的 IP 格式就停止
DETECT_V4=$( (curl -4 -s -m 3 api.ip.sb/ip || curl -4 -s -m 3 ifconfig.me || curl -4 -s -m 3 ipv4.icanhazip.com) 2>/dev/null | grep -E "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" | head -n 1 | tr -d '[:space:]')
DETECT_V6=$( (curl -6 -s -m 3 api.ip.sb/ip || curl -6 -s -m 3 ifconfig.me || curl -6 -s -m 3 ipv6.icanhazip.com) 2>/dev/null | grep -E "^[0-9a-fA-F:]+.*:" | head -n 1 | tr -d '[:space:]')
# 引入容灾机制:依次尝试三个不同的 API拿到有效的 IP 格式就停止
DETECT_V4=$( (curl -4 -s -m 3 api.ip.sb/ip || curl -4 -s -m 3 ifconfig.me || curl -4 -s -m 3 ipv4.icanhazip.com) 2>/dev/null | grep -E "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" | head -n 1 | tr -d '[:space:]')
DETECT_V6=$( (curl -6 -s -m 3 api.ip.sb/ip || curl -6 -s -m 3 ifconfig.me || curl -6 -s -m 3 ipv6.icanhazip.com) 2>/dev/null | grep -E "^[0-9a-fA-F:]+.*:" | head -n 1 | tr -d '[:space:]')
# 构建动态选项数组
IP_OPTIONS=()
IP_PROTO=()
# 构建动态选项数组
IP_OPTIONS=()
IP_PROTO=()
[[ -n "$DETECT_V4" ]] && { IP_OPTIONS+=("$DETECT_V4"); IP_PROTO+=("4"); }
[[ -n "$DETECT_V6" ]] && { IP_OPTIONS+=("$DETECT_V6"); IP_PROTO+=("6"); }
[[ -n "$DETECT_V4" ]] && { IP_OPTIONS+=("$DETECT_V4"); IP_PROTO+=("4"); }
[[ -n "$DETECT_V6" ]] && { IP_OPTIONS+=("$DETECT_V6"); IP_PROTO+=("6"); }
if [ ${#IP_OPTIONS[@]} -eq 0 ]; then
echo -e "\033[33m⚠ 雷达受阻:未能自动探测到公网 IP请手动指定。\033[0m"
read -p "请输入您要绑定的公网 IP (v4 或 v6): " PUBLIC_IP
[[ "$PUBLIC_IP" == *":"* ]] && IP_PREF="6" || IP_PREF="4"
else
echo "📍 发现可用出口 IP请选择要注册与养护的锚点:"
for i in "${!IP_OPTIONS[@]}"; do
num=$((i+1))
if [ "${IP_PROTO[$i]}" == "4" ]; then
echo " $num) 🌐 IPv4: ${IP_OPTIONS[$i]} (默认选项)"
else
echo " $num) 🌌 IPv6: ${IP_OPTIONS[$i]}"
fi
done
CUSTOM_OPT=$(( ${#IP_OPTIONS[@]} + 1 ))
echo " $CUSTOM_OPT) ✍️ 手动指定其他 IP (适合多 IP 站群机)"
read -p "请输入选择 (默认1): " IP_CHOICE
IP_CHOICE=${IP_CHOICE:-1}
if [ "$IP_CHOICE" -le "${#IP_OPTIONS[@]}" ] && [ "$IP_CHOICE" -gt 0 ]; then
idx=$((IP_CHOICE-1))
PUBLIC_IP="${IP_OPTIONS[$idx]}"
IP_PREF="${IP_PROTO[$idx]}"
elif [ "$IP_CHOICE" -eq "$CUSTOM_OPT" ]; then
if [ ${#IP_OPTIONS[@]} -eq 0 ]; then
echo -e "\033[33m⚠ 雷达受阻:未能自动探测到公网 IP请手动指定。\033[0m"
read -p "请输入您要绑定的公网 IP (v4 或 v6): " PUBLIC_IP
[[ "$PUBLIC_IP" == *":"* ]] && IP_PREF="6" || IP_PREF="4"
else
# 兜底:乱输就默认选第一个
PUBLIC_IP="${IP_OPTIONS[0]}"
IP_PREF="${IP_PROTO[0]}"
echo "📍 发现可用出口 IP请选择要注册与养护的锚点:"
for i in "${!IP_OPTIONS[@]}"; do
num=$((i+1))
if [ "${IP_PROTO[$i]}" == "4" ]; then
echo " $num) 🌐 IPv4: ${IP_OPTIONS[$i]} (默认选项)"
else
echo " $num) 🌌 IPv6: ${IP_OPTIONS[$i]}"
fi
done
CUSTOM_OPT=$(( ${#IP_OPTIONS[@]} + 1 ))
echo " $CUSTOM_OPT) ✍️ 手动指定其他 IP (适合多 IP 站群机)"
read -p "请输入选择 (默认1): " IP_CHOICE
IP_CHOICE=${IP_CHOICE:-1}
if [ "$IP_CHOICE" -le "${#IP_OPTIONS[@]}" ] && [ "$IP_CHOICE" -gt 0 ]; then
idx=$((IP_CHOICE-1))
PUBLIC_IP="${IP_OPTIONS[$idx]}"
IP_PREF="${IP_PROTO[$idx]}"
elif [ "$IP_CHOICE" -eq "$CUSTOM_OPT" ]; then
read -p "请输入您要绑定的公网 IP (v4 或 v6): " PUBLIC_IP
[[ "$PUBLIC_IP" == *":"* ]] && IP_PREF="6" || IP_PREF="4"
else
# 兜底:乱输就默认选第一个
PUBLIC_IP="${IP_OPTIONS[0]}"
IP_PREF="${IP_PROTO[0]}"
fi
fi
fi
# 终极修复:为 IPv6 自动穿上防护装甲(方括号),解决 Master 拼接 URL 报错问题
if [[ "$PUBLIC_IP" == *":"* ]] && [[ "$PUBLIC_IP" != *"["* ]]; then
BIND_IP="[${PUBLIC_IP}]"
else
BIND_IP="$PUBLIC_IP"
fi
echo -e "\033[32m✅ 哨兵锚点已永久锁定至: $BIND_IP\033[0m"
# ========================================================================
# 终极修复:为 IPv6 自动穿上防护装甲(方括号),解决 Master 拼接 URL 报错问题
if [[ "$PUBLIC_IP" == *":"* ]] && [[ "$PUBLIC_IP" != *"["* ]]; then
BIND_IP="[${PUBLIC_IP}]"
else
BIND_IP="$PUBLIC_IP"
fi
echo -e "\033[32m✅ 哨兵锚点已永久锁定至: $BIND_IP\033[0m"
# ========================================================================
# 5. 远程拉取冷数据并解析固化
echo -e "\n[5/7] 正在从云端数据仓库拉取 [${CITY_NAME}] 节点的底层规则..."
REGION_JSON_FILE="${INSTALL_DIR}/data/regions/${COUNTRY_ID}/${STATE_ID}/${CITY_ID}.json"
curl -sL "${REPO_RAW_URL}/data/regions/${COUNTRY_ID}/${STATE_ID}/${CITY_ID}.json" -o "$REGION_JSON_FILE"
# 5. 远程拉取冷数据并解析固化
echo -e "\n[5/7] 正在从云端数据仓库拉取 [${CITY_NAME}] 节点的底层规则..."
REGION_JSON_FILE="${INSTALL_DIR}/data/regions/${COUNTRY_ID}/${STATE_ID}/${CITY_ID}.json"
curl -sL "${REPO_RAW_URL}/data/regions/${COUNTRY_ID}/${STATE_ID}/${CITY_ID}.json" -o "$REGION_JSON_FILE"
if [ ! -s "$REGION_JSON_FILE" ]; then
echo "❌ 拉取或解析规则失败!请检查 Forgejo 仓库是否公开或网络是否畅通。"
exit 1
fi
if [ ! -s "$REGION_JSON_FILE" ]; then
echo "❌ 拉取或解析规则失败!请检查 Forgejo 仓库是否公开或网络是否畅通。"
exit 1
fi
# 使用 jq 提取 JSON 里的核心值
REGION_NAME=$(jq -r '.region_name' "$REGION_JSON_FILE")
BASE_LAT=$(jq -r '.google_module.base_lat' "$REGION_JSON_FILE")
BASE_LON=$(jq -r '.google_module.base_lon' "$REGION_JSON_FILE")
LANG_PARAMS=$(jq -r '.google_module.lang_params' "$REGION_JSON_FILE")
VALID_URL_SUFFIX=$(jq -r '.google_module.valid_url_suffix' "$REGION_JSON_FILE")
# 使用 jq 提取 JSON 里的核心值
REGION_NAME=$(jq -r '.region_name' "$REGION_JSON_FILE")
BASE_LAT=$(jq -r '.google_module.base_lat' "$REGION_JSON_FILE")
BASE_LON=$(jq -r '.google_module.base_lon' "$REGION_JSON_FILE")
LANG_PARAMS=$(jq -r '.google_module.lang_params' "$REGION_JSON_FILE")
VALID_URL_SUFFIX=$(jq -r '.google_module.valid_url_suffix' "$REGION_JSON_FILE")
# 写入本地静态配置文件
cat > "$CONFIG_FILE" << EOF
# 写入本地静态配置文件
cat > "$CONFIG_FILE" << EOF
# IP-Sentinel 本地固化配置 (生成时间: $(date '+%Y-%m-%d %H:%M:%S'))
REGION_CODE="$REGION_CODE"
REGION_NAME="$REGION_NAME"
@@ -319,12 +370,19 @@ IP_PREF="$IP_PREF"
BIND_IP="$BIND_IP"
EOF
# ================== [v3.0.3 变更: 敏感配置文件权限收敛] ==================
chmod 600 "$CONFIG_FILE"
# ====================================================================
# ================== [v3.0.3 变更: 敏感配置文件权限收敛] ==================
chmod 600 "$CONFIG_FILE"
# ====================================================================
fi
# 🛑 拦截块结束 (全套交互配置跳过完毕)
# 6. 拉取全套组件 (按需下载,绝不浪费空间)
echo -e "\n[6/7] 正在根据模块开关部署核心引擎与热数据..."
# 确保目录在升级模式下也能被正确建立
mkdir -p "${INSTALL_DIR}/core"
mkdir -p "${INSTALL_DIR}/data/keywords"
# 基础公共组件
curl -sL "${REPO_RAW_URL}/core/runner.sh" -o "${INSTALL_DIR}/core/runner.sh"
curl -sL "${REPO_RAW_URL}/core/updater.sh" -o "${INSTALL_DIR}/core/updater.sh"
@@ -336,8 +394,13 @@ curl -sL "${REPO_RAW_URL}/data/user_agents.txt" -o "${INSTALL_DIR}/data/user_age
# 动态按需组件
if [ "$ENABLE_GOOGLE" == "true" ]; then
curl -sL "${REPO_RAW_URL}/core/mod_google.sh" -o "${INSTALL_DIR}/core/mod_google.sh"
# 根据 map.json 动态匹配词库文件进行下载
curl -sL "${REPO_RAW_URL}/data/keywords/${KEYWORD_FILE}" -o "${INSTALL_DIR}/data/keywords/${KEYWORD_FILE}"
# [v3.2.2 修复] 动态匹配词库下载逻辑
if [ "$UPGRADE_MODE" == "false" ]; then
curl -sL "${REPO_RAW_URL}/data/keywords/${KEYWORD_FILE}" -o "${INSTALL_DIR}/data/keywords/${KEYWORD_FILE}"
else
# 升级模式:利用已有的 REGION_CODE 更新通用词库
curl -sL "${REPO_RAW_URL}/data/keywords/kw_${REGION_CODE}.txt" -o "${INSTALL_DIR}/data/keywords/kw_${REGION_CODE}.txt" 2>/dev/null || true
fi
fi
if [ "$ENABLE_TRUST" == "true" ]; then
@@ -352,14 +415,22 @@ crontab -l 2>/dev/null | grep -v "ip_sentinel" > /tmp/cron_backup
# 核心养护模块: 每 30 分钟触发一次
echo "*/30 * * * * ${INSTALL_DIR}/core/runner.sh >/dev/null 2>&1" >> /tmp/cron_backup
# 养料更新模块: 每周日凌晨 3 点静默去云端更新热数据
echo "0 3 * * 0 ${INSTALL_DIR}/core/updater.sh >/dev/null 2>&1" >> /tmp/cron_backup
# 养料更新模块: (v3.3.0升级) 每天凌晨 3 点触发,由中枢自动进行分频调度
echo "0 3 * * * ${INSTALL_DIR}/core/updater.sh >/dev/null 2>&1" >> /tmp/cron_backup
# [v3.3.0 新增] 初始化 UA 指纹库更新时间戳,确立 30 天滚动周期的计算锚点
echo $(date +%s) > "${INSTALL_DIR}/core/.ua_last_update"
# 如果配置了联控,启动 Webhook 与战报任务
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
# 每天早上 8 点发送昨天的统计战报
echo "0 8 * * * ${INSTALL_DIR}/core/tg_report.sh >/dev/null 2>&1" >> /tmp/cron_backup
# [v3.0.1新增修改 3: 删除原来的 curl 取 IP直接使用我们上方锁定的 BIND_IP]
# 并提前写入 IP 缓存,彻底阻断 agent_daemon 首次启动时的重复推送
# [修复竞态]: 提前写入 IP 缓存,彻底阻断 agent_daemon 首次启动时的抢跑推送
echo "$BIND_IP" > "${INSTALL_DIR}/core/.last_ip"
# 双保险守护进程看门狗
echo "@reboot nohup bash ${INSTALL_DIR}/core/agent_daemon.sh >/dev/null 2>&1 &" >> /tmp/cron_backup
echo "* * * * * nohup bash ${INSTALL_DIR}/core/agent_daemon.sh >/dev/null 2>&1 &" >> /tmp/cron_backup
@@ -371,22 +442,30 @@ fi
crontab /tmp/cron_backup
rm -f /tmp/cron_backup
# ================== [v3.2.2 优化: 战报通知分流 (注册/升级)] ==================
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
echo -e "\n📡 正在向指挥部发送注册暗号..."
# [v3.0.1新增修改 3: 删除原来的 curl 取 IP直接使用我们上方锁定的 BIND_IP]
# 并提前写入 IP 缓存,彻底阻断 agent_daemon 首次启动时的重复推送
echo "$BIND_IP" > "${INSTALL_DIR}/core/.last_ip"
# 构造注册暗号 (使用带 [] 装甲的 BIND_IP防止 Master 端解析错误)
NODE_NAME=$(hostname | cut -c 1-15)
REG_MSG="#REGISTER#|${NODE_NAME}|${BIND_IP}|${AGENT_PORT}"
# 执行主动推送
PUSH_RESULT=$(curl -s -X POST "${TG_API_URL}" \
-d "chat_id=${CHAT_ID}" \
-d "parse_mode=Markdown" \
-d "text=✨ *IP-Sentinel 部署成功!*
if [ "$UPGRADE_MODE" == "true" ]; then
echo -e "\n📡 正在向指挥部发送升级成功战报..."
curl -s -X POST "${TG_API_URL}" \
-d "chat_id=${CHAT_ID}" \
-d "parse_mode=Markdown" \
-d "text=✨ *IP-Sentinel 引擎热更新完成!*
📍 节点:\`${NODE_NAME}\`
🌐 IP\`${BIND_IP}\`
🚀 状态v3.3.0 OTA 动态活体养护引擎已部署" >/dev/null 2>&1
echo -e "\033[32m✅ 升级成功通知已推送到您的 Telegram\033[0m"
else
echo -e "\n📡 正在向指挥部发送注册暗号..."
# 构造注册暗号 (V3.1.3 协议升级: 携带 REGION_CODE 大区标识)
REG_MSG="#REGISTER#|${REGION_CODE}|${NODE_NAME}|${BIND_IP}|${AGENT_PORT}"
# 执行主动推送
PUSH_RESULT=$(curl -s -X POST "${TG_API_URL}" \
-d "chat_id=${CHAT_ID}" \
-d "parse_mode=Markdown" \
-d "text=✨ *IP-Sentinel 部署成功!*
📍 区域:${REGION_NAME}
🌐 IP${BIND_IP}
🔌 端口:${AGENT_PORT}
@@ -394,15 +473,21 @@ if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
🔑 *请点击下方指令复制并回复给机器人:*
\`${REG_MSG}\`")
if echo "$PUSH_RESULT" | grep -q '"ok":true'; then
echo -e "\033[32m✅ 注册信息已推送到您的 Telegram请按指令完成最终激活\033[0m"
else
echo -e "\033[31m❌ 消息推送失败,请检查 Chat ID 是否正确或是否已关注机器人。\033[0m"
if echo "$PUSH_RESULT" | grep -q '"ok":true'; then
echo -e "\033[32m✅ 注册信息已推送到您的 Telegram请按指令完成最终激活\033[0m"
else
echo -e "\033[31m❌ 消息推送失败,请检查 Chat ID 是否正确或是否已关注机器人。\033[0m"
fi
fi
fi
# =========================================================================
echo "========================================================"
echo "🎉 边缘节点 (Agent) 部署流程彻底完成!"
if [ "$UPGRADE_MODE" == "true" ]; then
echo "🎉 边缘节点 (Agent) 平滑热更新已彻底完成!"
else
echo "🎉 边缘节点 (Agent) 部署流程彻底完成!"
fi
echo "📍 你的本地守护区域已锁定为: $REGION_NAME"
echo "⚙️ 哨兵现已开启 [每30分钟] 的高频高拟真养护循环。"
if [[ -n "$TG_TOKEN" ]]; then
@@ -415,7 +500,12 @@ if [[ -n "$TG_TOKEN" ]]; then
elif command -v firewall-cmd >/dev/null 2>&1 && systemctl is-active firewalld | grep -qw active; then
FW_MSG="firewall-cmd --zone=public --add-port=$AGENT_PORT/tcp --permanent && firewall-cmd --reload"
elif command -v iptables >/dev/null 2>&1; then
FW_MSG="iptables -I INPUT -p tcp --dport $AGENT_PORT -j ACCEPT"
# 智能双栈雷达:根据绑定的 IP 属性,动态下发对应的防火墙放行指令
if [[ "$BIND_IP" == *":"* ]]; then
FW_MSG="ip6tables -I INPUT -p tcp --dport $AGENT_PORT -j ACCEPT"
else
FW_MSG="iptables -I INPUT -p tcp --dport $AGENT_PORT -j ACCEPT"
fi
fi
echo -e "\033[33m⚠ 警告:请务必确保本机及云服务商安全组放行了 TCP $AGENT_PORT 端口!\033[0m"

View File

@@ -51,8 +51,29 @@ get_random_coord() {
# [v3.0.2修复] 直接读取系统已锁定的锚点 IP彻底杜绝“获取IP失败”及隧道偏移
CURRENT_IP="${BIND_IP:-Unknown}"
# 会话锁定:单次执行内使用固定的浏览器指纹
SESSION_UA=${UA_POOL[$RANDOM % ${#UA_POOL[@]}]}
# -----------------------------------------------------------
# [V3.1.5] 哈希锚定法 (Hash-Seeded Persona)
# 利用 IP 算力固定 3 个永久化专属指纹,破除僵尸网络同质化特征
# -----------------------------------------------------------
TOTAL_UA=${#UA_POOL[@]}
if [ "$TOTAL_UA" -gt 0 ]; then
# 1. 以本地锁定的公网 IP 为种子,计算固定不变的 CRC32 哈希值
SEED=$(echo -n "$CURRENT_IP" | cksum | awk '{print $1}')
# 2. 利用确定的种子和质数乘数,在全球 4000 的库中计算出本机的 3 个绝对专属坐标
IDX1=$(( SEED % TOTAL_UA ))
IDX2=$(( (SEED * 17) % TOTAL_UA ))
IDX3=$(( (SEED * 31) % TOTAL_UA ))
# 3. 将绝对坐标映射为该节点的“专属设备库”
MY_UA_POOL=("${UA_POOL[$IDX1]}" "${UA_POOL[$IDX2]}" "${UA_POOL[$IDX3]}")
# 4. 本次会话从这 3 台专属设备中随机挑选 1 台进行模拟
SESSION_UA=${MY_UA_POOL[$RANDOM % 3]}
else
# 兜底容错机制
SESSION_UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
fi
# 位置锁定:在基准点(比如东京新宿)附近 3 公里内随机生成本次上网的“固定咖啡馆”坐标
SESSION_BASE_LAT=$(get_random_coord $BASE_LAT 270)
SESSION_BASE_LON=$(get_random_coord $BASE_LON 270)
@@ -64,6 +85,25 @@ log "$MODULE_NAME" "INFO " "当前出网 IP: $CURRENT_IP"
log "$MODULE_NAME" "INFO " "设备指纹锁定: ${SESSION_UA:0:45}..."
log "$MODULE_NAME" "INFO " "虚拟驻留坐标: $SESSION_BASE_LAT, $SESSION_BASE_LON"
# -----------------------------------------------------------
# [V3.2.1 热修复] 网络锚定与协议自适应构建
# 强制 curl 绑定网卡,并自动匹配 IPv4/v6 协议,杜绝 curl 冲突报错
# -----------------------------------------------------------
CURL_BIND_OPT=""
DYNAMIC_IP_PREF="-${IP_PREF:-4}" # 默认提取用户配置
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
CURL_BIND_OPT="--interface $BIND_IP"
# 智能探测:带冒号为 V6带点号为 V4
if [[ "$BIND_IP" == *":"* ]]; then
DYNAMIC_IP_PREF="-6"
log "$MODULE_NAME" "INFO " "底层路由锁定: 绑定 IPv6 出口及协议 ($BIND_IP)"
elif [[ "$BIND_IP" == *"."* ]]; then
DYNAMIC_IP_PREF="-4"
log "$MODULE_NAME" "INFO " "底层路由锁定: 绑定 IPv4 出口及协议 ($BIND_IP)"
fi
fi
# --- [行为循环模拟] ---
for ((i=1; i<=TOTAL_ACTIONS; i++)); do
# 模拟真实移动设备拿在手里时的 GPS 信号微抖动 (范围约 10 米)
@@ -77,21 +117,22 @@ for ((i=1; i<=TOTAL_ACTIONS; i++)); do
# 随机选择一种上网行为
ACTION_TYPE=$((1 + RANDOM % 4))
# [V3.2.1 热修复] 注入 $CURL_BIND_OPT 与 $DYNAMIC_IP_PREF 协议自适应
case $ACTION_TYPE in
1) # 搜索行为
CODE=$(curl -${IP_PREF:-4} -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
"https://www.google.com/search?q=${ENCODED_KEY}&${LANG_PARAMS}")
;;
2) # 浏览本土新闻
CODE=$(curl -${IP_PREF:-4} -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 15 -s -L -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
"https://news.google.com/home?${LANG_PARAMS}")
;;
3) # 地图坐标查询
CODE=$(curl -${IP_PREF:-4} -m 15 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
"https://www.google.com/maps/search/${ENCODED_KEY}/@${ACTION_LAT},${ACTION_LON},17z?${LANG_PARAMS}")
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 15 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
"https://www.google.com/maps/search/$${ENCODED_KEY}/@${ACTION_LAT},${ACTION_LON},17z?${LANG_PARAMS}")
;;
4) # 触发移动端系统底层位置检测像素
CODE=$(curl -${IP_PREF:-4} -m 10 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 10 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
"https://connectivitycheck.gstatic.com/generate_204")
;;
esac
@@ -107,16 +148,45 @@ for ((i=1; i<=TOTAL_ACTIONS; i++)); do
fi
done
# --- [结果纠偏自检] ---
# 去掉所有语言参数,进行一次最干净的直连测试 (强制遵循锚点协议)
FINAL_URL=$(curl -${IP_PREF:-4} -m 15 -s -L -o /dev/null -w "%{url_effective}" https://www.google.com)
# --- [结果纠偏自检 (V3.2.1 高精度容错版)] ---
# [V3.2.1 热修复] 探针同样应用 $DYNAMIC_IP_PREF 协议自适应
PROBE_RESULT=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -m 15 -s -L -o /dev/null -w "%{http_code}|%{url_effective}" https://www.google.com)
if [[ "$FINAL_URL" == *"$VALID_URL_SUFFIX"* ]]; then
STATUS="✅ 目标区域达成 ($VALID_URL_SUFFIX)"
elif [[ "$FINAL_URL" == *"google.com.hk"* ]]; then
STATUS="❌ 判定为送中区 (CN/HK)"
# 分离状态码与 URL
PROBE_CODE=$(echo "$PROBE_RESULT" | cut -d'|' -f1)
FINAL_URL=$(echo "$PROBE_RESULT" | cut -d'|' -f2)
# 0. 致命拦截网络断开、DNS 解析失败或严重超时
if [ "$PROBE_CODE" == "000" ] || [ -z "$FINAL_URL" ]; then
STATUS="🚨 探针失效 (网络阻断或底层路由异常)"
else
STATUS="⚠️ 其他分站跳板 ($FINAL_URL)"
# 核心战术:精准提取最终 URL 的域名部分
ACTUAL_DOMAIN=$(echo "$FINAL_URL" | awk -F/ '{print $3}')
# [V3.2.1 优化] 使用通配符 * 剔除任意前缀 (无论是 www.google. 还是 ipv4.google.)
ACTUAL_SUFFIX=${ACTUAL_DOMAIN#*google.}
# 1. 优先验证:绝对匹配目标后缀 (彻底杜绝 com 包含于 com.hk 的陷阱)
if [ "$ACTUAL_SUFFIX" == "$VALID_URL_SUFFIX" ]; then
STATUS="✅ 目标区域达成 ($ACTUAL_SUFFIX)"
# 2. 核心拦截:精准捕捉送中特征 (com.hk)
elif [ "$ACTUAL_SUFFIX" == "com.hk" ]; then
if [ "$REGION_CODE" == "HK" ]; then
STATUS="✅ 目标区域达成 (HK 专属 com.hk)"
else
STATUS="❌ 严重漂移!判定为送中区 (实际跳往 $ACTUAL_SUFFIX)"
fi
# 3. 宽容处理:遵守 Google 无跳转新规 (严格限定必须是纯粹的 com)
# [视觉优化] 留在 .com 代表 IP 极度纯净未被区域沙盒锁定,计入成功战绩!
elif [ "$ACTUAL_SUFFIX" == "com" ]; then
STATUS="✅ 目标区域达成 (免签停留 .com 通用主站)"
# 4. 跨区漂移:所有预判之外的后缀,全部视为异常
else
STATUS="⚠️ 跨区跳板漂移 (当前实际归属: $ACTUAL_SUFFIX)"
fi
fi
log "$MODULE_NAME" "SCORE" "自检结论: $STATUS"

View File

@@ -1,14 +1,17 @@
#!/bin/bash
# ==========================================================
# 脚本名称: mod_trust.sh (IP 信用净化模块 V2.0 数据解耦版)
# 核心功能: 动态读取云端/本地 JSON 规则池,模拟访问高权重网站稀释恶意流量
# 脚本名称: mod_trust.sh (IP 信用净化模块 V3.1.4 拓扑自适应版)
# 核心功能: 动态扫描本地 LBS 冷数据,提取权威白名单,执行流量净化
# ==========================================================
INSTALL_DIR="/opt/ip_sentinel"
CONFIG_FILE="${INSTALL_DIR}/config.conf"
UA_FILE="${INSTALL_DIR}/data/user_agents.txt"
REPO_RAW_URL="https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main"
# 你的 GitHub 仓库 Raw 数据直链前缀
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
# 临时改为私库地址用于测试
# REPO_RAW_URL="https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main"
# 1. 基础环境校验
[ ! -f "$CONFIG_FILE" ] && exit 1
@@ -16,11 +19,14 @@ source "$CONFIG_FILE"
REGION=${REGION_CODE:-"US"}
LOG_FILE="${INSTALL_DIR}/logs/sentinel.log"
REGION_JSON_FILE="${INSTALL_DIR}/data/regions/${REGION}.json"
# 2. 动态获取配置 (解耦核心)
# 兼容旧节点:如果本地没有 json自动拉取最新的云端配置 (强制遵循锚点协议)
if [ ! -f "$REGION_JSON_FILE" ]; then
# 2. 动态获取配置 (V3 拓扑自适应与兜底)
# 利用 find 穿透多级子目录,自动抓取安装时落地的那份专属 json 文件
REGION_JSON_FILE=$(find "${INSTALL_DIR}/data/regions" -name "*.json" 2>/dev/null | head -n 1)
# 兼容旧节点兜底:如果本地真没找到 json回退到拉取云端通用大区配置
if [ -z "$REGION_JSON_FILE" ] || [ ! -f "$REGION_JSON_FILE" ]; then
REGION_JSON_FILE="${INSTALL_DIR}/data/regions/${REGION}.json"
mkdir -p "${INSTALL_DIR}/data/regions"
curl -${IP_PREF:-4} -sL "${REPO_RAW_URL}/data/regions/${REGION}.json" -o "$REGION_JSON_FILE"
fi
@@ -44,10 +50,34 @@ log_msg() {
}
# 4. 锁定单次会话指纹
# -----------------------------------------------------------
# [V3.1.5] 哈希锚定法 (Hash-Seeded Persona)
# 利用 IP 算力固定 3 个永久化专属指纹,破除僵尸网络同质化特征
# -----------------------------------------------------------
if [ -f "$UA_FILE" ]; then
CURRENT_UA=$(shuf -n 1 "$UA_FILE")
mapfile -t UA_POOL < <(grep -v '^$' "$UA_FILE")
TOTAL_UA=${#UA_POOL[@]}
if [ "$TOTAL_UA" -gt 0 ]; then
# 以本地锁定的公网 IP (BIND_IP) 为种子计算 CRC32 哈希值
SEED=$(echo -n "${BIND_IP:-127.0.0.1}" | cksum | awk '{print $1}')
# 利用确定的种子,在全球 4000 的库中,计算出本机的 3 个绝对专属坐标
IDX1=$(( SEED % TOTAL_UA ))
IDX2=$(( (SEED * 17) % TOTAL_UA ))
IDX3=$(( (SEED * 31) % TOTAL_UA ))
# 将专属坐标映射为专属设备库
MY_UA_POOL=("${UA_POOL[$IDX1]}" "${UA_POOL[$IDX2]}" "${UA_POOL[$IDX3]}")
# 本次会话从这 3 台专属设备中随机挑选 1 台 (模拟真实的家庭多设备环境)
CURRENT_UA=${MY_UA_POOL[$RANDOM % 3]}
else
# 兜底容错
CURRENT_UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
fi
else
CURRENT_UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
CURRENT_UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
fi
# ==========================================
@@ -57,6 +87,25 @@ log_msg "START" "========== 启动区域 IP 信用净化会话 =========="
log_msg "INFO " "已载入 [${REGION}] 区域白名单,配置库条目: ${#TRUST_URLS[@]}"
log_msg "INFO " "已锁定本地伪装指纹: $(echo $CURRENT_UA | cut -d' ' -f1-2)..."
# -----------------------------------------------------------
# [V3.2.1 热修复] 网络锚定与协议自适应构建
# 强制 curl 绑定网卡,并自动匹配 IPv4/v6 协议,杜绝 curl 冲突报错
# -----------------------------------------------------------
CURL_BIND_OPT=""
DYNAMIC_IP_PREF="-${IP_PREF:-4}" # 默认提取用户配置
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
CURL_BIND_OPT="--interface $BIND_IP"
# 智能探测:带冒号为 V6带点号为 V4
if [[ "$BIND_IP" == *":"* ]]; then
DYNAMIC_IP_PREF="-6"
log_msg "INFO " "底层路由锁定: 绑定 IPv6 出口及协议 ($BIND_IP)"
elif [[ "$BIND_IP" == *"."* ]]; then
DYNAMIC_IP_PREF="-4"
log_msg "INFO " "底层路由锁定: 绑定 IPv4 出口及协议 ($BIND_IP)"
fi
fi
STEP_COUNT=$((RANDOM % 4 + 3))
SUCCESS_INJECT=0
@@ -65,7 +114,8 @@ for ((i=1; i<=STEP_COUNT; i++)); do
TARGET_URL=${TRUST_URLS[$RANDOM % ${#TRUST_URLS[@]}]}
# [v3.0.1修复] 注入高权重流量时,强制从绑定的 IPv4 或 IPv6 隧道出网
HTTP_CODE=$(curl -${IP_PREF:-4} -A "$CURRENT_UA" \
# [V3.2.1 热修复] 注入 $CURL_BIND_OPT 与 $DYNAMIC_IP_PREF 协议自适应
HTTP_CODE=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -A "$CURRENT_UA" \
-H "Accept: text/html,application/xhtml+xml;q=0.9,image/avif,image/webp,*/*;q=0.8" \
-H "Accept-Language: en-US,en;q=0.9" \
-H "Sec-Fetch-Dest: document" \
@@ -74,7 +124,8 @@ for ((i=1; i<=STEP_COUNT; i++)); do
--compressed \
-s -o /dev/null -w "%{http_code}" -m 15 "$TARGET_URL")
if [[ "$HTTP_CODE" =~ ^(200|301|302)$ ]]; then
# 扩大 HTTP 状态码容错区间:包含所有 20x (如亚马逊的 202) 和 30x 重定向
if [[ "$HTTP_CODE" =~ ^(20[0-9]|30[1-8])$ ]]; then
log_msg "EXEC " "动作[$i/$STEP_COUNT]完成 | 状态: $HTTP_CODE | 注入: $TARGET_URL"
((SUCCESS_INJECT++))
else

View File

@@ -28,10 +28,14 @@ export -f log
export CONFIG_FILE INSTALL_DIR
# 3. 防僵尸网络特征 (Cron Jitter) - 核心隐蔽逻辑
# 配合每 30 分钟的调度周期,将随机休眠控制在 0 到 180 秒 (3分钟) 内,彻底打散全球并发请求
JITTER_TIME=$((RANDOM % 180))
log "SYSTEM" "INFO" "主控引擎被 Cron 唤醒,进入防并发随机休眠状态: ${JITTER_TIME} 秒..."
sleep $JITTER_TIME
# 配合每 30 分钟的调度周期,将随机休眠控制在 0 到 180 秒内,彻底打散全球并发请求
if [ -t 1 ]; then
log "SYSTEM" "INFO " "💻 检测到人工终端干预,跳过静默休眠,立即执行任务!"
else
JITTER_TIME=$((RANDOM % 180))
log "SYSTEM" "INFO " "⏱️ 主控引擎由后台唤醒,进入防并发随机休眠状态: ${JITTER_TIME} 秒..."
sleep $JITTER_TIME
fi
# 4. 唤醒并读取功能开关,执行智能调度 (Feature Flag)
log "SYSTEM" "INFO" "休眠结束,开始计算本轮任务轮盘..."

View File

@@ -18,19 +18,53 @@ if [ -z "$TG_TOKEN" ] || [ -z "$CHAT_ID" ]; then
exit 0
fi
# 2. 节点元数据抓取 (v3.0.1修复: 严格使用配置中的协议探测出口与多节点容灾)
# 2. 节点元数据抓取 (v3.2.2 协议自适应与多级容灾)
NODE_NAME=$(hostname | cut -c 1-15)
# 多节点容灾探测
CURRENT_IP=$( (curl -${IP_PREF:-4} -s -m 5 api.ip.sb/ip || curl -${IP_PREF:-4} -s -m 5 ifconfig.me) 2>/dev/null | tr -d '[:space:]' )
# --- [防线 1: 底层路由锁定与协议自适应] ---
CURL_BIND_OPT=""
DYNAMIC_IP_PREF="-${IP_PREF:-4}"
if [[ -n "$BIND_IP" && "$BIND_IP" =~ ^[0-9a-fA-F:\.]+$ ]]; then
CURL_BIND_OPT="--interface $BIND_IP"
if [[ "$BIND_IP" == *":"* ]]; then
DYNAMIC_IP_PREF="-6"
elif [[ "$BIND_IP" == *"."* ]]; then
DYNAMIC_IP_PREF="-4"
fi
fi
# 多节点容灾探测出口 IP (注入协议自适应)
CURRENT_IP=$( (curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 api.ip.sb/ip || curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 ifconfig.me) 2>/dev/null | tr -d '[:space:]' )
# 强制兜底:如果所有外部 API 都挂了,直接使用本地强行锁定的 BIND_IP
[ -z "$CURRENT_IP" ] && CURRENT_IP="$BIND_IP"
# 为可能获取到的 IPv6 自动添加方括号护甲
[[ "$CURRENT_IP" == *":"* ]] && [[ "$CURRENT_IP" != *"["* ]] && CURRENT_IP="[${CURRENT_IP}]"
# 智能判断 IP 属性
ISP_INFO=$(curl -${IP_PREF:-4} -s -m 5 api.ip.sb/geoip | jq -r '.organization' 2>/dev/null)
# --- [防线 2: 多级 ISP 容灾探针链路] ---
ISP_INFO=""
# 探针 A: 纯文本 API (免 jq极速稳定)
ISP_INFO=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 ipinfo.io/org 2>/dev/null)
# 探针 B: 备用纯文本 API
if [ -z "$ISP_INFO" ] || [[ "$ISP_INFO" == *"error"* ]]; then
ISP_INFO=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 ip-api.com/line/?fields=isp 2>/dev/null)
fi
# 探针 C: 原版的 JSON API (需要 jq 兜底)
if [ -z "$ISP_INFO" ] || [[ "$ISP_INFO" == *"error"* ]]; then
if command -v jq &> /dev/null; then
ISP_INFO=$(curl $CURL_BIND_OPT $DYNAMIC_IP_PREF -s -m 5 api.ip.sb/geoip | jq -r '.organization' 2>/dev/null)
fi
fi
# --- [防线 3: 数据清洗 (遵循底层共识原则)] ---
# 剔除 ipinfo 返回的开头 AS 号 (例如 "AS137535 JT TELECOM" -> "JT TELECOM")
ISP_INFO=$(echo "$ISP_INFO" | sed -E 's/^AS[0-9]+ //')
# 最终兜底判断
[ -z "$ISP_INFO" ] || [ "$ISP_INFO" == "null" ] && ISP_INFO="未知 ISP"
if [[ "$ISP_INFO" == *"Cloudflare"* ]]; then

View File

@@ -1,48 +1,49 @@
#!/bin/bash
# ==========================================================
# 脚本名称: uninstall.sh (IP-Sentinel 一键卸载脚本 V2.0)
# 核心功能: 清除所有世代的守护进程、清理系统定时任务、删除程序文件
# 脚本名称: uninstall.sh (IP-Sentinel 一键卸载脚本 V3.1.4 焦土版)
# 核心功能: 无痕清理守护进程、定时任务、运行目录及临时缓存
# ==========================================================
INSTALL_DIR="/opt/ip_sentinel"
echo "========================================================"
echo " 🗑️ 准备卸载 IP-Sentinel (VPS IP 自动养护哨兵)"
echo " 🗑️ 准备卸载 IP-Sentinel (边缘节点 Edge Agent)"
echo "========================================================"
# 1. 停止运行中的守护进程与主控模块 (涵盖 V1.0 至 V2.0 架构全量进程)
# 1. 停止运行中的守护进程与主控模块 (涵盖所有历史版本进程)
echo "[1/3] 正在终止后台守护进程与所有养护任务..."
# 击杀旧版 (V1.x) 遗留进程
pgrep -f tg_daemon.sh | xargs -r kill -9 >/dev/null 2>&1
# 击杀新版 (V2.x) 神经末梢守护进程
pgrep -f agent_daemon.sh | xargs -r kill -9 >/dev/null 2>&1
pgrep -f webhook.py | xargs -r kill -9 >/dev/null 2>&1
# 击杀调度引擎与更新/战报模块
pgrep -f runner.sh | xargs -r kill -9 >/dev/null 2>&1
pgrep -f updater.sh | xargs -r kill -9 >/dev/null 2>&1
pgrep -f tg_report.sh | xargs -r kill -9 >/dev/null 2>&1
# 击杀所有具体的业务执行模块
pgrep -f mod_google.sh | xargs -r kill -9 >/dev/null 2>&1
pgrep -f mod_trust.sh | xargs -r kill -9 >/dev/null 2>&1
# 使用 pkill 替代传统的 pgrep | xargs指令更短、容错率更高
pkill -9 -f "tg_daemon.sh" >/dev/null 2>&1
pkill -9 -f "agent_daemon.sh" >/dev/null 2>&1
pkill -9 -f "webhook.py" >/dev/null 2>&1
pkill -9 -f "runner.sh" >/dev/null 2>&1
pkill -9 -f "updater.sh" >/dev/null 2>&1
pkill -9 -f "tg_report.sh" >/dev/null 2>&1
pkill -9 -f "mod_google.sh" >/dev/null 2>&1
pkill -9 -f "mod_trust.sh" >/dev/null 2>&1
# 2. 清除系统定时任务 (Cron)
echo "[2/3] 正在清理系统定时任务 (Cron)..."
crontab -l 2>/dev/null | grep -v "ip_sentinel" > /tmp/cron_backup
crontab /tmp/cron_backup
rm -f /tmp/cron_backup
if crontab -l >/dev/null 2>&1; then
crontab -l | grep -v "ip_sentinel" > /tmp/cron_backup
crontab /tmp/cron_backup
rm -f /tmp/cron_backup
fi
# 3. 删除所有文件日志
echo "[3/3] 正在抹除核心程序、配置文件与系统日志..."
# 3. 删除所有文件日志与临时缓存
echo "[3/3] 正在抹除核心程序、配置文件与系统痕迹..."
if [ -d "$INSTALL_DIR" ]; then
rm -rf "$INSTALL_DIR"
fi
# 拔除 /tmp 目录下的所有更新下载临时文件和 V1/V2 遗留的偏移量记录
rm -f /tmp/ip_sentinel_*.txt
rm -f /tmp/ip_sentinel_*.json
echo "========================================================"
echo "✅ 卸载彻底完成IP-Sentinel 已从您的系统中无痕移除。"
echo "👋 感谢您的使用,期待未来再次为您守护 IP"
echo "💡 提示:如果安装时在防火墙放行了 Webhook 随机端口,请您按需手动关闭。"
echo "👋 感谢您的使用,期待未来再次为您守护资产!"
echo "========================================================"

View File

@@ -1,14 +1,16 @@
#!/bin/bash
# ==========================================================
# 脚本名称: updater.sh (IP-Sentinel 养料注入与系统维护模块)
# 核心功能: 定期静默更新热数据、清理瘦身日志文件
# 脚本名称: updater.sh (IP-Sentinel V3.3.0 养料注入与分频调度中枢)
# 核心功能: 静默更新热搜词/LBS、指纹库错峰调度、强制出站死锁
# ==========================================================
INSTALL_DIR="/opt/ip_sentinel"
CONFIG_FILE="${INSTALL_DIR}/config.conf"
# 你的专属 Forgejo 仓库 Raw 数据直链前缀
REPO_RAW_URL="https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main"
UA_TIME_FILE="${INSTALL_DIR}/core/.ua_last_update"
# GitHub 仓库 Raw 数据直链前缀
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
# 1. 加载本地冷数据配置
if [ ! -f "$CONFIG_FILE" ]; then
@@ -24,29 +26,94 @@ log() {
log "Updater" "INFO " "========== 触发后台静默 OTA 热数据更新 =========="
# 3. 容灾机制拉取 UA 池
TMP_UA="/tmp/ip_sentinel_ua.txt"
curl -sL "${REPO_RAW_URL}/data/user_agents.txt" -o "$TMP_UA"
if [ -s "$TMP_UA" ]; then
mv "$TMP_UA" "${INSTALL_DIR}/data/user_agents.txt"
log "Updater" "INFO " "✅ 设备指纹池 (User-Agents) 更新成功"
else
log "Updater" "WARN " "❌ UA 池拉取失败或为空,保留本地旧数据防崩溃"
rm -f "$TMP_UA"
# ==========================================================
# 🛡️ 终极护城河:构建强锚定出站的 curl 请求引擎
# ==========================================================
# 基础参数:跟随 install.sh 锁定的协议偏好 (4 或 6)
CURL_CMD="curl -${IP_PREF:-4} -sL"
# 【防坑核心】如果用户配置了死锁锚点,必须强制绑定网卡,杜绝流量溢出!
if [ -n "$BIND_IP" ]; then
# curl 的 --interface 参数不支持带方括号的 IPv6 地址,必须强行脱壳
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
CURL_CMD="$CURL_CMD --interface $RAW_BIND_IP"
fi
# 4. 容灾机制拉取当地最新搜索词库
# ==========================================================
# 3. 容灾机制拉取 UA 指纹池 (V3.3.0 引入 30 天错峰防惊群逻辑)
# ==========================================================
NOW=$(date +%s)
LAST_UPDATE=0
# 读取上一次更新的时间戳
if [ -f "$UA_TIME_FILE" ]; then
# tr -d 清除可能存在的换行或回车符,防止算术崩溃
LAST_UPDATE=$(cat "$UA_TIME_FILE" | tr -d '\r\n')
fi
# 校验数据合法性,防崩溃
if ! [[ "$LAST_UPDATE" =~ ^[0-9]+$ ]]; then
LAST_UPDATE=0
fi
DIFF=$((NOW - LAST_UPDATE))
# 距离上次拉取超过 30 天 (2592000 秒),才执行下载
if [ "$DIFF" -ge 2592000 ] || [ "$LAST_UPDATE" -eq 0 ]; then
TMP_UA="/tmp/ip_sentinel_ua.txt"
# 使用重装升级后的 CURL_CMD
$CURL_CMD "${REPO_RAW_URL}/data/user_agents.txt" -o "$TMP_UA"
if [ -s "$TMP_UA" ]; then
mv "$TMP_UA" "${INSTALL_DIR}/data/user_agents.txt"
echo "$NOW" > "$UA_TIME_FILE"
log "Updater" "INFO " "✅ 设备指纹池 (User-Agents) 30天错峰滚动更新成功"
else
log "Updater" "WARN " "❌ UA 池拉取失败,保留本地旧数据防崩溃"
rm -f "$TMP_UA"
fi
else
DAYS_LEFT=$(((2592000 - DIFF) / 86400))
log "Updater" "INFO " "⏳ 设备指纹池处于 30 天静默期 (剩余约 ${DAYS_LEFT} 天),跳过拉取"
fi
# ==========================================================
# 4. 容灾机制拉取当地最新搜索词库 (每日高频拉取,保证活体新鲜度)
# ==========================================================
TMP_KW="/tmp/ip_sentinel_kw.txt"
curl -sL "${REPO_RAW_URL}/data/keywords/kw_${REGION_CODE}.txt" -o "$TMP_KW"
$CURL_CMD "${REPO_RAW_URL}/data/keywords/kw_${REGION_CODE}.txt" -o "$TMP_KW"
if [ -s "$TMP_KW" ]; then
mv "$TMP_KW" "${INSTALL_DIR}/data/keywords/kw_${REGION_CODE}.txt"
log "Updater" "INFO " "✅ 区域搜索词库 (kw_${REGION_CODE}) 更新成功"
log "Updater" "INFO " "✅ 区域搜索词库 (kw_${REGION_CODE}) 每日同步成功"
else
log "Updater" "WARN " "❌ 搜索词库拉取失败,保留本地旧数据防崩溃"
rm -f "$TMP_KW"
fi
# 5. 【升级点】日志防满瘦身机制 (保留最近 2000 行)
# ==========================================================
# 5. 自适应拉取本地 LBS 专属 JSON 规则库 (每日同步)
# ==========================================================
REGION_JSON_FILE=$(find "${INSTALL_DIR}/data/regions" -name "*.json" 2>/dev/null | head -n 1)
if [ -n "$REGION_JSON_FILE" ] && [ -f "$REGION_JSON_FILE" ]; then
REL_PATH=${REGION_JSON_FILE#*${INSTALL_DIR}/}
TMP_JSON="/tmp/ip_sentinel_region.json"
$CURL_CMD "${REPO_RAW_URL}/${REL_PATH}" -o "$TMP_JSON"
if [ -s "$TMP_JSON" ]; then
mv "$TMP_JSON" "$REGION_JSON_FILE"
log "Updater" "INFO " "✅ 核心战区规则库 ($REL_PATH) 每日同步成功"
else
log "Updater" "WARN " "❌ 战区规则库拉取失败,保留本地旧数据"
rm -f "$TMP_JSON"
fi
fi
# ==========================================================
# 6. 日志防满瘦身机制 (保留最近 2000 行)
# ==========================================================
if [ -f "$LOG_FILE" ]; then
tail -n 2000 "$LOG_FILE" > "${LOG_FILE}.tmp"
mv "${LOG_FILE}.tmp" "$LOG_FILE"

View File

@@ -3,4 +3,7 @@ S&P 500 stock chart
local coffee shops near me
latest tech news
California traffic updates
AI startups in Silicon Valley
AI startups in Silicon ValleySan Jose weather this weekend
Silicon Valley tech news
best tacos in San Jose
Apple park visitor center hours

View File

@@ -11,7 +11,8 @@
"id": "CA",
"name": "California (加州)",
"cities": [
{ "id": "Los_Angeles", "name": "Los Angeles (洛杉矶)" }
{ "id": "Los_Angeles", "name": "Los Angeles (洛杉矶)" },
{ "id": "San_Jose", "name": "San Jose (圣何塞)" }
]
}
]

View File

@@ -0,0 +1,21 @@
{
"region_name": "United States - San Jose",
"google_module": {
"base_lat": 37.3382,
"base_lon": -121.8863,
"lang_params": "hl=en&gl=US",
"valid_url_suffix": "com"
},
"trust_module": {
"white_urls": [
"https://en.wikipedia.org/wiki/Special:Random",
"https://www.yahoo.com/",
"https://www.target.com/",
"https://www.npr.org/",
"https://www.weather.com/",
"https://www.amazon.com/",
"https://www.cdc.gov/",
"https://www.mercurynews.com/"
]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,12 @@
#!/bin/bash
# ==========================================================
# 脚本名称: install_master.sh (IP-Sentinel 控制中枢部署脚本 v3.2.3)
# 核心功能: 部署/卸载调度中枢、SQLite 资产管理、平滑热更新引擎
# ==========================================================
# [新增] 提取仓库直链前缀变量,方便后续在官方库和私库间一键切换
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/legacy"
# 临时改为私库地址用于测试
# REPO_RAW_URL="https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main"
@@ -10,7 +15,7 @@ DB_FILE="${MASTER_DIR}/sentinel.db"
echo "========================================================"
# [修改] 将欢迎语改为更通用的文案,因为现在不仅能部署,还能卸载
echo " 🧠 欢迎使用 IP-Sentinel Master (控制中枢)"
echo " 🧠 欢迎使用 IP-Sentinel Master (控制中枢) v3.2.2"
echo "========================================================"
# [新增] 交互式操作菜单:支持选择部署或调用卸载程序
@@ -29,13 +34,51 @@ if [ "$ACTION_CHOICE" == "2" ]; then
exit 0
fi
# ================== [v3.1.1 延续: 安装前环境纯净度清理] ==================
echo -e "\n⏳ 正在清理旧版 Master 守护进程 (绝对安全保留 SQLite 数据库)..."
# ================== [v3.2.2 新增: 平滑升级模式嗅探] ==================
UPGRADE_MODE="false"
KEEP_DB="true"
if [ "$ACTION_CHOICE" == "1" ] && [ -f "${MASTER_DIR}/master.conf" ]; then
echo -e "\n\033[33m💡 司令部雷达提示:检测到本机已部署过 Master 中枢。\033[0m"
read -p "👉 是否按原配置直接进行平滑升级?(y/n, 默认y): " UPGRADE_CHOICE
if [[ -z "$UPGRADE_CHOICE" || "$UPGRADE_CHOICE" =~ ^[Yy]$ ]]; then
UPGRADE_MODE="true"
read -p "👉 是否保留历史节点数据库 (SQLite)(y/n, 默认y): " DB_CHOICE
if [[ "$DB_CHOICE" =~ ^[Nn]$ ]]; then
KEEP_DB="false"
fi
# 汲取原配置进入内存
source "${MASTER_DIR}/master.conf"
echo -e "\033[32m✅ 已激活 [平滑升级模式],即将跳过基础配置,直接更新核心中枢...\033[0m"
else
echo -e "\033[33m🔄 您选择了重新配置,旧的中枢数据将被彻底抹除。\033[0m"
fi
fi
# ====================================================================
# ================== [v3.2.2 优化: 安装前环境纯净度清理与数据保护] ==================
echo -e "\n⏳ 正在清理旧版 Master 守护进程..."
pkill -9 -f "tg_master.sh" >/dev/null 2>&1 || true
if [ "$UPGRADE_MODE" == "true" ]; then
if [ "$KEEP_DB" == "false" ]; then
rm -f "$DB_FILE" 2>/dev/null
echo -e "🗑️ 历史节点数据库已按指令清空。"
else
echo -e "📦 历史节点数据库 (SQLite) 已绝密保留。"
fi
# 删除旧的核心脚本,准备拉取新的
rm -f "${MASTER_DIR}/tg_master.sh" 2>/dev/null
else
# 焦土政策:如果不是升级模式,直接扬了整个司令部目录
rm -rf "$MASTER_DIR" 2>/dev/null
fi
echo -e "\033[32m✅ 旧进程已肃清!\033[0m"
# =======================================================================
# 1. 环境依赖安装
echo "[1/4] 安装核心依赖 (curl, jq, sqlite3)..."
echo -e "\n[1/4] 安装核心依赖 (curl, jq, sqlite3)..."
if [ -f /etc/debian_version ]; then
apt-get update -y >/dev/null 2>&1
apt-get install -y curl jq sqlite3 procps >/dev/null 2>&1
@@ -45,17 +88,23 @@ fi
mkdir -p "$MASTER_DIR"
# 2. 交互配置机器人
echo -e "\n[2/4] 配置控制中枢机器人:"
read -p "请输入 Telegram Bot Token: " TG_TOKEN
# ==========================================================
# 🛑 如果是全新部署,才询问 Token 并写入配置
# ==========================================================
if [ "$UPGRADE_MODE" == "false" ]; then
# 2. 交互配置机器人
echo -e "\n[2/4] 配置控制中枢机器人:"
read -p "请输入 Telegram Bot Token: " TG_TOKEN
cat > "${MASTER_DIR}/master.conf" << EOF
cat > "${MASTER_DIR}/master.conf" << EOF
TG_TOKEN="$TG_TOKEN"
DB_FILE="$DB_FILE"
MASTER_DIR="$MASTER_DIR"
EOF
fi
# 🛑 拦截块结束
# 3. 初始化 SQLite 数据库
# 3. 初始化 SQLite 数据库 (幂等操作,升级模式下可安全修补表结构)
echo -e "\n[3/4] 正在初始化 SQLite 数据库表结构..."
sqlite3 "$DB_FILE" <<EOF
CREATE TABLE IF NOT EXISTS nodes (
@@ -89,10 +138,17 @@ rm -f /tmp/cron_master
# 立刻启动
pgrep -f tg_master.sh >/dev/null || nohup bash "${MASTER_DIR}/tg_master.sh" >/dev/null 2>&1 &
# ================== [v3.2.2 优化: 战报文案分流] ==================
echo "========================================================"
echo "🎉 Master 控制中枢部署完成!"
echo "🤖 机器人现已开始全局接客,等待边缘节点注册。"
if [ "$UPGRADE_MODE" == "true" ]; then
echo "🎉 Master 控制中枢平滑热更新完成!"
echo "🤖 新版中枢引擎已接管数据库,继续等待边缘节点汇报。"
else
echo "🎉 Master 控制中枢部署完成!"
echo "🤖 机器人现已开始全局接客,等待边缘节点注册。"
fi
echo "========================================================"
# =================================================================
# ================== [v3.1.2 新增: 玻璃房透明装机统计] ==================
echo -e "\n📡 正在向开源社区汇报装机量 (完全匿名不收集IP)..."

View File

@@ -9,7 +9,7 @@ CONF="/opt/ip_sentinel_master/master.conf"
[ ! -f "$CONF" ] && exit 1
source "$CONF"
OFFSET_FILE="/tmp/tg_master_offset"
OFFSET_FILE="${MASTER_DIR}/.tg_offset"
[[ -f $OFFSET_FILE ]] || echo "0" > $OFFSET_FILE
# --- 工具函数 ---
@@ -54,6 +54,11 @@ generate_signed_url() {
}
# ========================================================================
# ================== [v3.1.3 核心: 数据库结构无损热升级] ==================
# 自动探测并增加 region 字段,屏蔽已存在的报错,保护老节点数据
db_exec "ALTER TABLE nodes ADD COLUMN region TEXT DEFAULT 'UNKNOWN';" 2>/dev/null
# ========================================================================
# --- 核心轮询循环 ---
while true; do
OFFSET=$(cat $OFFSET_FILE)
@@ -79,33 +84,59 @@ while true; do
fi
# ==========================================
# 1. 节点注册通道 (v3.0.1 终极防注入补丁)
# 1. 节点注册通道 (V3.1.3 大区拓扑升级版)
# ==========================================
if [[ "$TEXT" == *"#REGISTER#"* ]]; then
REG_LINE=$(echo "$TEXT" | grep "#REGISTER#" | head -n 1 | tr -d '\` ')
IFS='|' read -r MAGIC RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
# 🛡️ 强制字符白名单过滤:物理抹杀所有 SQL 注入特殊字符
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-') # ChatID 只能是数字和负号
NODE_NAME=$(echo "$RAW_NODE" | tr -cd 'a-zA-Z0-9_.-' | cut -c 1-30) # 节点名限字母数字横杠下划线
AGENT_IP=$(echo "$RAW_IP" | tr -cd 'a-zA-Z0-9.:\[\]-' | cut -c 1-50) # IP 格式限制
AGENT_PORT=$(echo "$RAW_PORT" | tr -cd '0-9' | cut -c 1-5) # 端口只能是数字
# V3.1.3 兼容性拆包: 判断是新版协议 (5个字段) 还是老版协议 (4个字段)
FIELD_COUNT=$(echo "$REG_LINE" | awk -F'|' '{print NF}')
if [ "$FIELD_COUNT" -ge 5 ]; then
IFS='|' read -r MAGIC RAW_REGION RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
else
IFS='|' read -r MAGIC RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
RAW_REGION="UNKNOWN"
fi
# 🛡️ 强制字符白名单过滤:保留历史特征不变
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
AGENT_REGION=$(echo "$RAW_REGION" | tr -cd 'a-zA-Z0-9' | cut -c 1-10) # 提取国家大区
NODE_NAME=$(echo "$RAW_NODE" | tr -cd 'a-zA-Z0-9_.-' | cut -c 1-30)
AGENT_IP=$(echo "$RAW_IP" | tr -cd 'a-zA-Z0-9.:\[\]-' | cut -c 1-50)
AGENT_PORT=$(echo "$RAW_PORT" | tr -cd '0-9' | cut -c 1-5)
# ================== [v3.0.2 紧急加固: SSRF 内网隔离墙] ==================
# 拒绝 127.x, 10.x, 192.168.x, 172.16~31.x 以及 IPv6 回环地址的注册
if [[ "$AGENT_IP" =~ ^127\.|^10\.|^192\.168\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^::1$|^localhost$ ]]; then
send_msg "$CHAT_ID" "⛔ **安全拦截**:禁止注册内网或回环 IP防止 SSRF 攻击渗透。"
continue
fi
# 异常拦截:如果核心字段被过滤成了空值,说明是恶意请求,直接抛弃
if [ -z "$NODE_NAME" ] || [ -z "$AGENT_IP" ] || [ -z "$AGENT_PORT" ] || [ -z "$CHAT_ID" ]; then
send_msg "$CHAT_ID" "⛔ **安全拦截**:检测到非法注册载荷,请求已拒绝。"
continue
fi
db_exec "INSERT INTO nodes (chat_id, node_name, agent_ip, agent_port, last_seen) VALUES ('$CHAT_ID', '$NODE_NAME', '$AGENT_IP', '$AGENT_PORT', CURRENT_TIMESTAMP) ON CONFLICT(chat_id, node_name) DO UPDATE SET agent_ip='$AGENT_IP', agent_port='$AGENT_PORT', last_seen=CURRENT_TIMESTAMP;"
# 入库时追加 region 字段
db_exec "INSERT INTO nodes (chat_id, node_name, agent_ip, agent_port, last_seen, region) VALUES ('$CHAT_ID', '$NODE_NAME', '$AGENT_IP', '$AGENT_PORT', CURRENT_TIMESTAMP, '$AGENT_REGION') ON CONFLICT(chat_id, node_name) DO UPDATE SET agent_ip='$AGENT_IP', agent_port='$AGENT_PORT', last_seen=CURRENT_TIMESTAMP, region='$AGENT_REGION';"
send_msg "$CHAT_ID" "✅ 司令部已确认!节点接入成功: \`$NODE_NAME\` ($AGENT_IP:$AGENT_PORT)"
# ================== [v3.1.3 丝滑连招: 直接呼出全球大区雷达] ==================
REGION_DATA=$(db_exec "SELECT region, COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID' GROUP BY region;")
if [ -n "$REGION_DATA" ]; then
BTNS="["
while IFS='|' read -r REGION_NAME NODE_COUNT; do
[ -z "$REGION_NAME" ] && REGION_NAME="UNKNOWN"
FLAG="🌐"
case "$REGION_NAME" in
"US") FLAG="🇺🇸" ;; "JP") FLAG="🇯🇵" ;; "HK") FLAG="🇭🇰" ;;
"SG") FLAG="🇸🇬" ;; "UK"|"GB") FLAG="🇬🇧" ;; "DE") FLAG="🇩🇪" ;; "FR") FLAG="🇫🇷" ;;
esac
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
done <<< "$REGION_DATA"
BTNS="${BTNS%,}]"
send_ui "$CHAT_ID" "🌍 **全视界战略雷达**\n请选择要检阅的战区" "$BTNS"
fi
# ========================================================================
continue
fi
@@ -149,16 +180,56 @@ while true; do
# ====================================================================
"list_nodes")
NODE_LIST=$(db_exec "SELECT node_name FROM nodes WHERE chat_id='$CHAT_ID';")
if [ -z "$NODE_LIST" ]; then
# 【V3.1.3】一级菜单:大区聚合并列出数量
REGION_DATA=$(db_exec "SELECT region, COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID' GROUP BY region;")
if [ -z "$REGION_DATA" ]; then
send_msg "$CHAT_ID" "⚠️ 您名下暂无在线节点,请先在边缘机执行部署。"
else
BTNS="["
for N in $NODE_LIST; do
BTNS="$BTNS[{\"text\":\"🖥️ $N\",\"callback_data\":\"manage:$N\"}],"
done
while IFS='|' read -r REGION_NAME NODE_COUNT; do
[ -z "$REGION_NAME" ] && REGION_NAME="UNKNOWN"
FLAG="🌐"
case "$REGION_NAME" in
"US") FLAG="🇺🇸" ;; "JP") FLAG="🇯🇵" ;; "HK") FLAG="🇭🇰" ;;
"SG") FLAG="🇸🇬" ;; "UK"|"GB") FLAG="🇬🇧" ;; "DE") FLAG="🇩🇪" ;; "FR") FLAG="🇫🇷" ;;
esac
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
done <<< "$REGION_DATA"
BTNS="${BTNS%,}]"
send_ui "$CHAT_ID" "🔍 您名下的活跃节点" "$BTNS"
send_ui "$CHAT_ID" "🌍 **全视界战略雷达**\n请选择要检阅的战区" "$BTNS"
fi
;;
region:*)
# 【V3.1.3】二级菜单:目标大区下的节点双列排版
TARGET_REGION=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9')
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
NODE_LIST=$(db_exec "SELECT node_name FROM nodes WHERE chat_id='$CHAT_ID' AND region='$TARGET_REGION';")
if [ -z "$NODE_LIST" ]; then
send_msg "$CHAT_ID" "⚠️ 该战区下暂无可用节点。"
else
BTNS="["
COL=0
ROW_STR="["
for N in $NODE_LIST; do
ROW_STR="$ROW_STR{\"text\":\"🖥️ $N\",\"callback_data\":\"manage:$N\"},"
COL=$((COL+1))
if [ $COL -eq 2 ]; then
ROW_STR="${ROW_STR%,}]"
BTNS="$BTNS$ROW_STR,"
COL=0
ROW_STR="["
fi
done
# 如果是奇数,补齐最后的尾巴
if [ $COL -eq 1 ]; then
ROW_STR="${ROW_STR%,}]"
BTNS="$BTNS$ROW_STR,"
fi
# 添加返回上级大区雷达的按钮
BTNS="$BTNS[{\"text\":\"⬅️ 返回全球战区分布\",\"callback_data\":\"list_nodes\"}]]"
send_ui "$CHAT_ID" "📍 **[$TARGET_REGION] 战区哨兵矩阵**\n请下达控制指令" "$BTNS"
fi
;;
@@ -166,7 +237,7 @@ while true; do
# 🛡️ 强制过滤节点名,防止面板渲染时发生 XSS 或注入
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
# 【核心升级】拆分下发按钮,精准对应 Google 与 Trust 两个模块,并排版为 3 行 2 列
BTNS="[[{\"text\":\"📍 Google 纠偏\",\"callback_data\":\"google:$TARGET_NODE\"}, {\"text\":\"🛡️ 信用净化\",\"callback_data\":\"trust:$TARGET_NODE\"}], [{\"text\":\"📜 实时日志\",\"callback_data\":\"log:$TARGET_NODE\"}, {\"text\":\"📊 统计战报\",\"callback_data\":\"report:$TARGET_NODE\"}], [{\"text\":\"🗑️ 剔除失联节点\",\"callback_data\":\"del:$TARGET_NODE\"}, {\"text\":\"⬅️ 返回主列表\",\"callback_data\":\"list_nodes\"}]]"
BTNS="[[{\"text\":\"📍 Google 纠偏\",\"callback_data\":\"google:$TARGET_NODE\"}, {\"text\":\"🛡️ 信用净化\",\"callback_data\":\"trust:$TARGET_NODE\"}], [{\"text\":\"📜 实时日志\",\"callback_data\":\"log:$TARGET_NODE\"}, {\"text\":\"📊 统计战报\",\"callback_data\":\"report:$TARGET_NODE\"}], [{\"text\":\"🗑️ 剔除失联节点\",\"callback_data\":\"del:$TARGET_NODE\"}, {\"text\":\"⬅️ 返回大区目录\",\"callback_data\":\"list_nodes\"}]]"
send_ui "$CHAT_ID" "⚙️ **目标锁定**: \`$TARGET_NODE\`\n请选择战术动作" "$BTNS"
;;
@@ -178,16 +249,23 @@ while true; do
db_exec "DELETE FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
send_msg "$CHAT_ID" "🗑️ 节点 \`$TARGET_NODE\` 的档案已从司令部彻底销毁!"
NODE_LIST=$(db_exec "SELECT node_name FROM nodes WHERE chat_id='$CHAT_ID';")
if [ -z "$NODE_LIST" ]; then
# 剔除后直接返回上级一级雷达菜单
REGION_DATA=$(db_exec "SELECT region, COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID' GROUP BY region;")
if [ -z "$REGION_DATA" ]; then
send_msg "$CHAT_ID" "⚠️ 当前司令部已无任何节点挂载。"
else
BTNS="["
for N in $NODE_LIST; do
BTNS="$BTNS[{\"text\":\"🖥️ $N\",\"callback_data\":\"manage:$N\"}],"
done
while IFS='|' read -r REGION_NAME NODE_COUNT; do
[ -z "$REGION_NAME" ] && REGION_NAME="UNKNOWN"
FLAG="🌐"
case "$REGION_NAME" in
"US") FLAG="🇺🇸" ;; "JP") FLAG="🇯🇵" ;; "HK") FLAG="🇭🇰" ;;
"SG") FLAG="🇸🇬" ;; "UK"|"GB") FLAG="🇬🇧" ;; "DE") FLAG="🇩🇪" ;; "FR") FLAG="🇫🇷" ;;
esac
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
done <<< "$REGION_DATA"
BTNS="${BTNS%,}]"
send_ui "$CHAT_ID" "🔍 刷新后的节点列表" "$BTNS"
send_ui "$CHAT_ID" "🌍 刷新后的全视界雷达" "$BTNS"
fi
;;

77
scripts/ua_generator.py Normal file
View File

@@ -0,0 +1,77 @@
import random
import os
# ==========================================
# IP-Sentinel 超大型高保真指纹工厂 (V3.1.5)
# 无需第三方库,直接生成千万级组合的真实指纹
# ==========================================
def generate_chrome_version():
# 模拟 2024 年主流 Chrome 内核号 (122 - 125)
major = random.randint(122, 125)
build = random.randint(5000, 6500)
patch = random.randint(10, 150)
return f"{major}.0.{build}.{patch}"
def generate_windows_ua(count=1000):
uas = set()
while len(uas) < count:
# 现代 Windows UA 已经固化为 Windows NT 10.0
uas.add(f"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{generate_chrome_version()} Safari/537.36")
return list(uas)
def generate_macos_ua(count=1000):
uas = set()
while len(uas) < count:
mac_os_minor = random.randint(11, 15)
mac_os_patch = random.randint(1, 6)
if random.choice([True, False]):
# Chrome on Mac
uas.add(f"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_{mac_os_minor}_{mac_os_patch}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{generate_chrome_version()} Safari/537.36")
else:
# Safari on Mac
safari_build = f"605.1.{random.randint(10, 15)}"
safari_version = f"17.{random.randint(1, 4)}"
uas.add(f"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_{mac_os_minor}_{mac_os_patch}) AppleWebKit/{safari_build} (KHTML, like Gecko) Version/{safari_version} Safari/{safari_build}")
return list(uas)
def generate_ios_ua(count=1000):
uas = set()
devices = ["iPhone", "iPad"]
while len(uas) < count:
device = random.choice(devices)
ios_major = random.randint(16, 17)
ios_minor = random.randint(1, 5)
ios_patch = random.randint(1, 3)
safari_build = f"605.1.{random.randint(10, 15)}"
safari_version = f"{ios_major}.{random.choice(['0', '1', '2', '3'])}"
uas.add(f"Mozilla/5.0 ({device}; CPU {'iPhone ' if device=='iPhone' else ''}OS {ios_major}_{ios_minor}_{ios_patch} like Mac OS X) AppleWebKit/{safari_build} (KHTML, like Gecko) Version/{safari_version} Mobile/15E148 Safari/604.1")
return list(uas)
def generate_android_ua(count=1000):
uas = set()
# 主流 Android 机型库
models = ["Pixel 8 Pro", "Pixel 8", "Pixel 7a", "Pixel 7 Pro", "SM-S928B", "SM-S928U", "SM-S918B", "SM-A546B", "SM-A346B", "23113RKC6C", "23049PCD8G", "CPH2437", "V2227A", "PGT-AN10", "NX729J"]
while len(uas) < count:
android_ver = random.randint(12, 14)
model = random.choice(models)
uas.add(f"Mozilla/5.0 (Linux; Android {android_ver}; {model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{generate_chrome_version()} Mobile Safari/537.36")
return list(uas)
if __name__ == "__main__":
# 确保输出目录存在
os.makedirs('data', exist_ok=True)
# 严格按照“绝对坐标”顺序生成 4000 条数据
pool = []
pool.extend(generate_windows_ua(1000)) # 行 1-1000
pool.extend(generate_macos_ua(1000)) # 行 1001-2000
pool.extend(generate_ios_ua(1000)) # 行 2001-3000
pool.extend(generate_android_ua(1000)) # 行 3001-4000
with open('data/user_agents.txt', 'w') as f:
for ua in pool:
f.write(ua + '\n')
print(f"✅ 成功生成 4000 条高保真绝对坐标指纹库!")