mirror of
https://github.com/hotyue/IP-Sentinel.git
synced 2026-05-12 02:49:42 +08:00
Compare commits
91 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de3b4ca6d3 | ||
|
|
423a765e5f | ||
|
|
ca74c31b96 | ||
|
|
9366240d62 | ||
|
|
1fa2cd10b1 | ||
|
|
3210c8cfcc | ||
|
|
d7e1e8fc8f | ||
|
|
a81f0564f1 | ||
|
|
3b20630e9e | ||
|
|
7321b76bb5 | ||
|
|
8016b0531c | ||
|
|
c7c93ea22e | ||
|
|
9aef79831c | ||
|
|
43f2e98459 | ||
|
|
5627c0115a | ||
|
|
f252b26088 | ||
|
|
12e6619ab9 | ||
|
|
a0bad1acf0 | ||
|
|
01d65972fd | ||
|
|
b1334fc06a | ||
|
|
918c73b5dc | ||
|
|
28f04a4eb9 | ||
|
|
26328e66c4 | ||
|
|
1d85837e79 | ||
|
|
ba8e2f1625 | ||
|
|
6b809138e5 | ||
|
|
6f4e871c7c | ||
|
|
fe2c9de80b | ||
|
|
6c0a589395 | ||
|
|
42a128fd6b | ||
|
|
a891f2017a | ||
|
|
66fdfb1908 | ||
|
|
aa2874fdcd | ||
|
|
120dd264c2 | ||
|
|
455f98fafd | ||
|
|
2c1041ebed | ||
|
|
0af3ff5cd8 | ||
|
|
6faa7b2c2a | ||
|
|
873b6996ca | ||
|
|
84832395bd | ||
|
|
96a7400be8 | ||
|
|
ba565978c6 | ||
|
|
b53032cc92 | ||
|
|
e96eacd6f8 | ||
|
|
4cac51673a | ||
|
|
be75e5b65c | ||
|
|
c7ece6620c | ||
|
|
7e9da4b82a | ||
|
|
c657c92b27 | ||
|
|
d43163703e | ||
|
|
1150450718 | ||
|
|
df6483afa8 | ||
|
|
29de2eadf8 | ||
|
|
318689f163 | ||
|
|
6b9563b858 | ||
|
|
2c7491449c | ||
|
|
f698bc4b92 | ||
|
|
c18a10dbd5 | ||
|
|
69e7803c40 | ||
|
|
b2a5afe562 | ||
|
|
45fda5f498 | ||
|
|
1459d5efec | ||
|
|
b7375e5e7d | ||
|
|
4a7f88a0da | ||
|
|
a0ec759dd7 | ||
|
|
fe1e4c0e6f | ||
|
|
62770ba50c | ||
|
|
11bdece9e2 | ||
|
|
2c5df879c0 | ||
|
|
862de2e49e | ||
|
|
26e3fd435d | ||
|
|
e279fc6e38 | ||
|
|
acff304a35 | ||
|
|
9fe39fc274 | ||
|
|
9eccace3bc | ||
|
|
cf410fce61 | ||
|
|
0ffb173bb5 | ||
|
|
edd6425fe5 | ||
|
|
16b6c3f20a | ||
|
|
e3750f4565 | ||
|
|
d3face9621 | ||
|
|
aeeb9e1a43 | ||
|
|
09c6bab04a | ||
|
|
2e73aa8833 | ||
|
|
03d88f4856 | ||
|
|
3cab2b2379 | ||
|
|
87faeae8f7 | ||
|
|
19f0bb7c36 | ||
|
|
01305f98b5 | ||
|
|
a3ae3b29f9 | ||
|
|
053c9805d4 |
44
.github/workflows/daily_keywords.yml
vendored
Normal file
44
.github/workflows/daily_keywords.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
name: Daily Trends Factory
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# 每天 UTC 18:00 运行 (北京时间凌晨 02:00)
|
||||||
|
- cron: '0 18 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-trends:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.10'
|
||||||
|
|
||||||
|
- name: Execute Trends Engine
|
||||||
|
run: python scripts/fetch_trends.py
|
||||||
|
|
||||||
|
- name: Commit and Push
|
||||||
|
run: |
|
||||||
|
git config --global user.name "github-actions[bot]"
|
||||||
|
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
git add data/keywords/
|
||||||
|
|
||||||
|
# 防御机制:如果没有新数据,就静默退出,不产生空提交
|
||||||
|
if git diff --staged --quiet; then
|
||||||
|
echo "No changes, skipping."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 策略:放弃危险的 amend 强制覆盖,采用带日期的标准安全提交
|
||||||
|
git commit -m "chore(data): 🤖 自动机兵:刷新全战区热点词库 [$(date +'%Y-%m-%d')]"
|
||||||
|
git push origin main
|
||||||
33
.github/workflows/ua_factory.yml
vendored
Normal file
33
.github/workflows/ua_factory.yml
vendored
Normal 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)
|
||||||
75
README.md
75
README.md
@@ -1,20 +1,35 @@
|
|||||||
# 🛡️ IP-Sentinel (分布式 IP 哨兵集群)
|
# 🛡️ IP-Sentinel (分布式 IP 哨兵集群)
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
> **一个极度轻量、零感知、支持中枢遥控的 VPS IP 自动化养护与区域纠偏引擎。**
|
> **一个极度轻量、零感知、支持中枢遥控的 VPS IP 自动化养护与区域纠偏引擎。**
|
||||||
|
|
||||||
📢 官方战术交流频道: 🛰️ [IP-Sentinel Matrix](https://t.me/IP_Sentinel_Matrix)
|
📢 官方战术交流频道: 🛰️ [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 随时随地对整个舰队进行毫秒级“点名”与“遥控”。
|
||||||
|
|
||||||
## ✨ 核心极客特性
|
## ✨ 核心极客特性 (Evolution History)
|
||||||
|
|
||||||
* 🗺️ **全球拓扑矩阵 (Global Nexus)**:**v3.0.0 新特性**。引入动态 `map.json` 索引中心与多级地理架构 (国家-省州-城市)。安装脚本全自动解析云端地图,支持无限扩展全球节点,真正实现“按图索骥”。
|
- 🌍 **[v3.5.0] 大洲战区与降维引擎 (Continental Grouping)**:随着全球版图的极速扩张,彻底重构底层地图索引。引入“战区(大洲)-国家-省州-城市”四级降维解析菜单,完美承载未来数十个国家的扩容,终端交互界面永远清爽干练。
|
||||||
* ☁️ **云端中枢 (Public Master)**:引入官方公共机器人 [@OmniBeacon_bot](https://t.me/OmniBeacon_bot),新手无需部署 Master 司令部,一键回车即可接入全球养护矩阵,极大降低入伍门槛。
|
- 🧬 **[v3.5.0] SSOT 动态版本溯源 (Single Source of Truth)**:全系脚本彻底消灭硬编码版本号!引入企业级 DevOps 理念,部署时动态抓取云端信标并固化落盘,常驻进程与日志绝对继承本地基因,实现“改一处,全网同步”的极致架构。
|
||||||
* 🧠 **分布式中枢 (Master-Agent)**:对于硬核极客,支持私有化部署。一台 Master 主控集成 SQLite 数据库,统管无数台 Agent 边缘节点,确保数据绝对私有。
|
- 🎯 **[v3.4.0] 版本锚点与路由中枢 (Version-Linked Epoch)**:彻底告别“盲盒式更新”,全系引入全局版本号机制。边缘节点具备“身份自知”能力,安装脚本根据本地版本执行精准的路由跳转,实现新老架构的智能化无损跃迁。
|
||||||
* 🎮 **TG 战术面板 (Command Center)**:无需记忆繁琐命令,原生 Inline Keyboard 按钮驱动。支持一键调出节点列表、一键下发伪装指令、一键索要精准战报、**毫秒级抓取实时运行日志**。
|
- 📡 **[v3.4.0] OTA 实时版本探针 (Version-Aware Radar)**:边缘哨兵现已接入云端“北极星”校准。每日战报自动扫描 GitHub 最新发布状态,发现版本落后即刻在 Telegram 战报底部亮起 OTA 预警,消除指挥官与前线的信息差。
|
||||||
* 🛡️ **NAT 穿透与安全网关 (NAT-Friendly)**:边缘节点采用 Python3 极轻量 Webhook 监听,**完全自定义通信端口**,完美支持受限 NAT 小鸡。独创 TG 转发授权机制,杜绝野生节点恶意接入。
|
- 📡 **[v3.3.0] OTA 动态活体词库 (Dynamic Trends)**:彻底废弃静态搜索词,引入 GitHub Actions 云端流水线。每天自动抓取全战区当日 Google 热搜榜单,并通过边缘节点每日静默同步,让搜索行为永远贴合当地当天的真实网络脉搏。
|
||||||
* 👻 **高仿真人类行为 (Human-Like)**:摒弃死板的 Ping/Curl,引入单次会话指纹锁定、10 米级 GPS 坐标微抖动、以及 60~150 秒的真实阅读停顿拉伸,完美避开 AI 封控。
|
- 🔀 **[v3.3.0] 智能错峰调度 (Thundering Herd Mitigation)**:首创节点部署时间戳锚定逻辑。边缘节点按需智能分频(每日拉取词库,每月按 30 天周期错峰拉取千万级指纹库),化解“惊群效应”,抹平统一升级时的并发特征,隐匿于无形。
|
||||||
* 📡 **OTA 静默进化 (Smart Updates)**:系统每周日凌晨自动从云端拉取最新的“热搜词汇”和“真实设备指纹池”,确保养护行为与时俱进、永不过时。
|
- 🎯 **[v3.2.2] 多级容灾与高精度探针 (High-Precision Probe)**:重写战报模块与底层协议自适应逻辑,植入多级 ISP 容灾探针链路,并按“底层数据共识原则”智能清洗冗余 AS 号。确保在纯 V6、隧道或弱网环境下,数据获取依然 100% 精准畅通。
|
||||||
|
- 🔄 **[v3.2.2] 平滑热更新装甲 (Smooth Upgrade Engine)**:全系植入状态机嗅探逻辑。再次执行部署脚本时将自动识别并继承历史配置、SQLite 数据库与锚定 IP,一键回车瞬间完成无损换代。
|
||||||
|
- 🖧 **[v3.2.1] 底层路由死锁 (Hard-Bind Routing)**:底层探测引擎强力接管 curl 核心参数 (`--interface`),强制将发出的每一滴伪装流量死死绑定在您设定的物理网卡或隧道 IP 上,彻底杜绝双栈或多网卡环境下的流量溢出漏洞。
|
||||||
|
- 👻 **[v3.2.0] 设备资产持久化 (Hash-Seeded Persona)**:彻底摒弃随机抽取指纹,引入基于节点物理 IP 的哈希锚定引擎。利用不可变哈希种子,为您的每台 VPS 永久锁定 3 个绝对专属设备,完美构建高权重真实家庭内网画像,根除“僵尸网络”同质化特征!
|
||||||
|
- 🗺️ **[v3.1.0] 全球拓扑矩阵 (Global Nexus)**:守护版图横跨亚、欧、美三大洲。为每个国家注入极其硬核的“原生本地化”搜索词库与本土高权重站点(如政府、权威媒体、高铁网),真正实现拟真融入。
|
||||||
|
|
||||||
|
**—— 💎 骨干基建特征 ——**
|
||||||
|
- 🏭 **自动化指纹兵工厂**:依托 GitHub Actions CI/CD 流水线,每月 1 日无人值守锻造 4000+ 带绝对物理分区的真实终端设备数据。
|
||||||
|
- 🔒 **叹息之墙 (Zero-Trust HMAC)**:底层通讯引入 时间戳 + HMAC-SHA256 军用级动态签名。指令有效期仅 60 秒(阅后即焚),彻底免疫中间人抓包与重放攻击。
|
||||||
|
- ☁️ **云端中枢 (Public Master)**:官方公共机器人 @OmniBeacon_bot,新手免自建,一键接入极速入伍!同时支持硬核极客私有化 SQLite 分布式部署。
|
||||||
|
- 🎮 **TG 战术面板 (Command Center)**:全 Inline Keyboard 交互,一键下发伪装指令、索要战报、毫秒级抓取边缘节点实时运行日志。
|
||||||
|
- 👁️🗨️ **玻璃房透明遥测 (Glasshouse)**:基于 Cloudflare Workers 的全透明计数中枢,绝对零隐私收集,仅作原子累加,底层网关源码全开源。
|
||||||
|
|
||||||
## 📂 项目架构 (Monorepo)
|
## 📂 项目架构 (Monorepo)
|
||||||
|
|
||||||
@@ -22,18 +37,22 @@
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
📦 IP-Sentinel
|
📦 IP-Sentinel
|
||||||
|
┣ 📂 .github/workflows/ # 🏭 自动化兵工厂:每月定时触发指纹生成的 CI/CD 流水线
|
||||||
┣ 📂 master/ # 🧠 司令部:SQLite 存储、TG 监听与 Webhook 调度中心
|
┣ 📂 master/ # 🧠 司令部:SQLite 存储、TG 监听与 Webhook 调度中心
|
||||||
┣ 📂 core/ # 🛡️ 边缘哨兵:Webhook 被动监听、高拟真养护引擎
|
┣ 📂 core/ # 🛡️ 边缘哨兵:Webhook 被动监听、哈希锚定执行引擎
|
||||||
┗ 📂 data/ # 🗂️ 全球数据规则库 (v3.0 全新拓扑)
|
┣ 📂 scripts/ # 🐍 兵工厂引擎:基于 Python 的多物理分区 UA 生成器
|
||||||
┣ 📜 map.json # 🌐 全球区域索引大脑 (Master Index)
|
┣ 📂 data/ # 🗂️ 全球数据规则库 (动态拓扑)
|
||||||
┣ 📂 regions/ # 🧊 冷数据:按 [国家/省州/城市] 深度细分的 LBS 锚点
|
┃ ┣ 📜 map.json # 🌍 全球区域大脑 (v3.5.0 大洲战区拓扑)
|
||||||
┣ 📂 keywords/ # 🔥 热数据:按国家归类的动态搜索词库 (OTA 自动更新)
|
┃ ┣ 📂 regions/ # 🧊 冷数据:按 [国家/省州/城市] 深度细分的 LBS 锚点
|
||||||
┗ 📜 user_agents.txt # 🔥 热数据:全局真实设备指纹池
|
┃ ┣ 📂 keywords/ # 🔥 热数据:按国家归类的动态搜索词库 (OTA 自动更新)
|
||||||
|
┃ ┗ 📜 user_agents.txt # 🔥 热数据:由兵工厂每月锻造的绝对坐标专属设备库
|
||||||
|
┣ 📜 version.txt # 🚩 全球版本信标:SSOT 单一事实来源锚点 (v3.5.0)
|
||||||
|
┗ 📂 telemetry/ # 👁️🗨️ 玻璃房计划:Cloudflare Workers 透明计数器网关源码
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🚀 极速部署 (Quick Start)
|
## 🚀 极速部署 (Quick Start)
|
||||||
|
|
||||||
v3.0.0 提供了两种接入模式,请根据您的战术需求选择:
|
v3.5.x 提供了两种接入模式,请根据您的战术需求选择:
|
||||||
|
|
||||||
### 🔹 模式 A:官方公共模式 (最简、推荐)
|
### 🔹 模式 A:官方公共模式 (最简、推荐)
|
||||||
**适合不想折腾、只想快速养护 IP 的新兵。**
|
**适合不想折腾、只想快速养护 IP 的新兵。**
|
||||||
@@ -53,7 +72,6 @@ bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/core/i
|
|||||||
```Bash
|
```Bash
|
||||||
bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/master/install_master.sh)
|
bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/master/install_master.sh)
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
2. **部署 Agent**:在需要养护的机器上执行 Agent 脚本,输入您自建机器人的 Token 以及与 Master 一致的配置。
|
2. **部署 Agent**:在需要养护的机器上执行 Agent 脚本,输入您自建机器人的 Token 以及与 Master 一致的配置。
|
||||||
```Bash
|
```Bash
|
||||||
@@ -62,15 +80,36 @@ bash <(curl -sL https://raw.githubusercontent.com/hotyue/IP-Sentinel/main/core/i
|
|||||||
```
|
```
|
||||||
3. **激活节点**:同上,将暗号转发给您自己的机器人即可。
|
3. **激活节点**:同上,将暗号转发给您自己的机器人即可。
|
||||||
|
|
||||||
|
### ⚠️ 架构级热升级指引 (Upgrade to v3.5.0)
|
||||||
|
|
||||||
|
得益于 **v3.5.0 全新引入的 SSOT 版本锚点与状态机路由**,系统升级现已变得极其智能化。
|
||||||
|
|
||||||
|
**如果您是从远古旧版 (v3.3.1 / v3.3.2) 升级:**
|
||||||
|
1. 在终端再次运行对应的官方部署指令。
|
||||||
|
2. 脚本会识别到您处于“前版本锚点时代”,会自动为您执行【跨代架构重组】。
|
||||||
|
3. **关键动作**:由于节点命名防撞机制变更,升级后您的 TG 会收到一条新的 `#REGISTER#` 指令,请点击并发送一次以同步新身份。
|
||||||
|
4. **清理**:在面板中手动剔除失联的旧节点即可。
|
||||||
|
|
||||||
|
**如果您已处于 v3.4.0+:**
|
||||||
|
所有的升级已进入**“极致静默平滑模式”**。安装引擎会动态抓取云端 `version.txt`,自动修正本地 `config.conf` 的版本号,一键回车,3 秒即可完成全系组件的热重载换代!
|
||||||
|
|
||||||
🗑️ 一键无痕卸载
|
🗑️ 一键无痕卸载
|
||||||
如果你需要清理某个边缘节点,只需重新运行 core/install.sh 并选择 [3],或直接在节点终端执行:
|
如果你需要清理某个边缘节点,只需重新运行 `core/install.sh` 并选择 **[2]**,或直接在节点终端执行:
|
||||||
|
|
||||||
```Bash
|
```Bash
|
||||||
bash /opt/ip_sentinel/core/uninstall.sh
|
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)
|
📡 战术联络 (Community)
|
||||||
如果你在使用过程中遇到任何疑难杂症,或者想围观大佬们的养护战报,欢迎加入我们的基地:
|
如果你在使用过程中遇到任何疑难杂症,或者想围观大佬们的养护战报,欢迎加入我们的基地:
|
||||||
- Telegram 频道: [@IP_Sentinel_Matrix](https://t.me/IP_Sentinel_Matrix)
|
- Telegram 频道: [@IP_Sentinel_Matrix](https://t.me/IP_Sentinel_Matrix)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
# 脚本名称: agent_daemon.sh (受控节点 Webhook 守护进程 V3.0.3)
|
# 脚本名称: agent_daemon.sh (受控节点 Webhook 守护进程 - 动态锚点版)
|
||||||
# 核心功能: 智能防打扰注册、进程自检、模块级路由分发(403拦截)
|
# 核心功能: 智能防打扰注册、进程自检、模块级路由分发(403拦截)
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
|
|
||||||
@@ -17,21 +17,27 @@ source "$CONFIG_FILE"
|
|||||||
|
|
||||||
# 默认 Webhook 监听端口
|
# 默认 Webhook 监听端口
|
||||||
AGENT_PORT=${AGENT_PORT:-9527}
|
AGENT_PORT=${AGENT_PORT:-9527}
|
||||||
NODE_NAME=$(hostname | cut -c 1-15)
|
# [v3.4.0 核心] 统一采用防 Markdown 崩溃的中划线连接符
|
||||||
|
IP_HASH=$(echo "${PUBLIC_IP:-127.0.0.1}" | md5sum | cut -c 1-4 | tr 'a-z' 'A-Z')
|
||||||
|
NODE_NAME="$(hostname | cut -c 1-10)-${IP_HASH}"
|
||||||
|
|
||||||
# --- [重点升级 1: 守护进程防冲突自检] ---
|
# --- [重点升级 1: 守护进程防冲突自检] ---
|
||||||
if pgrep -f "webhook.py $AGENT_PORT" > /dev/null; then
|
if pgrep -f "webhook.py $AGENT_PORT" > /dev/null; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 1. [v3.0.1修复] 严格按照 install.sh 锁定的网络协议 (v4/v6) 获取 IP
|
# 1. 尝试获取实时公网 IP
|
||||||
RAW_IP=$(curl -${IP_PREF:-4} -s -m 5 api.ip.sb/ip | tr -d '[:space:]')
|
RAW_IP=$(curl -${IP_PREF:-4} -s -m 5 api.ip.sb/ip | tr -d '[:space:]')
|
||||||
|
|
||||||
# 为新获取到的 v6 自动加方括号,以确保与之前锁定的格式对齐比对
|
# [v3.3.1 修改] 为新获取到的 v6 自动加方括号;如果网络波动没抓到,强制信任本地 config 中的公网面孔
|
||||||
if [[ "$RAW_IP" == *":"* ]] && [[ "$RAW_IP" != *"["* ]]; then
|
if [ -n "$RAW_IP" ]; then
|
||||||
AGENT_IP="[${RAW_IP}]"
|
if [[ "$RAW_IP" == *":"* ]] && [[ "$RAW_IP" != *"["* ]]; then
|
||||||
|
AGENT_IP="[${RAW_IP}]"
|
||||||
|
else
|
||||||
|
AGENT_IP="$RAW_IP"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
AGENT_IP="$RAW_IP"
|
AGENT_IP="${PUBLIC_IP:-${BIND_IP:-Unknown}}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$AGENT_IP" ]; then
|
if [ -n "$AGENT_IP" ]; then
|
||||||
@@ -41,7 +47,8 @@ if [ -n "$AGENT_IP" ]; then
|
|||||||
|
|
||||||
# 只有当这是第一次运行,或者公网 IP 发生变动时,才发送 Telegram 申请
|
# 只有当这是第一次运行,或者公网 IP 发生变动时,才发送 Telegram 申请
|
||||||
if [ "$AGENT_IP" != "$LAST_IP" ]; then
|
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}" \
|
curl -s -m 5 -X POST "${TG_API_URL}" \
|
||||||
-d "chat_id=${CHAT_ID}" \
|
-d "chat_id=${CHAT_ID}" \
|
||||||
@@ -126,8 +133,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 区域纠偏
|
# 路由 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'):
|
if os.path.exists('/opt/ip_sentinel/core/mod_google.sh'):
|
||||||
self.send_response(200)
|
self.send_response(200)
|
||||||
self.send_header("Content-type", "text/plain")
|
self.send_header("Content-type", "text/plain")
|
||||||
@@ -187,8 +206,13 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
|||||||
if lines:
|
if lines:
|
||||||
log_data = html.escape("".join(lines[-15:]))
|
log_data = html.escape("".join(lines[-15:]))
|
||||||
|
|
||||||
node_name = subprocess.check_output(['hostname']).decode('utf-8').strip()[:15]
|
# [v3.4.0 核心] 获取版本与主机名
|
||||||
text_msg = f"📄 <b>[{node_name}] 实时运行日志:</b>\n<pre><code>{log_data}</code></pre>"
|
local_ver = config.get('AGENT_VERSION', '未知')
|
||||||
|
node_hostname = subprocess.check_output(['hostname']).decode('utf-8').strip()[:10]
|
||||||
|
ip_hash = hashlib.md5(config.get('PUBLIC_IP', '127.0.0.1').encode()).hexdigest()[:4].upper()
|
||||||
|
full_node_name = f"{node_hostname}-{ip_hash}"
|
||||||
|
|
||||||
|
text_msg = f"📄 <b>[{full_node_name}] 实时日志 (v{local_ver}):</b>\n<pre><code>{log_data}</code></pre>"
|
||||||
|
|
||||||
data = urllib.parse.urlencode({
|
data = urllib.parse.urlencode({
|
||||||
'chat_id': config.get('CHAT_ID', ''),
|
'chat_id': config.get('CHAT_ID', ''),
|
||||||
@@ -199,7 +223,8 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
|
|||||||
req = urllib.request.Request(
|
req = urllib.request.Request(
|
||||||
config.get('TG_API_URL', ''),
|
config.get('TG_API_URL', ''),
|
||||||
data=data,
|
data=data,
|
||||||
headers={'User-Agent': 'IP-Sentinel-Agent/3.0.4'}
|
# [动态化] 彻底消灭硬编码,使用运行态版本号
|
||||||
|
headers={'User-Agent': f'IP-Sentinel-Agent/{local_ver}'}
|
||||||
)
|
)
|
||||||
urllib.request.urlopen(req, timeout=10)
|
urllib.request.urlopen(req, timeout=10)
|
||||||
|
|
||||||
|
|||||||
656
core/install.sh
656
core/install.sh
@@ -1,8 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
# 脚本名称: install.sh (IP-Sentinel 分布式边缘节点部署脚本 v3.0.3 - Global Nexus)
|
# 脚本名称: install.sh (IP-Sentinel 分布式边缘节点部署脚本 - 动态锚点版)
|
||||||
# 核心功能: 区域选择、模块按需开启、官方机器人一键配置
|
# 核心功能: 战区分组菜单、模块按需开启、官方机器人一键配置、版本状态机路由
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
|
|
||||||
# 你的 GitHub 仓库 Raw 数据直链前缀
|
# 你的 GitHub 仓库 Raw 数据直链前缀
|
||||||
@@ -12,8 +12,19 @@ REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
|||||||
INSTALL_DIR="/opt/ip_sentinel"
|
INSTALL_DIR="/opt/ip_sentinel"
|
||||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||||
|
|
||||||
|
# [核心: 动态提取 Agent 专属版本锚点 (KV 解析法)]
|
||||||
|
TARGET_VERSION=$(curl -s -m 3 "${REPO_RAW_URL}/version.txt" | grep "^AGENT_VERSION=" | cut -d'=' -f2 | tr -d '[:space:]')
|
||||||
|
# 🛡️ 兜底防线:如果网络波动拉取失败,启用内置的安全兜底版本
|
||||||
|
TARGET_VERSION=${TARGET_VERSION:-"3.5.1"}
|
||||||
|
|
||||||
|
# 轻量级版本号比对函数 (例如: version_lt "3.3.1" "3.4.0" 返回 true)
|
||||||
|
version_lt() {
|
||||||
|
test "$(printf '%s\n' "$1" "$2" | sort -V | head -n 1)" = "$1" && test "$1" != "$2"
|
||||||
|
}
|
||||||
|
|
||||||
echo "========================================================"
|
echo "========================================================"
|
||||||
echo " 🛡️ 欢迎使用 IP-Sentinel (边缘节点 Edge Agent)"
|
echo " 🛡️ 欢迎使用 IP-Sentinel (边缘节点 Edge Agent)"
|
||||||
|
echo " 当前安装包版本: v${TARGET_VERSION}"
|
||||||
echo "========================================================"
|
echo "========================================================"
|
||||||
|
|
||||||
# 1. 依赖检查与安装 (新增 python3 用于轻量级 Webhook 服务)
|
# 1. 依赖检查与安装 (新增 python3 用于轻量级 Webhook 服务)
|
||||||
@@ -51,8 +62,31 @@ if [ "$ACTION_CHOICE" == "2" ]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ================== [v3.1.1 新增: 安装前环境纯净度清理 (严格保留日志)] ==================
|
# ================== [v3.2.2 新增: 平滑升级模式嗅探] ==================
|
||||||
echo -e "\n⏳ 正在清理旧版守护进程与冗余任务 (保留历史日志)..."
|
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 及各类看门狗进程,释放端口
|
# 1. 强制超度可能存活的 Webhook 及各类看门狗进程,释放端口
|
||||||
pkill -9 -f "webhook.py" >/dev/null 2>&1 || true
|
pkill -9 -f "webhook.py" >/dev/null 2>&1 || true
|
||||||
pkill -9 -f "agent_daemon.sh" >/dev/null 2>&1 || true
|
pkill -9 -f "agent_daemon.sh" >/dev/null 2>&1 || true
|
||||||
@@ -65,237 +99,290 @@ if crontab -l >/dev/null 2>&1; then
|
|||||||
rm -f /tmp/cron_clean
|
rm -f /tmp/cron_clean
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 3. 抹除旧版核心代码与配置文件,杜绝代码冲突 (精准避开 logs 目录)
|
# 3. 抹除旧版核心代码,杜绝代码冲突 (根据模式分流)
|
||||||
if [ -d "$INSTALL_DIR" ]; then
|
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||||
rm -rf "${INSTALL_DIR}/core" "${INSTALL_DIR}/data" "${INSTALL_DIR}/config.conf" "${INSTALL_DIR}/.last_ip" 2>/dev/null
|
# 升级模式:仅销毁核心引擎,严格保留 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
|
fi
|
||||||
echo -e "\033[32m✅ 环境清理完毕,幽灵进程已肃清!\033[0m"
|
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=()
|
if [ "$UPGRADE_MODE" == "false" ]; then
|
||||||
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
|
|
||||||
|
|
||||||
read -p "请输入选择 [1-$((i-1))] (默认1): " C_SEL
|
# 📍 动态零级菜单:战区(大洲)选择
|
||||||
C_SEL=${C_SEL:-1}
|
echo -e "\n\033[36m📍 【第零级】请选择目标战区 (Continent):\033[0m"
|
||||||
COUNTRY_ID="${COUNTRY_MAP[$C_SEL]}"
|
jq -r '.continents[] | "\(.id)|\(.name)"' /tmp/map.json > /tmp/continents.txt
|
||||||
KEYWORD_FILE="${KEYWORD_MAP[$C_SEL]}"
|
i=1; CONT_MAP=()
|
||||||
REGION_CODE="$COUNTRY_ID" # 兼容旧版的 config.conf
|
while IFS="|" read -r cont_id cont_name; do
|
||||||
|
echo " $i) $cont_name"
|
||||||
# 📍 动态二级菜单:省/州选择
|
CONT_MAP[$i]="$cont_id"
|
||||||
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++))
|
((i++))
|
||||||
done < /tmp/states.txt
|
done < /tmp/continents.txt
|
||||||
read -p "请输入选择 [1-$((i-1))] (默认1): " S_SEL
|
|
||||||
S_SEL=${S_SEL:-1}
|
|
||||||
STATE_ID="${STATE_MAP[$S_SEL]}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 📍 动态三级菜单:城市选择
|
read -p "请输入选择 [1-$((i-1))] (默认1): " CONT_SEL
|
||||||
echo -e "\n\033[36m📍 【第三级】请锁定具体城市节点:\033[0m"
|
CONT_SEL=${CONT_SEL:-1}
|
||||||
jq -r ".countries[] | select(.id==\"$COUNTRY_ID\") | .states[] | select(.id==\"$STATE_ID\") | .cities[] | \"\(.id)|\(.name)\"" /tmp/map.json > /tmp/cities.txt
|
CONT_ID="${CONT_MAP[$CONT_SEL]}"
|
||||||
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 "\n\033[36m📍 【第一级】正在检索 [$CONT_ID] 战区下的国家/地区...\033[0m"
|
||||||
echo -e "\033[32m💡 该区域下仅有单一城市 [$CITY_NAME],已自动锁定。\033[0m"
|
jq -r ".continents[] | select(.id==\"$CONT_ID\") | .countries[] | \"\(.id)|\(.name)|\(.keyword_file)\"" /tmp/map.json > /tmp/countries.txt
|
||||||
else
|
i=1; COUNTRY_MAP=(); KEYWORD_MAP=()
|
||||||
i=1; CITY_MAP=()
|
while IFS="|" read -r c_id c_name k_file; do
|
||||||
while IFS="|" read -r c_id c_name; do
|
|
||||||
echo " $i) $c_name"
|
echo " $i) $c_name"
|
||||||
CITY_MAP[$i]="$c_id"
|
COUNTRY_MAP[$i]="$c_id"
|
||||||
|
KEYWORD_MAP[$i]="$k_file"
|
||||||
((i++))
|
((i++))
|
||||||
done < /tmp/cities.txt
|
done < /tmp/countries.txt
|
||||||
read -p "请输入选择 [1-$((i-1))] (默认1): " CI_SEL
|
|
||||||
CI_SEL=${CI_SEL:-1}
|
|
||||||
CITY_ID="${CITY_MAP[$CI_SEL]}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 清理临时文件
|
read -p "请输入选择 [1-$((i-1))] (默认1): " C_SEL
|
||||||
rm -f /tmp/map.json /tmp/countries.txt /tmp/states.txt /tmp/cities.txt
|
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"
|
echo -e "\n\033[36m📍 【第二级】正在检索 [$COUNTRY_ID] 的行政区数据...\033[0m"
|
||||||
mkdir -p "${INSTALL_DIR}/data/keywords"
|
jq -r ".continents[] | select(.id==\"$CONT_ID\") | .countries[] | select(.id==\"$COUNTRY_ID\") | .states[] | \"\(.id)|\(.name)\"" /tmp/map.json > /tmp/states.txt
|
||||||
mkdir -p "${INSTALL_DIR}/data/regions/${COUNTRY_ID}/${STATE_ID}"
|
STATE_COUNT=$(wc -l < /tmp/states.txt)
|
||||||
mkdir -p "${INSTALL_DIR}/logs"
|
|
||||||
|
|
||||||
# 3. 功能模块前置开关 (按需加载)
|
if [ "$STATE_COUNT" -eq 1 ]; then
|
||||||
echo -e "\n[3/7] 请选择需要开启的养护模块 (按需开启,节省资源):"
|
IFS="|" read -r STATE_ID STATE_NAME < /tmp/states.txt
|
||||||
echo " 1) 📍 仅开启 [Google 区域纠偏] (默认,适合流媒体解锁机位漂移)"
|
echo -e "\033[32m💡 该国家下仅有单一配置 [$STATE_NAME],已自动跃迁。\033[0m"
|
||||||
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"
|
|
||||||
else
|
else
|
||||||
TG_TOKEN="$USER_TOKEN"
|
i=1; STATE_MAP=()
|
||||||
TG_API_URL="https://api.telegram.org/bot${TG_TOKEN}/sendMessage"
|
while IFS="|" read -r s_id s_name; do
|
||||||
echo -e "\033[32m✅ 已记录您的私有机器人 Token。\033[0m"
|
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
|
fi
|
||||||
|
|
||||||
echo -e "\033[33m💡 提示:如果您不知道自己的 Chat ID,可以关注 @userinfobot 获取。\033[0m"
|
# 📍 动态三级菜单:城市选择 (基于战区、国家、州三层过滤)
|
||||||
read -p "请输入你的 Chat ID (与主控一致): " CHAT_ID
|
echo -e "\n\033[36m📍 【第三级】请锁定具体城市节点:\033[0m"
|
||||||
|
jq -r ".continents[] | select(.id==\"$CONT_ID\") | .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)
|
||||||
|
|
||||||
# ================== [v3.0.3 变更: 智能随机高位端口生成系统] ==================
|
if [ "$CITY_COUNT" -eq 1 ]; then
|
||||||
echo -e "\n\033[36m[4.2/7] 正在构建 Webhook 安全通信隧道...\033[0m"
|
IFS="|" read -r CITY_ID CITY_NAME < /tmp/cities.txt
|
||||||
echo -n "🎲 正在探测可用随机端口..."
|
echo -e "\033[32m💡 该区域下仅有单一城市 [$CITY_NAME],已自动锁定。\033[0m"
|
||||||
while true; do
|
else
|
||||||
RANDOM_PORT=$((RANDOM % 55536 + 10000))
|
i=1; CITY_MAP=()
|
||||||
# 同时兼容 ss (新) 和 netstat (旧) 检查端口占用
|
while IFS="|" read -r c_id c_name; do
|
||||||
if ! (ss -tuln 2>/dev/null | grep -q ":$RANDOM_PORT " || netstat -tuln 2>/dev/null | grep -q ":$RANDOM_PORT "); then
|
echo " $i) $c_name"
|
||||||
break
|
CITY_MAP[$i]="$c_id"
|
||||||
fi
|
((i++))
|
||||||
echo -n "."
|
done < /tmp/cities.txt
|
||||||
done
|
read -p "请输入选择 [1-$((i-1))] (默认1): " CI_SEL
|
||||||
echo -e " 完成!"
|
CI_SEL=${CI_SEL:-1}
|
||||||
|
CITY_ID="${CITY_MAP[$CI_SEL]}"
|
||||||
|
fi
|
||||||
|
|
||||||
echo -e "💡 系统为您生成的推荐随机高位端口为: \033[32m$RANDOM_PORT\033[0m"
|
# 清理临时文件 (增加清理 continents.txt)
|
||||||
echo -e "\033[33m(该端口已通过本地占用校验,可直接使用)\033[0m"
|
rm -f /tmp/map.json /tmp/continents.txt /tmp/countries.txt /tmp/states.txt /tmp/cities.txt
|
||||||
|
|
||||||
while true; do
|
# 本地工作目录初始化 (支持 v3.0 的深度层级)
|
||||||
read -p "请输入 Webhook 监听端口 (回车采用推荐, 或手动输入): " INPUT_PORT
|
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"
|
||||||
|
|
||||||
if [ -z "$INPUT_PORT" ]; then
|
# 3. 功能模块前置开关 (按需加载)
|
||||||
AGENT_PORT="$RANDOM_PORT"
|
echo -e "\n[3/7] 请选择需要开启的养护模块 (按需开启,节省资源):"
|
||||||
break
|
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.samanthaestime296.workers.dev"
|
||||||
|
echo -e "\033[32m✅ 已自动连接官方安全网关 (@OmniBeacon_bot)。\033[0m"
|
||||||
|
echo -e "\033[33m👉 请确保您已关注官方机器人并发送过 /start,否则将无法接收消息。\033[0m"
|
||||||
else
|
else
|
||||||
# 校验手动输入的合法性与可用性
|
TG_TOKEN="$USER_TOKEN"
|
||||||
if [[ "$INPUT_PORT" =~ ^[0-9]+$ ]] && [ "$INPUT_PORT" -ge 1 ] && [ "$INPUT_PORT" -le 65535 ]; then
|
TG_API_URL="https://api.telegram.org/bot${TG_TOKEN}/sendMessage"
|
||||||
if (ss -tuln 2>/dev/null | grep -q ":$INPUT_PORT " || netstat -tuln 2>/dev/null | grep -q ":$INPUT_PORT "); then
|
echo -e "\033[32m✅ 已记录您的私有机器人 Token。\033[0m"
|
||||||
echo -e "\033[31m❌ 端口 $INPUT_PORT 已被占用,请重新输入或使用推荐端口。\033[0m"
|
fi
|
||||||
else
|
|
||||||
AGENT_PORT="$INPUT_PORT"
|
echo -e "\033[33m💡 提示:如果您不知道自己的 Chat ID,可以关注 @userinfobot 获取。\033[0m"
|
||||||
break
|
read -p "请输入你的 Chat ID (与主控一致): " CHAT_ID
|
||||||
fi
|
|
||||||
else
|
# ================== [v3.0.3 变更: 智能随机高位端口生成系统] ==================
|
||||||
echo -e "\033[31m❌ 输入非法!端口范围应为 1-65535。\033[0m"
|
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
|
||||||
fi
|
echo -n "."
|
||||||
done
|
done
|
||||||
echo -e "✅ 已锁定 Webhook 通讯端口: \033[32m$AGENT_PORT\033[0m"
|
echo -e " 完成!"
|
||||||
# ====================================================================
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ================== [v3.0.1新增修改 1: 冗余网络栈探测与锚点锁定] ==================
|
echo -e "💡 系统为您生成的推荐随机高位端口为: \033[32m$RANDOM_PORT\033[0m"
|
||||||
echo -e "\n\033[36m[4.5/7] 正在探测本机网络栈与可用出口 (多节点雷达扫描中)...\033[0m"
|
echo -e "\033[33m(该端口已通过本地占用校验,可直接使用)\033[0m"
|
||||||
|
|
||||||
# 引入容灾机制:依次尝试三个不同的 API,拿到有效的 IP 格式就停止
|
while true; do
|
||||||
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:]')
|
read -p "请输入 Webhook 监听端口 (回车采用推荐, 或手动输入): " INPUT_PORT
|
||||||
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:]')
|
|
||||||
|
|
||||||
# 构建动态选项数组
|
if [ -z "$INPUT_PORT" ]; then
|
||||||
IP_OPTIONS=()
|
AGENT_PORT="$RANDOM_PORT"
|
||||||
IP_PROTO=()
|
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
|
||||||
|
|
||||||
[[ -n "$DETECT_V4" ]] && { IP_OPTIONS+=("$DETECT_V4"); IP_PROTO+=("4"); }
|
# ================== [v3.0.1新增修改 1: 冗余网络栈探测与锚点锁定] ==================
|
||||||
[[ -n "$DETECT_V6" ]] && { IP_OPTIONS+=("$DETECT_V6"); IP_PROTO+=("6"); }
|
echo -e "\n\033[36m[4.5/7] 正在探测本机网络栈与可用出口 (多节点雷达扫描中)...\033[0m"
|
||||||
|
|
||||||
if [ ${#IP_OPTIONS[@]} -eq 0 ]; then
|
# 引入容灾机制:依次尝试三个不同的 API,拿到有效的 IP 格式就停止
|
||||||
echo -e "\033[33m⚠️ 雷达受阻:未能自动探测到公网 IP,请手动指定。\033[0m"
|
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:]')
|
||||||
read -p "请输入您要绑定的公网 IP (v4 或 v6): " PUBLIC_IP
|
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:]')
|
||||||
[[ "$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}
|
IP_OPTIONS=()
|
||||||
|
IP_PROTO=()
|
||||||
|
|
||||||
if [ "$IP_CHOICE" -le "${#IP_OPTIONS[@]}" ] && [ "$IP_CHOICE" -gt 0 ]; then
|
[[ -n "$DETECT_V4" ]] && { IP_OPTIONS+=("$DETECT_V4"); IP_PROTO+=("4"); }
|
||||||
idx=$((IP_CHOICE-1))
|
[[ -n "$DETECT_V6" ]] && { IP_OPTIONS+=("$DETECT_V6"); IP_PROTO+=("6"); }
|
||||||
PUBLIC_IP="${IP_OPTIONS[$idx]}"
|
|
||||||
IP_PREF="${IP_PROTO[$idx]}"
|
if [ ${#IP_OPTIONS[@]} -eq 0 ]; then
|
||||||
elif [ "$IP_CHOICE" -eq "$CUSTOM_OPT" ]; then
|
echo -e "\033[33m⚠️ 雷达受阻:未能自动探测到公网 IP,请手动指定。\033[0m"
|
||||||
read -p "请输入您要绑定的公网 IP (v4 或 v6): " PUBLIC_IP
|
read -p "请输入您要绑定的公网 IP (v4 或 v6): " PUBLIC_IP
|
||||||
[[ "$PUBLIC_IP" == *":"* ]] && IP_PREF="6" || IP_PREF="4"
|
[[ "$PUBLIC_IP" == *":"* ]] && IP_PREF="6" || IP_PREF="4"
|
||||||
else
|
else
|
||||||
# 兜底:乱输就默认选第一个
|
echo "📍 发现可用出口 IP,请选择要注册与养护的锚点:"
|
||||||
PUBLIC_IP="${IP_OPTIONS[0]}"
|
for i in "${!IP_OPTIONS[@]}"; do
|
||||||
IP_PREF="${IP_PROTO[0]}"
|
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
|
||||||
fi
|
|
||||||
|
|
||||||
# 终极修复:为 IPv6 自动穿上防护装甲(方括号),解决 Master 拼接 URL 报错问题
|
# ================== [v3.3.1 核心重构: 身份剥离与双栈实弹嗅探] ==================
|
||||||
if [[ "$PUBLIC_IP" == *":"* ]] && [[ "$PUBLIC_IP" != *"["* ]]; then
|
# 1. 固化对外通讯身份 (自动穿透方括号护甲)
|
||||||
BIND_IP="[${PUBLIC_IP}]"
|
if [[ "$PUBLIC_IP" == *":"* ]] && [[ "$PUBLIC_IP" != *"["* ]]; then
|
||||||
else
|
SAFE_PUBLIC_IP="[${PUBLIC_IP}]"
|
||||||
BIND_IP="$PUBLIC_IP"
|
else
|
||||||
fi
|
SAFE_PUBLIC_IP="$PUBLIC_IP"
|
||||||
echo -e "\033[32m✅ 哨兵锚点已永久锁定至: $BIND_IP\033[0m"
|
fi
|
||||||
# ========================================================================
|
|
||||||
|
|
||||||
# 5. 远程拉取冷数据并解析固化
|
# 2. 实弹打靶测试 (NAT 环境嗅探与双栈自适应)
|
||||||
echo -e "\n[5/7] 正在从云端数据仓库拉取 [${CITY_NAME}] 节点的底层规则..."
|
echo -n "🕵️ 正在进行出站链路试射 (NAT环境与双栈嗅探)..."
|
||||||
REGION_JSON_FILE="${INSTALL_DIR}/data/regions/${COUNTRY_ID}/${STATE_ID}/${CITY_ID}.json"
|
RAW_TEST_IP=$(echo "$SAFE_PUBLIC_IP" | tr -d '[]')
|
||||||
curl -sL "${REPO_RAW_URL}/data/regions/${COUNTRY_ID}/${STATE_ID}/${CITY_ID}.json" -o "$REGION_JSON_FILE"
|
|
||||||
|
|
||||||
if [ ! -s "$REGION_JSON_FILE" ]; then
|
# 智能切换靶机:V6 机器打 Cloudflare V6 节点,V4 机器打 1.1.1.1
|
||||||
echo "❌ 拉取或解析规则失败!请检查 Forgejo 仓库是否公开或网络是否畅通。"
|
if [[ "$RAW_TEST_IP" == *":"* ]]; then
|
||||||
exit 1
|
TEST_TARGET="https://[2606:4700:4700::1111]"
|
||||||
fi
|
else
|
||||||
|
TEST_TARGET="https://1.1.1.1"
|
||||||
|
fi
|
||||||
|
|
||||||
# 使用 jq 提取 JSON 里的核心值
|
# 执行实弹试射
|
||||||
REGION_NAME=$(jq -r '.region_name' "$REGION_JSON_FILE")
|
if curl --interface "$RAW_TEST_IP" -sI -m 3 "$TEST_TARGET" >/dev/null 2>&1; then
|
||||||
BASE_LAT=$(jq -r '.google_module.base_lat' "$REGION_JSON_FILE")
|
echo -e " \033[32m✅ 原生直连,物理网卡死锁已激活。\033[0m"
|
||||||
BASE_LON=$(jq -r '.google_module.base_lon' "$REGION_JSON_FILE")
|
BIND_IP="$SAFE_PUBLIC_IP"
|
||||||
LANG_PARAMS=$(jq -r '.google_module.lang_params' "$REGION_JSON_FILE")
|
else
|
||||||
VALID_URL_SUFFIX=$(jq -r '.google_module.valid_url_suffix' "$REGION_JSON_FILE")
|
echo -e " \033[33m⚠️ 发现 NAT/虚拟路由架构,自动卸除网卡枷锁,交由内核路由。\033[0m"
|
||||||
|
BIND_IP=""
|
||||||
|
fi
|
||||||
|
echo -e "\033[32m✅ 哨兵对外联络点已永久锁定至: $SAFE_PUBLIC_IP\033[0m"
|
||||||
|
# ========================================================================
|
||||||
|
|
||||||
# 写入本地静态配置文件
|
# 5. 远程拉取冷数据并解析固化
|
||||||
cat > "$CONFIG_FILE" << EOF
|
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
|
||||||
|
|
||||||
|
# 使用 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")
|
||||||
|
|
||||||
|
# 写入本地静态配置文件 (v3.4.0 引入版本锚点)
|
||||||
|
cat > "$CONFIG_FILE" << EOF
|
||||||
# IP-Sentinel 本地固化配置 (生成时间: $(date '+%Y-%m-%d %H:%M:%S'))
|
# IP-Sentinel 本地固化配置 (生成时间: $(date '+%Y-%m-%d %H:%M:%S'))
|
||||||
|
AGENT_VERSION="$TARGET_VERSION"
|
||||||
REGION_CODE="$REGION_CODE"
|
REGION_CODE="$REGION_CODE"
|
||||||
REGION_NAME="$REGION_NAME"
|
REGION_NAME="$REGION_NAME"
|
||||||
BASE_LAT="$BASE_LAT"
|
BASE_LAT="$BASE_LAT"
|
||||||
@@ -314,17 +401,64 @@ AGENT_PORT="$AGENT_PORT"
|
|||||||
INSTALL_DIR="$INSTALL_DIR"
|
INSTALL_DIR="$INSTALL_DIR"
|
||||||
LOG_FILE="${INSTALL_DIR}/logs/sentinel.log"
|
LOG_FILE="${INSTALL_DIR}/logs/sentinel.log"
|
||||||
|
|
||||||
# [v3.0.1新增修改 2: 网络栈锚点锁定配置,供其他脚本读取]
|
# [v3.3.1修改: 双核身份剥离配置]
|
||||||
IP_PREF="$IP_PREF"
|
IP_PREF="$IP_PREF"
|
||||||
|
PUBLIC_IP="$SAFE_PUBLIC_IP"
|
||||||
BIND_IP="$BIND_IP"
|
BIND_IP="$BIND_IP"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# ================== [v3.0.3 变更: 敏感配置文件权限收敛] ==================
|
# ================== [v3.0.3 变更: 敏感配置文件权限收敛] ==================
|
||||||
chmod 600 "$CONFIG_FILE"
|
chmod 600 "$CONFIG_FILE"
|
||||||
# ====================================================================
|
# ====================================================================
|
||||||
|
|
||||||
|
fi
|
||||||
|
# 🛑 拦截块结束 (全套交互配置跳过完毕)
|
||||||
|
|
||||||
|
# ================== [v3.3.1 核心修复: 老节点配置无损热迁移] ==================
|
||||||
|
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||||
|
if ! grep -q "PUBLIC_IP=" "$CONFIG_FILE"; then
|
||||||
|
echo -e "\n🔄 [平滑迁移] 正在对老节点进行 v3.3.1 双核身份架构升级..."
|
||||||
|
|
||||||
|
# 重新抓取公网面孔 (应对老节点 BIND_IP 可能已被手动清空的情况)
|
||||||
|
MIGRATE_IP=$(curl -${IP_PREF:-4} -s -m 5 api.ip.sb/ip | tr -d '[:space:]')
|
||||||
|
[[ "$MIGRATE_IP" == *":"* ]] && [[ "$MIGRATE_IP" != *"["* ]] && MIGRATE_IP="[${MIGRATE_IP}]"
|
||||||
|
|
||||||
|
echo -n "🕵️ 正在进行补发链路试射 (NAT与双栈嗅探)..."
|
||||||
|
RAW_TEST_IP=$(echo "$MIGRATE_IP" | tr -d '[]')
|
||||||
|
if [[ "$RAW_TEST_IP" == *":"* ]]; then
|
||||||
|
TEST_TARGET="https://[2606:4700:4700::1111]"
|
||||||
|
else
|
||||||
|
TEST_TARGET="https://1.1.1.1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if curl --interface "$RAW_TEST_IP" -sI -m 3 "$TEST_TARGET" >/dev/null 2>&1; then
|
||||||
|
echo -e " \033[32m✅ 原生直连,网卡死锁已继承。\033[0m"
|
||||||
|
NEW_BIND_IP="$MIGRATE_IP"
|
||||||
|
else
|
||||||
|
echo -e " \033[33m⚠️ 发现 NAT 架构,已自动卸除老版本的物理枷锁。\033[0m"
|
||||||
|
NEW_BIND_IP=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 动态修改旧配置文件 (更新 BIND_IP,追加 PUBLIC_IP)
|
||||||
|
sed -i "s/^BIND_IP=.*/BIND_IP=\"$NEW_BIND_IP\"/" "$CONFIG_FILE"
|
||||||
|
echo "PUBLIC_IP=\"$MIGRATE_IP\"" >> "$CONFIG_FILE"
|
||||||
|
|
||||||
|
# 刷新当前安装脚本的环境变量,防止底部代码报错
|
||||||
|
SAFE_PUBLIC_IP="$MIGRATE_IP"
|
||||||
|
BIND_IP="$NEW_BIND_IP"
|
||||||
|
else
|
||||||
|
# 如果是未来再升级,配置文件已是最新,直接提取变量供安装脚本尾部使用
|
||||||
|
SAFE_PUBLIC_IP=$(grep "^PUBLIC_IP=" "$CONFIG_FILE" | cut -d'"' -f2)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# ========================================================================
|
||||||
|
|
||||||
# 6. 拉取全套组件 (按需下载,绝不浪费空间)
|
# 6. 拉取全套组件 (按需下载,绝不浪费空间)
|
||||||
echo -e "\n[6/7] 正在根据模块开关部署核心引擎与热数据..."
|
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/runner.sh" -o "${INSTALL_DIR}/core/runner.sh"
|
||||||
curl -sL "${REPO_RAW_URL}/core/updater.sh" -o "${INSTALL_DIR}/core/updater.sh"
|
curl -sL "${REPO_RAW_URL}/core/updater.sh" -o "${INSTALL_DIR}/core/updater.sh"
|
||||||
@@ -336,8 +470,13 @@ curl -sL "${REPO_RAW_URL}/data/user_agents.txt" -o "${INSTALL_DIR}/data/user_age
|
|||||||
# 动态按需组件
|
# 动态按需组件
|
||||||
if [ "$ENABLE_GOOGLE" == "true" ]; then
|
if [ "$ENABLE_GOOGLE" == "true" ]; then
|
||||||
curl -sL "${REPO_RAW_URL}/core/mod_google.sh" -o "${INSTALL_DIR}/core/mod_google.sh"
|
curl -sL "${REPO_RAW_URL}/core/mod_google.sh" -o "${INSTALL_DIR}/core/mod_google.sh"
|
||||||
# 根据 map.json 动态匹配的词库文件进行下载
|
# [v3.2.2 修复] 动态匹配词库下载逻辑
|
||||||
curl -sL "${REPO_RAW_URL}/data/keywords/${KEYWORD_FILE}" -o "${INSTALL_DIR}/data/keywords/${KEYWORD_FILE}"
|
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
|
fi
|
||||||
|
|
||||||
if [ "$ENABLE_TRUST" == "true" ]; then
|
if [ "$ENABLE_TRUST" == "true" ]; then
|
||||||
@@ -352,14 +491,22 @@ crontab -l 2>/dev/null | grep -v "ip_sentinel" > /tmp/cron_backup
|
|||||||
|
|
||||||
# 核心养护模块: 每 30 分钟触发一次
|
# 核心养护模块: 每 30 分钟触发一次
|
||||||
echo "*/30 * * * * ${INSTALL_DIR}/core/runner.sh >/dev/null 2>&1" >> /tmp/cron_backup
|
echo "*/30 * * * * ${INSTALL_DIR}/core/runner.sh >/dev/null 2>&1" >> /tmp/cron_backup
|
||||||
# 养料更新模块: 每周日凌晨 3 点静默去云端更新热数据
|
# 养料更新模块: (v3.3.0升级) 每天凌晨 3 点触发,由中枢自动进行分频调度
|
||||||
echo "0 3 * * 0 ${INSTALL_DIR}/core/updater.sh >/dev/null 2>&1" >> /tmp/cron_backup
|
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 与战报任务
|
# 如果配置了联控,启动 Webhook 与战报任务
|
||||||
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||||
# 每天早上 8 点发送昨天的统计战报
|
# 每天早上 8 点发送昨天的统计战报
|
||||||
echo "0 8 * * * ${INSTALL_DIR}/core/tg_report.sh >/dev/null 2>&1" >> /tmp/cron_backup
|
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 "$SAFE_PUBLIC_IP" > "${INSTALL_DIR}/core/.last_ip"
|
||||||
|
|
||||||
# 双保险守护进程看门狗
|
# 双保险守护进程看门狗
|
||||||
echo "@reboot nohup bash ${INSTALL_DIR}/core/agent_daemon.sh >/dev/null 2>&1 &" >> /tmp/cron_backup
|
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
|
echo "* * * * * nohup bash ${INSTALL_DIR}/core/agent_daemon.sh >/dev/null 2>&1 &" >> /tmp/cron_backup
|
||||||
@@ -371,38 +518,85 @@ fi
|
|||||||
crontab /tmp/cron_backup
|
crontab /tmp/cron_backup
|
||||||
rm -f /tmp/cron_backup
|
rm -f /tmp/cron_backup
|
||||||
|
|
||||||
|
# ================== [v3.4.0 核心: 状态机驱动的热更新路由] ==================
|
||||||
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
|
||||||
echo -e "\n📡 正在向指挥部发送注册暗号..."
|
# 构造当前节点的唯一代号 (v3.3.2引入的防撞甲,已修正连接符)
|
||||||
|
IP_HASH=$(echo "${SAFE_PUBLIC_IP:-127.0.0.1}" | md5sum | cut -c 1-4 | tr 'a-z' 'A-Z')
|
||||||
|
NODE_NAME="$(hostname | cut -c 1-10)-${IP_HASH}"
|
||||||
|
|
||||||
# [v3.0.1新增修改 3: 删除原来的 curl 取 IP,直接使用我们上方锁定的 BIND_IP]
|
REG_MSG="#REGISTER#|${REGION_CODE}|${NODE_NAME}|${SAFE_PUBLIC_IP}|${AGENT_PORT}"
|
||||||
# 并提前写入 IP 缓存,彻底阻断 agent_daemon 首次启动时的重复推送
|
|
||||||
echo "$BIND_IP" > "${INSTALL_DIR}/core/.last_ip"
|
|
||||||
|
|
||||||
# 构造注册暗号 (使用带 [] 装甲的 BIND_IP,防止 Master 端解析错误)
|
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||||
NODE_NAME=$(hostname | cut -c 1-15)
|
# 读取本地老版本号,如果没有则视为远古版本 v3.3.1
|
||||||
REG_MSG="#REGISTER#|${NODE_NAME}|${BIND_IP}|${AGENT_PORT}"
|
OLD_VERSION=$(grep "^AGENT_VERSION=" "$CONFIG_FILE" | cut -d'"' -f2)
|
||||||
|
[ -z "$OLD_VERSION" ] && OLD_VERSION="3.3.1"
|
||||||
|
|
||||||
# 执行主动推送
|
# [路由表 1]: 跨代兼容 (老版本 < v3.3.2)
|
||||||
PUSH_RESULT=$(curl -s -X POST "${TG_API_URL}" \
|
# 必须强制下发带有 #REGISTER# 的警告,引导长官重新同步哈希身份
|
||||||
-d "chat_id=${CHAT_ID}" \
|
if version_lt "$OLD_VERSION" "3.3.2"; then
|
||||||
-d "parse_mode=Markdown" \
|
echo -e "\n📡 [路由枢纽] 正在执行跨代架构重组 (v${OLD_VERSION} -> v${TARGET_VERSION})..."
|
||||||
-d "text=✨ *IP-Sentinel 部署成功!*
|
curl -s -X POST "${TG_API_URL}" \
|
||||||
|
-d "chat_id=${CHAT_ID}" \
|
||||||
|
-d "parse_mode=Markdown" \
|
||||||
|
-d "text=✨ *IP-Sentinel 引擎热更新完成!*
|
||||||
|
📍 节点:\`${NODE_NAME}\`
|
||||||
|
🌐 IP:\`${SAFE_PUBLIC_IP}\`
|
||||||
|
🚀 状态:v${TARGET_VERSION} OTA 动态活体引擎已部署
|
||||||
|
|
||||||
|
⚠️ *战区架构已重组,请务必点击下方指令并发送,以同步新的防撞档案:*
|
||||||
|
\`${REG_MSG}\`" >/dev/null 2>&1
|
||||||
|
echo -e "\033[32m✅ 升级通知已推送!请前往 TG 点击注册指令完成身份同步!\033[0m"
|
||||||
|
|
||||||
|
# [路由表 2]: 现代静默升级 (老版本 >= v3.3.2)
|
||||||
|
else
|
||||||
|
echo -e "\n📡 [路由枢纽] 正在执行静默平滑升级 (v${OLD_VERSION} -> v${TARGET_VERSION})..."
|
||||||
|
curl -s -X POST "${TG_API_URL}" \
|
||||||
|
-d "chat_id=${CHAT_ID}" \
|
||||||
|
-d "parse_mode=Markdown" \
|
||||||
|
-d "text=✨ *IP-Sentinel 引擎热更新完成!*
|
||||||
|
📍 节点:\`${NODE_NAME}\`
|
||||||
|
🌐 IP:\`${SAFE_PUBLIC_IP}\`
|
||||||
|
🚀 状态:v${TARGET_VERSION} OTA 动态活体引擎已部署" >/dev/null 2>&1
|
||||||
|
echo -e "\033[32m✅ 升级成功通知已推送到您的 Telegram!\033[0m"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# [清理遗留垃圾并刷新版本号]
|
||||||
|
sed -i '/^NAME_HASHED=/d' "$CONFIG_FILE" 2>/dev/null # 抹除上个版本的临时基因锁
|
||||||
|
if grep -q "^AGENT_VERSION=" "$CONFIG_FILE"; then
|
||||||
|
sed -i "s/^AGENT_VERSION=.*/AGENT_VERSION=\"$TARGET_VERSION\"/" "$CONFIG_FILE"
|
||||||
|
else
|
||||||
|
echo "AGENT_VERSION=\"$TARGET_VERSION\"" >> "$CONFIG_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
# [全新安装路由]
|
||||||
|
echo -e "\n📡 正在向指挥部发送注册暗号..."
|
||||||
|
PUSH_RESULT=$(curl -s -X POST "${TG_API_URL}" \
|
||||||
|
-d "chat_id=${CHAT_ID}" \
|
||||||
|
-d "parse_mode=Markdown" \
|
||||||
|
-d "text=✨ *IP-Sentinel 部署成功!*
|
||||||
📍 区域:${REGION_NAME}
|
📍 区域:${REGION_NAME}
|
||||||
🌐 IP:${BIND_IP}
|
🌐 IP:${SAFE_PUBLIC_IP}
|
||||||
🔌 端口:${AGENT_PORT}
|
🔌 端口:${AGENT_PORT}
|
||||||
|
|
||||||
🔑 *请点击下方指令复制并回复给机器人:*
|
🔑 *请点击下方指令复制并回复给机器人:*
|
||||||
\`${REG_MSG}\`")
|
\`${REG_MSG}\`")
|
||||||
|
|
||||||
if echo "$PUSH_RESULT" | grep -q '"ok":true'; then
|
if echo "$PUSH_RESULT" | grep -q '"ok":true'; then
|
||||||
echo -e "\033[32m✅ 注册信息已推送到您的 Telegram,请按指令完成最终激活!\033[0m"
|
echo -e "\033[32m✅ 注册信息已推送到您的 Telegram,请按指令完成最终激活!\033[0m"
|
||||||
else
|
else
|
||||||
echo -e "\033[31m❌ 消息推送失败,请检查 Chat ID 是否正确或是否已关注机器人。\033[0m"
|
echo -e "\033[31m❌ 消息推送失败,请检查 Chat ID 是否正确或是否已关注机器人。\033[0m"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
# =========================================================================
|
||||||
|
|
||||||
echo "========================================================"
|
echo "========================================================"
|
||||||
echo "🎉 边缘节点 (Agent) 部署流程彻底完成!"
|
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||||
|
echo "🎉 边缘节点 (Agent) 平滑热更新已彻底完成!"
|
||||||
|
else
|
||||||
|
echo "🎉 边缘节点 (Agent) 部署流程彻底完成!"
|
||||||
|
fi
|
||||||
echo "📍 你的本地守护区域已锁定为: $REGION_NAME"
|
echo "📍 你的本地守护区域已锁定为: $REGION_NAME"
|
||||||
echo "⚙️ 哨兵现已开启 [每30分钟] 的高频高拟真养护循环。"
|
echo "⚙️ 哨兵现已开启 [每30分钟] 的高频高拟真养护循环。"
|
||||||
if [[ -n "$TG_TOKEN" ]]; then
|
if [[ -n "$TG_TOKEN" ]]; then
|
||||||
@@ -415,7 +609,12 @@ if [[ -n "$TG_TOKEN" ]]; then
|
|||||||
elif command -v firewall-cmd >/dev/null 2>&1 && systemctl is-active firewalld | grep -qw active; 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"
|
FW_MSG="firewall-cmd --zone=public --add-port=$AGENT_PORT/tcp --permanent && firewall-cmd --reload"
|
||||||
elif command -v iptables >/dev/null 2>&1; then
|
elif command -v iptables >/dev/null 2>&1; then
|
||||||
FW_MSG="iptables -I INPUT -p tcp --dport $AGENT_PORT -j ACCEPT"
|
# 智能双栈雷达:根据对外公网 IP 属性,动态下发对应的防火墙放行指令
|
||||||
|
if [[ "$SAFE_PUBLIC_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
|
fi
|
||||||
|
|
||||||
echo -e "\033[33m⚠️ 警告:请务必确保本机及云服务商安全组放行了 TCP $AGENT_PORT 端口!\033[0m"
|
echo -e "\033[33m⚠️ 警告:请务必确保本机及云服务商安全组放行了 TCP $AGENT_PORT 端口!\033[0m"
|
||||||
@@ -427,3 +626,14 @@ if [[ -n "$TG_TOKEN" ]]; then
|
|||||||
fi
|
fi
|
||||||
echo "🗑️ 若未来需卸载,可重新运行本脚本选择[2]或执行: bash ${INSTALL_DIR}/core/uninstall.sh"
|
echo "🗑️ 若未来需卸载,可重新运行本脚本选择[2]或执行: bash ${INSTALL_DIR}/core/uninstall.sh"
|
||||||
echo "========================================================"
|
echo "========================================================"
|
||||||
|
|
||||||
|
# ================== [v3.1.2 新增: 玻璃房透明装机统计] ==================
|
||||||
|
echo -e "\n📡 正在向开源社区汇报装机量 (完全匿名,不收集IP)..."
|
||||||
|
AGENT_COUNT=$(curl -s -m 3 "https://ip-sentinel-count.samanthaestime296.workers.dev/ping/agent" || echo "")
|
||||||
|
|
||||||
|
if [ -n "$AGENT_COUNT" ] && [[ "$AGENT_COUNT" =~ ^[0-9]+$ ]]; then
|
||||||
|
echo -e "\033[32m✅ 感谢您成为全球第 ${AGENT_COUNT} 名 IP-Sentinel 哨兵!\033[0m"
|
||||||
|
else
|
||||||
|
echo -e "\033[32m✅ 感谢您加入 IP-Sentinel 哨兵阵列!\033[0m"
|
||||||
|
fi
|
||||||
|
echo -e "\n"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
# 脚本名称: mod_google.sh (Google 业务逻辑模块)
|
# 脚本名称: mod_google.sh (Google 业务逻辑模块 - 动态锚点版)
|
||||||
# 核心功能: 执行坐标微抖动、模拟真实阅读时长、会话行为拉伸
|
# 核心功能: 执行坐标微抖动、模拟真实阅读时长、会话行为拉伸
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
|
|
||||||
@@ -16,11 +16,15 @@ else
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 容错机制:如果父进程没有传递 log 函数,则本地定义一个作为 fallback
|
# 容错机制:如果父进程没有传递 log 函数,则本地定义一个作为 fallback (v3.4.0 引入版本探针)
|
||||||
if ! type log >/dev/null 2>&1; then
|
if ! type log >/dev/null 2>&1; then
|
||||||
log() {
|
log() {
|
||||||
|
# [v3.4.0 核心] 提取当前配置中的版本锚点
|
||||||
|
local local_ver="${AGENT_VERSION:-未知}"
|
||||||
|
|
||||||
mkdir -p "${INSTALL_DIR}/logs"
|
mkdir -p "${INSTALL_DIR}/logs"
|
||||||
printf "[$(date '+%Y-%m-%d %H:%M:%S')] [%-5s] [%-7s] [%s] %s\n" "$2" "$1" "$REGION_CODE" "$3" >> "${INSTALL_DIR}/logs/sentinel.log"
|
# 统一日志格式,注入 [版本号] 追踪标识
|
||||||
|
printf "[$(date '+%Y-%m-%d %H:%M:%S')] [v%-5s] [%-5s] [%-7s] [%s] %s\n" "$local_ver" "$2" "$1" "$REGION_CODE" "$3" >> "${INSTALL_DIR}/logs/sentinel.log"
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -48,11 +52,32 @@ get_random_coord() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# --- [环境初始化] ---
|
# --- [环境初始化] ---
|
||||||
# [v3.0.2修复] 直接读取系统已锁定的锚点 IP,彻底杜绝“获取IP失败”及隧道偏移
|
# [v3.3.1修改] 优先读取对外公网面孔作为哈希种子,兼容 NAT 机的空 BIND_IP
|
||||||
CURRENT_IP="${BIND_IP:-Unknown}"
|
CURRENT_IP="${PUBLIC_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 公里内随机生成本次上网的“固定咖啡馆”坐标
|
# 位置锁定:在基准点(比如东京新宿)附近 3 公里内随机生成本次上网的“固定咖啡馆”坐标
|
||||||
SESSION_BASE_LAT=$(get_random_coord $BASE_LAT 270)
|
SESSION_BASE_LAT=$(get_random_coord $BASE_LAT 270)
|
||||||
SESSION_BASE_LON=$(get_random_coord $BASE_LON 270)
|
SESSION_BASE_LON=$(get_random_coord $BASE_LON 270)
|
||||||
@@ -64,6 +89,25 @@ log "$MODULE_NAME" "INFO " "当前出网 IP: $CURRENT_IP"
|
|||||||
log "$MODULE_NAME" "INFO " "设备指纹锁定: ${SESSION_UA:0:45}..."
|
log "$MODULE_NAME" "INFO " "设备指纹锁定: ${SESSION_UA:0:45}..."
|
||||||
log "$MODULE_NAME" "INFO " "虚拟驻留坐标: $SESSION_BASE_LAT, $SESSION_BASE_LON"
|
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
|
for ((i=1; i<=TOTAL_ACTIONS; i++)); do
|
||||||
# 模拟真实移动设备拿在手里时的 GPS 信号微抖动 (范围约 10 米)
|
# 模拟真实移动设备拿在手里时的 GPS 信号微抖动 (范围约 10 米)
|
||||||
@@ -77,21 +121,22 @@ for ((i=1; i<=TOTAL_ACTIONS; i++)); do
|
|||||||
# 随机选择一种上网行为
|
# 随机选择一种上网行为
|
||||||
ACTION_TYPE=$((1 + RANDOM % 4))
|
ACTION_TYPE=$((1 + RANDOM % 4))
|
||||||
|
|
||||||
|
# [V3.2.1 热修复] 注入 $CURL_BIND_OPT 与 $DYNAMIC_IP_PREF 协议自适应
|
||||||
case $ACTION_TYPE in
|
case $ACTION_TYPE in
|
||||||
1) # 搜索行为
|
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}")
|
"https://www.google.com/search?q=${ENCODED_KEY}&${LANG_PARAMS}")
|
||||||
;;
|
;;
|
||||||
2) # 浏览本土新闻
|
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}")
|
"https://news.google.com/home?${LANG_PARAMS}")
|
||||||
;;
|
;;
|
||||||
3) # 地图坐标查询
|
3) # 地图坐标查询
|
||||||
CODE=$(curl -${IP_PREF:-4} -m 15 -s -o /dev/null -w "%{http_code}" -A "$SESSION_UA" \
|
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}")
|
"https://www.google.com/maps/search/$${ENCODED_KEY}/@${ACTION_LAT},${ACTION_LON},17z?${LANG_PARAMS}")
|
||||||
;;
|
;;
|
||||||
4) # 触发移动端系统底层位置检测像素
|
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")
|
"https://connectivitycheck.gstatic.com/generate_204")
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -107,16 +152,45 @@ for ((i=1; i<=TOTAL_ACTIONS; i++)); do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# --- [结果纠偏自检] ---
|
# --- [结果纠偏自检 (V3.2.1 高精度容错版)] ---
|
||||||
# 去掉所有语言参数,进行一次最干净的直连测试 (强制遵循锚点协议)
|
# [V3.2.1 热修复] 探针同样应用 $DYNAMIC_IP_PREF 协议自适应
|
||||||
FINAL_URL=$(curl -${IP_PREF:-4} -m 15 -s -L -o /dev/null -w "%{url_effective}" https://www.google.com)
|
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
|
# 分离状态码与 URL
|
||||||
STATUS="✅ 目标区域达成 ($VALID_URL_SUFFIX)"
|
PROBE_CODE=$(echo "$PROBE_RESULT" | cut -d'|' -f1)
|
||||||
elif [[ "$FINAL_URL" == *"google.com.hk"* ]]; then
|
FINAL_URL=$(echo "$PROBE_RESULT" | cut -d'|' -f2)
|
||||||
STATUS="❌ 判定为送中区 (CN/HK)"
|
|
||||||
|
# 0. 致命拦截:网络断开、DNS 解析失败或严重超时
|
||||||
|
if [ "$PROBE_CODE" == "000" ] || [ -z "$FINAL_URL" ]; then
|
||||||
|
STATUS="🚨 探针失效 (网络阻断或底层路由异常)"
|
||||||
else
|
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
|
fi
|
||||||
|
|
||||||
log "$MODULE_NAME" "SCORE" "自检结论: $STATUS"
|
log "$MODULE_NAME" "SCORE" "自检结论: $STATUS"
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
# 脚本名称: mod_trust.sh (IP 信用净化模块 V2.0 数据解耦版)
|
# 脚本名称: mod_trust.sh (IP 信用净化模块 - 动态锚点版)
|
||||||
# 核心功能: 动态读取云端/本地 JSON 规则池,模拟访问高权重网站稀释恶意流量
|
# 核心功能: 动态扫描本地 LBS 冷数据,提取权威白名单,执行流量净化
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
|
|
||||||
INSTALL_DIR="/opt/ip_sentinel"
|
INSTALL_DIR="/opt/ip_sentinel"
|
||||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||||
UA_FILE="${INSTALL_DIR}/data/user_agents.txt"
|
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. 基础环境校验
|
# 1. 基础环境校验
|
||||||
[ ! -f "$CONFIG_FILE" ] && exit 1
|
[ ! -f "$CONFIG_FILE" ] && exit 1
|
||||||
@@ -16,11 +19,14 @@ source "$CONFIG_FILE"
|
|||||||
|
|
||||||
REGION=${REGION_CODE:-"US"}
|
REGION=${REGION_CODE:-"US"}
|
||||||
LOG_FILE="${INSTALL_DIR}/logs/sentinel.log"
|
LOG_FILE="${INSTALL_DIR}/logs/sentinel.log"
|
||||||
REGION_JSON_FILE="${INSTALL_DIR}/data/regions/${REGION}.json"
|
|
||||||
|
|
||||||
# 2. 动态获取配置 (解耦核心)
|
# 2. 动态获取配置 (V3 拓扑自适应与兜底)
|
||||||
# 兼容旧节点:如果本地没有 json,自动拉取最新的云端配置 (强制遵循锚点协议)
|
# 利用 find 穿透多级子目录,自动抓取安装时落地的那份专属 json 文件
|
||||||
if [ ! -f "$REGION_JSON_FILE" ]; then
|
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"
|
mkdir -p "${INSTALL_DIR}/data/regions"
|
||||||
curl -${IP_PREF:-4} -sL "${REPO_RAW_URL}/data/regions/${REGION}.json" -o "$REGION_JSON_FILE"
|
curl -${IP_PREF:-4} -sL "${REPO_RAW_URL}/data/regions/${REGION}.json" -o "$REGION_JSON_FILE"
|
||||||
fi
|
fi
|
||||||
@@ -35,19 +41,47 @@ if [ ${#TRUST_URLS[@]} -eq 0 ]; then
|
|||||||
TRUST_URLS=("https://en.wikipedia.org/wiki/Special:Random" "https://www.apple.com/" "https://www.microsoft.com/")
|
TRUST_URLS=("https://en.wikipedia.org/wiki/Special:Random" "https://www.apple.com/" "https://www.microsoft.com/")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 3. 日志规范化
|
# 3. 日志规范化 (v3.4.0 引入版本探针)
|
||||||
log_msg() {
|
log_msg() {
|
||||||
local TYPE=$1
|
local TYPE=$1
|
||||||
local MSG=$2
|
local MSG=$2
|
||||||
local TIME=$(date "+%Y-%m-%d %H:%M:%S")
|
local TIME=$(date "+%Y-%m-%d %H:%M:%S")
|
||||||
echo "[$TIME] [$TYPE] [Trust ] [$REGION] $MSG" | tee -a "$LOG_FILE"
|
# [v3.4.0 核心] 提取当前配置中的版本锚点
|
||||||
|
local local_ver="${AGENT_VERSION:-未知}"
|
||||||
|
|
||||||
|
# 日志格式注入 [版本号] 追踪标识,保持对齐
|
||||||
|
echo "[$TIME] [v%-5s] [%-5s] [Trust ] [$REGION] $MSG" | sed "s/%-5s/$local_ver/;s/%-5s/$TYPE/" | tee -a "$LOG_FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
# 4. 锁定单次会话指纹
|
# 4. 锁定单次会话指纹
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
# [V3.1.5] 哈希锚定法 (Hash-Seeded Persona)
|
||||||
|
# 利用 IP 算力固定 3 个永久化专属指纹,破除僵尸网络同质化特征
|
||||||
|
# -----------------------------------------------------------
|
||||||
if [ -f "$UA_FILE" ]; then
|
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
|
||||||
|
# [v3.3.1修改] 优先使用固化的公网 IP 作为哈希种子,防止 NAT 节点指纹同质化
|
||||||
|
SEED=$(echo -n "${PUBLIC_IP:-${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
|
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
|
fi
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
@@ -57,6 +91,25 @@ log_msg "START" "========== 启动区域 IP 信用净化会话 =========="
|
|||||||
log_msg "INFO " "已载入 [${REGION}] 区域白名单,配置库条目: ${#TRUST_URLS[@]} 个"
|
log_msg "INFO " "已载入 [${REGION}] 区域白名单,配置库条目: ${#TRUST_URLS[@]} 个"
|
||||||
log_msg "INFO " "已锁定本地伪装指纹: $(echo $CURRENT_UA | cut -d' ' -f1-2)..."
|
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))
|
STEP_COUNT=$((RANDOM % 4 + 3))
|
||||||
SUCCESS_INJECT=0
|
SUCCESS_INJECT=0
|
||||||
|
|
||||||
@@ -65,7 +118,8 @@ for ((i=1; i<=STEP_COUNT; i++)); do
|
|||||||
TARGET_URL=${TRUST_URLS[$RANDOM % ${#TRUST_URLS[@]}]}
|
TARGET_URL=${TRUST_URLS[$RANDOM % ${#TRUST_URLS[@]}]}
|
||||||
|
|
||||||
# [v3.0.1修复] 注入高权重流量时,强制从绑定的 IPv4 或 IPv6 隧道出网
|
# [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: 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 "Accept-Language: en-US,en;q=0.9" \
|
||||||
-H "Sec-Fetch-Dest: document" \
|
-H "Sec-Fetch-Dest: document" \
|
||||||
@@ -74,7 +128,8 @@ for ((i=1; i<=STEP_COUNT; i++)); do
|
|||||||
--compressed \
|
--compressed \
|
||||||
-s -o /dev/null -w "%{http_code}" -m 15 "$TARGET_URL")
|
-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"
|
log_msg "EXEC " "动作[$i/$STEP_COUNT]完成 | 状态: $HTTP_CODE | 注入: $TARGET_URL"
|
||||||
((SUCCESS_INJECT++))
|
((SUCCESS_INJECT++))
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
# 脚本名称: runner.sh (IP-Sentinel 主控调度引擎 V2.0 智能分配版)
|
# 脚本名称: runner.sh (IP-Sentinel 主控调度引擎 - 动态锚点版)
|
||||||
# 核心功能: 防并发延迟启动、功能开关(Feature Flag)自适应、多模块概率轮盘调度
|
# 核心功能: 防并发延迟启动、功能开关(Feature Flag)自适应、多模块概率轮盘调度
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
|
|
||||||
@@ -15,23 +15,31 @@ if [ ! -f "$CONFIG_FILE" ]; then
|
|||||||
fi
|
fi
|
||||||
source "$CONFIG_FILE"
|
source "$CONFIG_FILE"
|
||||||
|
|
||||||
# 2. 全局日志写入函数 (导出给子进程共享使用)
|
# 2. 全局日志写入函数 (导出给子进程共享使用,v3.4.0 引入版本探针)
|
||||||
log() {
|
log() {
|
||||||
local module=$1
|
local module=$1
|
||||||
local level=$2
|
local level=$2
|
||||||
local msg=$3
|
local msg=$3
|
||||||
|
# [v3.4.0 核心] 提取当前配置中的版本锚点
|
||||||
|
local local_ver="${AGENT_VERSION:-未知}"
|
||||||
|
|
||||||
# 保证日志目录存在
|
# 保证日志目录存在
|
||||||
mkdir -p "${INSTALL_DIR}/logs"
|
mkdir -p "${INSTALL_DIR}/logs"
|
||||||
printf "[$(date '+%Y-%m-%d %H:%M:%S')] [%-5s] [%-7s] [%s] %s\n" "$level" "$module" "$REGION_CODE" "$msg" >> "$LOG_FILE"
|
# 日志格式注入 [版本号] 追踪标识
|
||||||
|
printf "[$(date '+%Y-%m-%d %H:%M:%S')] [v%-5s] [%-5s] [%-7s] [%s] %s\n" "$local_ver" "$level" "$module" "$REGION_CODE" "$msg" >> "$LOG_FILE"
|
||||||
}
|
}
|
||||||
export -f log
|
export -f log
|
||||||
export CONFIG_FILE INSTALL_DIR
|
export CONFIG_FILE INSTALL_DIR
|
||||||
|
|
||||||
# 3. 防僵尸网络特征 (Cron Jitter) - 核心隐蔽逻辑
|
# 3. 防僵尸网络特征 (Cron Jitter) - 核心隐蔽逻辑
|
||||||
# 配合每 30 分钟的调度周期,将随机休眠控制在 0 到 180 秒 (3分钟) 内,彻底打散全球并发请求
|
# 配合每 30 分钟的调度周期,将随机休眠控制在 0 到 180 秒内,彻底打散全球并发请求
|
||||||
JITTER_TIME=$((RANDOM % 180))
|
if [ -t 1 ]; then
|
||||||
log "SYSTEM" "INFO" "主控引擎被 Cron 唤醒,进入防并发随机休眠状态: ${JITTER_TIME} 秒..."
|
log "SYSTEM" "INFO " "💻 检测到人工终端干预,跳过静默休眠,立即执行任务!"
|
||||||
sleep $JITTER_TIME
|
else
|
||||||
|
JITTER_TIME=$((RANDOM % 180))
|
||||||
|
log "SYSTEM" "INFO " "⏱️ 主控引擎由后台唤醒,进入防并发随机休眠状态: ${JITTER_TIME} 秒..."
|
||||||
|
sleep $JITTER_TIME
|
||||||
|
fi
|
||||||
|
|
||||||
# 4. 唤醒并读取功能开关,执行智能调度 (Feature Flag)
|
# 4. 唤醒并读取功能开关,执行智能调度 (Feature Flag)
|
||||||
log "SYSTEM" "INFO" "休眠结束,开始计算本轮任务轮盘..."
|
log "SYSTEM" "INFO" "休眠结束,开始计算本轮任务轮盘..."
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
# 脚本名称: tg_daemon.sh (Telegram 互动监听守护进程)
|
# 脚本名称: tg_daemon.sh (Telegram 互动监听守护进程 - 动态锚点版)
|
||||||
# 核心功能: 极低功耗长轮询监听 TG 指令,实现远程控制
|
# 核心功能: 极低功耗长轮询监听、节点溯源、版本继承
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
|
|
||||||
INSTALL_DIR="/opt/ip_sentinel"
|
INSTALL_DIR="/opt/ip_sentinel"
|
||||||
@@ -16,6 +16,11 @@ source "$CONFIG_FILE"
|
|||||||
# 如果没有配置 TG 机器人,则安静退出守护进程
|
# 如果没有配置 TG 机器人,则安静退出守护进程
|
||||||
[ -z "$TG_TOKEN" ] || [ -z "$CHAT_ID" ] && exit 0
|
[ -z "$TG_TOKEN" ] || [ -z "$CHAT_ID" ] && exit 0
|
||||||
|
|
||||||
|
# [核心: 动态版本锚点与防撞甲身份载入]
|
||||||
|
LOCAL_VER="${AGENT_VERSION:-未知}"
|
||||||
|
IP_HASH=$(echo "${PUBLIC_IP:-127.0.0.1}" | md5sum | cut -c 1-4 | tr 'a-z' 'A-Z')
|
||||||
|
NODE_NAME="$(hostname | cut -c 1-10)-${IP_HASH}"
|
||||||
|
|
||||||
# 2. 初始化消息偏移量 (Offset) 记录文件,防止重启后重复处理老消息
|
# 2. 初始化消息偏移量 (Offset) 记录文件,防止重启后重复处理老消息
|
||||||
OFFSET=0
|
OFFSET=0
|
||||||
[ -f "$OFFSET_FILE" ] && OFFSET=$(cat "$OFFSET_FILE")
|
[ -f "$OFFSET_FILE" ] && OFFSET=$(cat "$OFFSET_FILE")
|
||||||
@@ -44,20 +49,20 @@ while true; do
|
|||||||
if [ "$MSG_CHAT_ID" == "$CHAT_ID" ]; then
|
if [ "$MSG_CHAT_ID" == "$CHAT_ID" ]; then
|
||||||
case "$MSG_TEXT" in
|
case "$MSG_TEXT" in
|
||||||
"/run")
|
"/run")
|
||||||
send_msg "🚀 **[指令下达]** 正在后台立即触发 IP 养护任务..."
|
send_msg "🚀 **[${NODE_NAME}]** 正在后台触发 IP 养护任务 (v${LOCAL_VER})..."
|
||||||
# 使用 nohup 另起后台独立进程运行,防止阻塞当前监听器的循环
|
# 使用 nohup 另起后台独立进程运行,防止阻塞当前监听器的循环
|
||||||
nohup bash "${INSTALL_DIR}/core/mod_google.sh" >/dev/null 2>&1 &
|
nohup bash "${INSTALL_DIR}/core/mod_google.sh" >/dev/null 2>&1 &
|
||||||
;;
|
;;
|
||||||
"/log")
|
"/log")
|
||||||
LOG_DATA=$(tail -n 15 "${INSTALL_DIR}/logs/sentinel.log")
|
LOG_DATA=$(tail -n 15 "${INSTALL_DIR}/logs/sentinel.log")
|
||||||
send_msg "📄 **[最近 15 行系统日志]**%0A\`\`\`log%0A${LOG_DATA}%0A\`\`\`"
|
send_msg "📄 **[${NODE_NAME}] 实时日志 (v${LOCAL_VER}):**%0A\`\`\`log%0A${LOG_DATA}%0A\`\`\`"
|
||||||
;;
|
;;
|
||||||
"/report")
|
"/report")
|
||||||
# 触发生成一次战报
|
# 触发生成一次战报
|
||||||
bash "${INSTALL_DIR}/core/tg_report.sh"
|
bash "${INSTALL_DIR}/core/tg_report.sh"
|
||||||
;;
|
;;
|
||||||
"/help"|"/start")
|
"/help"|"/start")
|
||||||
HELP_MSG="🛡️ **IP-Sentinel 控制台**%0A/run - 立刻执行一次养护%0A/log - 抓取最新运行日志%0A/report - 手动生成统计简报"
|
HELP_MSG="🛡️ **IP-Sentinel 边缘控制台**%0A📍 节点: \`${NODE_NAME}\`%0A🔖 版本: \`v${LOCAL_VER}\`%0A%0A/run - 立刻执行一次养护%0A/log - 抓取最新运行日志%0A/report - 手动生成统计简报"
|
||||||
send_msg "$HELP_MSG"
|
send_msg "$HELP_MSG"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
# 脚本名称: tg_report.sh (Telegram 每日战报模块 V6.0 动态拼装版)
|
# 脚本名称: tg_report.sh (Telegram 每日战报模块 - 动态锚点版)
|
||||||
# 核心功能: 适配 Feature Flag 架构,按需展示 Google/Trust 独立统计数据
|
# 核心功能: 适配 Feature Flag 架构,按需展示独立统计数据,OTA 更新预警
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
|
|
||||||
INSTALL_DIR="/opt/ip_sentinel"
|
INSTALL_DIR="/opt/ip_sentinel"
|
||||||
@@ -18,19 +18,55 @@ if [ -z "$TG_TOKEN" ] || [ -z "$CHAT_ID" ]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 2. 节点元数据抓取 (v3.0.1修复: 严格使用配置中的协议探测出口与多节点容灾)
|
# 2. 节点元数据抓取 (v3.2.2 协议自适应与多级容灾版)
|
||||||
NODE_NAME=$(hostname | cut -c 1-15)
|
# [v3.3.2 修复: 引入 IP 哈希防同名覆盖机制]
|
||||||
|
IP_HASH=$(echo "${PUBLIC_IP:-127.0.0.1}" | md5sum | cut -c 1-4 | tr 'a-z' 'A-Z')
|
||||||
|
NODE_NAME="$(hostname | cut -c 1-10)-${IP_HASH}"
|
||||||
|
|
||||||
# 多节点容灾探测
|
# --- [防线 1: 底层路由锁定与协议自适应] ---
|
||||||
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:]' )
|
CURL_BIND_OPT=""
|
||||||
# 强制兜底:如果所有外部 API 都挂了,直接使用本地强行锁定的 BIND_IP
|
DYNAMIC_IP_PREF="-${IP_PREF:-4}"
|
||||||
[ -z "$CURRENT_IP" ] && CURRENT_IP="$BIND_IP"
|
|
||||||
|
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:]' )
|
||||||
|
# [v3.3.1 修改] 强制兜底:如果外部 API 挂了,优先使用固化的对外公网面孔 (兼容 NAT 机的空 BIND_IP)
|
||||||
|
[ -z "$CURRENT_IP" ] && CURRENT_IP="${PUBLIC_IP:-$BIND_IP}"
|
||||||
|
|
||||||
# 为可能获取到的 IPv6 自动添加方括号护甲
|
# 为可能获取到的 IPv6 自动添加方括号护甲
|
||||||
[[ "$CURRENT_IP" == *":"* ]] && [[ "$CURRENT_IP" != *"["* ]] && CURRENT_IP="[${CURRENT_IP}]"
|
[[ "$CURRENT_IP" == *":"* ]] && [[ "$CURRENT_IP" != *"["* ]] && CURRENT_IP="[${CURRENT_IP}]"
|
||||||
|
|
||||||
# 智能判断 IP 属性
|
# --- [防线 2: 多级 ISP 容灾探针链路] ---
|
||||||
ISP_INFO=$(curl -${IP_PREF:-4} -s -m 5 api.ip.sb/geoip | jq -r '.organization' 2>/dev/null)
|
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"
|
[ -z "$ISP_INFO" ] || [ "$ISP_INFO" == "null" ] && ISP_INFO="未知 ISP"
|
||||||
|
|
||||||
if [[ "$ISP_INFO" == *"Cloudflare"* ]]; then
|
if [[ "$ISP_INFO" == *"Cloudflare"* ]]; then
|
||||||
@@ -119,12 +155,41 @@ else
|
|||||||
|
|
||||||
🕒 **最近执行快照 [${LAST_MOD:-"System"}]:**
|
🕒 **最近执行快照 [${LAST_MOD:-"System"}]:**
|
||||||
时间: ${LAST_TIME:-"暂无数据"}
|
时间: ${LAST_TIME:-"暂无数据"}
|
||||||
结论: ${LAST_SCORE:-"暂无数据"}
|
结论: ${LAST_SCORE:-"暂无数据"}"
|
||||||
----------------------------
|
|
||||||
💡 哨兵正在后台默默守护您的资产。"
|
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# 5. [核心: OTA 云端版本探针与告警模块]
|
||||||
|
# ==========================================
|
||||||
|
# 从配置文件提取当前本地版本,若无则默认为未知
|
||||||
|
LOCAL_VER="${AGENT_VERSION:-未知}"
|
||||||
|
|
||||||
|
# 极轻量级探针: 抓取 GitHub 云端的 version.txt (超时 3 秒,KV解析法)
|
||||||
|
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||||
|
REMOTE_VER=$(curl -s -m 3 "${REPO_RAW_URL}/version.txt" | grep "^AGENT_VERSION=" | cut -d'=' -f2 | tr -d '[:space:]')
|
||||||
|
|
||||||
|
# 构建底部引擎状态块
|
||||||
|
MSG="$MSG
|
||||||
|
----------------------------
|
||||||
|
🛡️ **系统引擎状态**
|
||||||
|
当前运行版本: \`v${LOCAL_VER}\`"
|
||||||
|
|
||||||
|
# 比对逻辑:如果成功抓到了远端版本,且和本地不一样
|
||||||
|
if [ -n "$REMOTE_VER" ] && [ "$REMOTE_VER" != "$LOCAL_VER" ]; then
|
||||||
|
MSG="$MSG
|
||||||
|
最新官方版本: \`v${REMOTE_VER}\` (✨有新版)
|
||||||
|
💡 *司令部提示:检测到新版装甲,请长官登录节点执行平滑热更新!*"
|
||||||
|
elif [ -n "$REMOTE_VER" ] && [ "$REMOTE_VER" == "$LOCAL_VER" ]; then
|
||||||
|
MSG="$MSG
|
||||||
|
最新官方版本: \`v${REMOTE_VER}\` (✅已是最新)
|
||||||
|
💡 *哨兵正在后台默默守护您的资产。*"
|
||||||
|
else
|
||||||
|
# 抓取失败兜底
|
||||||
|
MSG="$MSG
|
||||||
|
💡 *哨兵正在后台默默守护您的资产。*"
|
||||||
|
fi
|
||||||
|
|
||||||
# 5. 调用 API 推送 (接入安全网关)
|
# 5. 调用 API 推送 (接入安全网关)
|
||||||
RESPONSE=$(curl -s -m 10 -X POST "${TG_API_URL}" \
|
RESPONSE=$(curl -s -m 10 -X POST "${TG_API_URL}" \
|
||||||
-d "chat_id=${CHAT_ID}" \
|
-d "chat_id=${CHAT_ID}" \
|
||||||
|
|||||||
@@ -1,48 +1,58 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
# 脚本名称: uninstall.sh (IP-Sentinel 一键卸载脚本 V2.0)
|
# 脚本名称: uninstall.sh (IP-Sentinel 一键卸载脚本 - 动态锚点版)
|
||||||
# 核心功能: 清除所有世代的守护进程、清理系统定时任务、删除程序文件
|
# 核心功能: 无痕清理守护进程、定时任务、运行目录及临时缓存
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
|
|
||||||
INSTALL_DIR="/opt/ip_sentinel"
|
INSTALL_DIR="/opt/ip_sentinel"
|
||||||
|
|
||||||
echo "========================================================"
|
echo "========================================================"
|
||||||
echo " 🗑️ 准备卸载 IP-Sentinel (VPS IP 自动养护哨兵)"
|
echo " 🗑️ 准备卸载 IP-Sentinel (边缘节点 Edge Agent)"
|
||||||
|
|
||||||
|
# [核心: 动态读取并播报即将销毁的本地版本号]
|
||||||
|
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||||
|
if [ -f "$CONFIG_FILE" ]; then
|
||||||
|
CURRENT_VER=$(grep "^AGENT_VERSION=" "$CONFIG_FILE" | cut -d'"' -f2)
|
||||||
|
[ -n "$CURRENT_VER" ] && echo " 📍 目标版本: v${CURRENT_VER}"
|
||||||
|
fi
|
||||||
echo "========================================================"
|
echo "========================================================"
|
||||||
|
|
||||||
# 1. 停止运行中的守护进程与主控模块 (涵盖 V1.0 至 V2.0 架构全量进程)
|
# 1. 停止运行中的守护进程与主控模块 (涵盖所有历史版本进程)
|
||||||
echo "[1/3] 正在终止后台守护进程与所有养护任务..."
|
echo "[1/3] 正在终止后台守护进程与所有养护任务..."
|
||||||
|
|
||||||
# 击杀旧版 (V1.x) 遗留进程
|
# 使用 pkill 替代传统的 pgrep | xargs,指令更短、容错率更高
|
||||||
pgrep -f tg_daemon.sh | xargs -r kill -9 >/dev/null 2>&1
|
pkill -9 -f "tg_daemon.sh" >/dev/null 2>&1
|
||||||
|
pkill -9 -f "agent_daemon.sh" >/dev/null 2>&1
|
||||||
# 击杀新版 (V2.x) 神经末梢守护进程
|
# [v3.4.0 优化] 确保清理所有 python3 调起的 Webhook 实例
|
||||||
pgrep -f agent_daemon.sh | xargs -r kill -9 >/dev/null 2>&1
|
pkill -9 -f "python3.*webhook.py" >/dev/null 2>&1
|
||||||
pgrep -f webhook.py | xargs -r kill -9 >/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
|
||||||
pgrep -f runner.sh | xargs -r kill -9 >/dev/null 2>&1
|
pkill -9 -f "tg_report.sh" >/dev/null 2>&1
|
||||||
pgrep -f updater.sh | xargs -r kill -9 >/dev/null 2>&1
|
pkill -9 -f "mod_google.sh" >/dev/null 2>&1
|
||||||
pgrep -f tg_report.sh | xargs -r kill -9 >/dev/null 2>&1
|
pkill -9 -f "mod_trust.sh" >/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
|
|
||||||
|
|
||||||
# 2. 清除系统定时任务 (Cron)
|
# 2. 清除系统定时任务 (Cron)
|
||||||
echo "[2/3] 正在清理系统定时任务 (Cron)..."
|
echo "[2/3] 正在清理系统定时任务 (Cron)..."
|
||||||
crontab -l 2>/dev/null | grep -v "ip_sentinel" > /tmp/cron_backup
|
if crontab -l >/dev/null 2>&1; then
|
||||||
crontab /tmp/cron_backup
|
crontab -l | grep -v "ip_sentinel" > /tmp/cron_backup
|
||||||
rm -f /tmp/cron_backup
|
crontab /tmp/cron_backup
|
||||||
|
rm -f /tmp/cron_backup
|
||||||
|
fi
|
||||||
|
|
||||||
# 3. 删除所有文件与日志
|
# 3. 删除所有文件、日志与临时缓存
|
||||||
echo "[3/3] 正在抹除核心程序、配置文件与系统日志..."
|
echo "[3/3] 正在抹除核心程序、配置文件与系统痕迹..."
|
||||||
if [ -d "$INSTALL_DIR" ]; then
|
if [ -d "$INSTALL_DIR" ]; then
|
||||||
rm -rf "$INSTALL_DIR"
|
rm -rf "$INSTALL_DIR"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 拔除 /tmp 目录下的所有更新下载临时文件和 V1/V2 遗留的偏移量记录
|
||||||
|
rm -f /tmp/ip_sentinel_*.txt
|
||||||
|
rm -f /tmp/ip_sentinel_*.json
|
||||||
|
|
||||||
echo "========================================================"
|
echo "========================================================"
|
||||||
echo "✅ 卸载彻底完成!IP-Sentinel 已从您的系统中无痕移除。"
|
echo "✅ 卸载彻底完成!IP-Sentinel 已从您的系统中无痕移除。"
|
||||||
echo "👋 感谢您的使用,期待未来再次为您守护 IP!"
|
echo "💡 提示:如果安装时在防火墙放行了 Webhook 随机端口,请您按需手动关闭。"
|
||||||
|
echo "👋 感谢您的使用,期待未来再次为您守护资产!"
|
||||||
echo "========================================================"
|
echo "========================================================"
|
||||||
109
core/updater.sh
109
core/updater.sh
@@ -1,14 +1,16 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
# 脚本名称: updater.sh (IP-Sentinel 养料注入与系统维护模块)
|
# 脚本名称: updater.sh (IP-Sentinel 养料注入与分频调度中枢 - 动态锚点版)
|
||||||
# 核心功能: 定期静默更新热数据、清理瘦身日志文件
|
# 核心功能: 静默更新热数据/LBS、指纹库错峰调度、强制出站死锁、版本无缝继承
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
|
|
||||||
INSTALL_DIR="/opt/ip_sentinel"
|
INSTALL_DIR="/opt/ip_sentinel"
|
||||||
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
CONFIG_FILE="${INSTALL_DIR}/config.conf"
|
||||||
# 你的专属 Forgejo 仓库 Raw 数据直链前缀
|
UA_TIME_FILE="${INSTALL_DIR}/core/.ua_last_update"
|
||||||
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"
|
||||||
|
|
||||||
# 1. 加载本地冷数据配置
|
# 1. 加载本地冷数据配置
|
||||||
if [ ! -f "$CONFIG_FILE" ]; then
|
if [ ! -f "$CONFIG_FILE" ]; then
|
||||||
@@ -16,37 +18,106 @@ if [ ! -f "$CONFIG_FILE" ]; then
|
|||||||
fi
|
fi
|
||||||
source "$CONFIG_FILE"
|
source "$CONFIG_FILE"
|
||||||
|
|
||||||
# 2. 全局日志写入函数
|
# 2. 全局日志写入函数 (v3.4.0 引入版本探针)
|
||||||
log() {
|
log() {
|
||||||
|
# [v3.4.0 核心] 提取当前配置中的版本锚点
|
||||||
|
local local_ver="${AGENT_VERSION:-未知}"
|
||||||
|
|
||||||
mkdir -p "${INSTALL_DIR}/logs"
|
mkdir -p "${INSTALL_DIR}/logs"
|
||||||
printf "[$(date '+%Y-%m-%d %H:%M:%S')] [%-5s] [%-7s] [%s] %s\n" "$2" "$1" "$REGION_CODE" "$3" >> "$LOG_FILE"
|
# 日志格式注入 [版本号] 追踪标识
|
||||||
|
printf "[$(date '+%Y-%m-%d %H:%M:%S')] [v%-5s] [%-5s] [%-7s] [%s] %s\n" "$local_ver" "$2" "$1" "$REGION_CODE" "$3" >> "$LOG_FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
log "Updater" "INFO " "========== 触发后台静默 OTA 热数据更新 =========="
|
log "Updater" "INFO " "========== 触发后台静默 OTA 热数据更新 =========="
|
||||||
|
|
||||||
# 3. 容灾机制拉取 UA 池
|
# ==========================================================
|
||||||
TMP_UA="/tmp/ip_sentinel_ua.txt"
|
# 🛡️ 终极护城河:构建强锚定出站的 curl 请求引擎
|
||||||
curl -sL "${REPO_RAW_URL}/data/user_agents.txt" -o "$TMP_UA"
|
# ==========================================================
|
||||||
if [ -s "$TMP_UA" ]; then
|
# 基础参数:跟随 install.sh 锁定的协议偏好 (4 或 6)
|
||||||
mv "$TMP_UA" "${INSTALL_DIR}/data/user_agents.txt"
|
CURL_CMD="curl -${IP_PREF:-4} -sL"
|
||||||
log "Updater" "INFO " "✅ 设备指纹池 (User-Agents) 更新成功"
|
|
||||||
else
|
# 【防坑核心】如果用户配置了死锁锚点,必须强制绑定网卡,杜绝流量溢出!
|
||||||
log "Updater" "WARN " "❌ UA 池拉取失败或为空,保留本地旧数据防崩溃"
|
if [ -n "$BIND_IP" ]; then
|
||||||
rm -f "$TMP_UA"
|
# curl 的 --interface 参数不支持带方括号的 IPv6 地址,必须强行脱壳
|
||||||
|
RAW_BIND_IP=$(echo "$BIND_IP" | tr -d '[]')
|
||||||
|
CURL_CMD="$CURL_CMD --interface $RAW_BIND_IP"
|
||||||
fi
|
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"
|
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
|
if [ -s "$TMP_KW" ]; then
|
||||||
mv "$TMP_KW" "${INSTALL_DIR}/data/keywords/kw_${REGION_CODE}.txt"
|
mv "$TMP_KW" "${INSTALL_DIR}/data/keywords/kw_${REGION_CODE}.txt"
|
||||||
log "Updater" "INFO " "✅ 区域搜索词库 (kw_${REGION_CODE}) 更新成功"
|
log "Updater" "INFO " "✅ 区域搜索词库 (kw_${REGION_CODE}) 每日同步成功"
|
||||||
else
|
else
|
||||||
log "Updater" "WARN " "❌ 搜索词库拉取失败,保留本地旧数据防崩溃"
|
log "Updater" "WARN " "❌ 搜索词库拉取失败,保留本地旧数据防崩溃"
|
||||||
rm -f "$TMP_KW"
|
rm -f "$TMP_KW"
|
||||||
fi
|
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
|
if [ -f "$LOG_FILE" ]; then
|
||||||
tail -n 2000 "$LOG_FILE" > "${LOG_FILE}.tmp"
|
tail -n 2000 "$LOG_FILE" > "${LOG_FILE}.tmp"
|
||||||
mv "${LOG_FILE}.tmp" "$LOG_FILE"
|
mv "${LOG_FILE}.tmp" "$LOG_FILE"
|
||||||
|
|||||||
25
data/keywords/kw_CA.txt
Normal file
25
data/keywords/kw_CA.txt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
ina garten
|
||||||
|
jordan goodwin
|
||||||
|
jerami grant
|
||||||
|
jimmy snuggerud
|
||||||
|
clav
|
||||||
|
bts
|
||||||
|
zach galifianakis
|
||||||
|
billy crystal
|
||||||
|
club américa vs nashville
|
||||||
|
allison williams
|
||||||
|
CBC News
|
||||||
|
Canada.ca
|
||||||
|
Toronto Weather
|
||||||
|
NHL Scores
|
||||||
|
Amazon.ca
|
||||||
|
Air Canada
|
||||||
|
Service Canada
|
||||||
|
CRA login
|
||||||
|
Rogers
|
||||||
|
Bell Internet
|
||||||
|
Tim Hortons
|
||||||
|
Indigo
|
||||||
|
Canadian Tire
|
||||||
|
Walmart Canada
|
||||||
|
Toronto Raptors
|
||||||
@@ -1,3 +1,43 @@
|
|||||||
|
gute zeiten, schlechte zeiten
|
||||||
|
inflation
|
||||||
|
fog warning
|
||||||
|
wetter bochum
|
||||||
|
wetter aachen
|
||||||
|
wetter bonn
|
||||||
|
onet
|
||||||
|
protest
|
||||||
|
jamie dornan
|
||||||
|
heizöl
|
||||||
|
champions league
|
||||||
|
uefa champions league
|
||||||
|
keytruda
|
||||||
|
péter magyar
|
||||||
|
psg
|
||||||
|
hechingen
|
||||||
|
şampiyonlar ligi
|
||||||
|
tschernobyl 1986
|
||||||
|
amazon video
|
||||||
|
paris saint-germain
|
||||||
|
dietrich grönemeyer
|
||||||
|
fränkische schweiz
|
||||||
|
scarlett johansson
|
||||||
|
jeff bezos
|
||||||
|
dan brown
|
||||||
|
паспорт громадянина україни для виїзду за кордон
|
||||||
|
serena williams
|
||||||
|
kampf der realitystars
|
||||||
|
манчестер юнайтед – лидс
|
||||||
|
catherine deneuve
|
||||||
|
bobzin
|
||||||
|
sprit
|
||||||
|
kev
|
||||||
|
abschiebung
|
||||||
|
steuer
|
||||||
|
masters rory mcilroy
|
||||||
|
großglockner
|
||||||
|
news38
|
||||||
|
jessie cave
|
||||||
|
michael schulte
|
||||||
wetter frankfurt heute
|
wetter frankfurt heute
|
||||||
bundesliga ergebnisse
|
bundesliga ergebnisse
|
||||||
aktuelle nachrichten deutschland
|
aktuelle nachrichten deutschland
|
||||||
|
|||||||
25
data/keywords/kw_ES.txt
Normal file
25
data/keywords/kw_ES.txt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
supervivientes
|
||||||
|
jalen green
|
||||||
|
rockstar games
|
||||||
|
bam adebayo
|
||||||
|
china
|
||||||
|
suns - trail blazers
|
||||||
|
euromillones
|
||||||
|
ldu quito - mirassol
|
||||||
|
davion mitchell
|
||||||
|
universitario - coquimbo unido
|
||||||
|
El País
|
||||||
|
Marca
|
||||||
|
RTVE Noticias
|
||||||
|
La Liga
|
||||||
|
Real Madrid
|
||||||
|
Barcelona FC
|
||||||
|
Tiempo Madrid
|
||||||
|
Renfe
|
||||||
|
Iberia
|
||||||
|
Amazon España
|
||||||
|
El Corte Inglés
|
||||||
|
Hacienda
|
||||||
|
Mercadona
|
||||||
|
YouTube Música
|
||||||
|
Entradas Cine
|
||||||
@@ -1,3 +1,42 @@
|
|||||||
|
kiev
|
||||||
|
julien royal
|
||||||
|
viktor orbán
|
||||||
|
aqababe
|
||||||
|
nhl
|
||||||
|
suns – trail blazers
|
||||||
|
bam adebayo
|
||||||
|
davion mitchell
|
||||||
|
l
|
||||||
|
santos – recoleta football club
|
||||||
|
atletico madrid
|
||||||
|
tf1
|
||||||
|
uefa champions league
|
||||||
|
camille cerf
|
||||||
|
giorgi mamardashvili
|
||||||
|
streaming football
|
||||||
|
atlético madryt – fc barcelona
|
||||||
|
miss france
|
||||||
|
rts
|
||||||
|
leonardo balerdi
|
||||||
|
yann barthes
|
||||||
|
alain delon
|
||||||
|
loto du 13 avril 2026
|
||||||
|
juan arbeláez
|
||||||
|
hbo
|
||||||
|
katy perry justin trudeau
|
||||||
|
jacob elordi
|
||||||
|
tondela – gil vicente
|
||||||
|
le rugbynistère
|
||||||
|
epstein
|
||||||
|
kino
|
||||||
|
horoscope du 13 avril 2026
|
||||||
|
golf masters augusta 2026
|
||||||
|
boursorama bourse
|
||||||
|
cac 40
|
||||||
|
sept à huit
|
||||||
|
ligne 12 métro
|
||||||
|
alice taglioni
|
||||||
|
pedro sánchez
|
||||||
meteo paris
|
meteo paris
|
||||||
actualités en direct
|
actualités en direct
|
||||||
résultats ligue 1
|
résultats ligue 1
|
||||||
|
|||||||
@@ -1,3 +1,43 @@
|
|||||||
|
nba 直播
|
||||||
|
航空公司
|
||||||
|
向華強
|
||||||
|
李嘉欣
|
||||||
|
typhoon
|
||||||
|
nba
|
||||||
|
nba線上看
|
||||||
|
nba直播
|
||||||
|
全民國家安全教育日
|
||||||
|
運輸署
|
||||||
|
liverpool vs psg
|
||||||
|
利物浦
|
||||||
|
barcelona
|
||||||
|
歐冠
|
||||||
|
馬德里競技 對 巴塞隆納
|
||||||
|
利物浦 對 巴黎聖日耳曼
|
||||||
|
hkjc
|
||||||
|
馬會
|
||||||
|
航空
|
||||||
|
2035
|
||||||
|
man united vs leeds
|
||||||
|
曼聯 對 里茲聯
|
||||||
|
prediction market
|
||||||
|
預測市場
|
||||||
|
polymarket
|
||||||
|
巴基斯坦
|
||||||
|
sndk
|
||||||
|
江美儀
|
||||||
|
楊何蓓茵
|
||||||
|
樂珈嘉
|
||||||
|
姜濤
|
||||||
|
日經平均指數
|
||||||
|
飲茶
|
||||||
|
上市公司
|
||||||
|
daniel caesar
|
||||||
|
中年好聲音4
|
||||||
|
香港天文台
|
||||||
|
煤氣
|
||||||
|
livenation
|
||||||
|
政府
|
||||||
香港天文台天氣預報
|
香港天文台天氣預報
|
||||||
MTR 港鐵路線圖
|
MTR 港鐵路線圖
|
||||||
OpenRice 附近美食
|
OpenRice 附近美食
|
||||||
|
|||||||
@@ -1,7 +1,46 @@
|
|||||||
|
小泉進次郎
|
||||||
|
政権
|
||||||
|
ミキティ
|
||||||
|
ソニック
|
||||||
|
今日のドジャースの結果
|
||||||
|
新名神高速道路
|
||||||
|
わたせせいぞう
|
||||||
|
ピーチ航空
|
||||||
|
山本由伸
|
||||||
|
アレックス・ベシア
|
||||||
|
リバプール
|
||||||
|
abema
|
||||||
|
champions league
|
||||||
|
wowow
|
||||||
|
エルニーニョ
|
||||||
|
アトレティコ 対 バルセロナ
|
||||||
|
オープンワールド
|
||||||
|
atlético madrid vs barcelona
|
||||||
|
松田好花
|
||||||
|
リコール
|
||||||
|
man united vs leeds
|
||||||
|
白鵬翔
|
||||||
|
日本アカデミー賞 最優秀助演男優賞
|
||||||
|
マンu 対 リーズ u
|
||||||
|
サンディスク 株価
|
||||||
|
らじるらじる
|
||||||
|
マクドナルド
|
||||||
|
ロシア
|
||||||
|
広島市
|
||||||
|
ゲイブル・スティーブソン
|
||||||
|
日本維新の会
|
||||||
|
新 日本 繊維
|
||||||
|
高見沢 俊彦
|
||||||
|
不登校
|
||||||
|
後期高齢者医療制度
|
||||||
|
バーミヤン
|
||||||
|
宮澤エマ
|
||||||
|
チケプラ
|
||||||
|
横綱
|
||||||
|
宮里美香
|
||||||
東京 天気 明日
|
東京 天気 明日
|
||||||
新宿 おすすめ 居酒屋
|
新宿 おすすめ 居酒屋
|
||||||
最新のニュース 速報
|
最新のニュース 速報
|
||||||
ゴールド 相場 チャート
|
ゴールド 相場 チャート
|
||||||
近くの静かなカフェ
|
近くの静かなカフェ
|
||||||
地震 速報
|
|
||||||
円安 影響 生活
|
円安 影響 生活
|
||||||
25
data/keywords/kw_NL.txt
Normal file
25
data/keywords/kw_NL.txt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
netflix
|
||||||
|
frenkie de jong
|
||||||
|
kanye west
|
||||||
|
vandaag inside
|
||||||
|
at5
|
||||||
|
veroordeling
|
||||||
|
verenigde staten
|
||||||
|
alec baldwin
|
||||||
|
anna paulowna
|
||||||
|
şampiyonlar ligi
|
||||||
|
NOS Nieuws
|
||||||
|
Buienradar
|
||||||
|
Rijksoverheid
|
||||||
|
Albert Heijn
|
||||||
|
Funda
|
||||||
|
Marktplaats
|
||||||
|
KLM
|
||||||
|
Ziggo
|
||||||
|
ING Bank
|
||||||
|
Eredivisie
|
||||||
|
Amsterdam Weer
|
||||||
|
Bol.com
|
||||||
|
Treinkaartjes NS
|
||||||
|
PostNL
|
||||||
|
Pathé
|
||||||
@@ -1,3 +1,43 @@
|
|||||||
|
retirement
|
||||||
|
asia flights delays cancellations
|
||||||
|
suns vs trail blazers
|
||||||
|
johnny somali
|
||||||
|
bam adebayo
|
||||||
|
zhang linghe pursuit of jade
|
||||||
|
roman gofman
|
||||||
|
cruz azul vs lafc
|
||||||
|
ocbc
|
||||||
|
santos vs recoleta
|
||||||
|
atlético madrid vs barcelona
|
||||||
|
ipl schedule
|
||||||
|
liverpool vs psg
|
||||||
|
iran blockade strait of hormuz
|
||||||
|
kartik tyagi
|
||||||
|
carlos alcaraz
|
||||||
|
propertylimbrothers
|
||||||
|
byeon woo-seok
|
||||||
|
mahathir mohamad
|
||||||
|
csk vs kkr
|
||||||
|
man united vs leeds
|
||||||
|
cbse class 10 result 2026 date
|
||||||
|
euphoria season 3
|
||||||
|
srh vs rr
|
||||||
|
tamil new year 2026
|
||||||
|
low de wei
|
||||||
|
pope
|
||||||
|
flexar
|
||||||
|
microsoft outlook
|
||||||
|
new rolex 2026
|
||||||
|
medical classification
|
||||||
|
blasphemy law
|
||||||
|
big bang coachella 2026
|
||||||
|
小贩
|
||||||
|
malaysia fuel price crisis
|
||||||
|
sbti personality test
|
||||||
|
cancer survivor
|
||||||
|
tim cook
|
||||||
|
spurs vs nuggets
|
||||||
|
asia flights cancelled delayed
|
||||||
singapore weather forecast
|
singapore weather forecast
|
||||||
mrt map singapore
|
mrt map singapore
|
||||||
straitstimes breaking news
|
straitstimes breaking news
|
||||||
|
|||||||
@@ -1,3 +1,43 @@
|
|||||||
|
talktalk
|
||||||
|
arne slot drops mohamed salah
|
||||||
|
suns vs trail blazers
|
||||||
|
italian
|
||||||
|
used cars
|
||||||
|
mlb
|
||||||
|
roman
|
||||||
|
johnny somali
|
||||||
|
windows update
|
||||||
|
davion mitchell
|
||||||
|
hbo max
|
||||||
|
bolton wanderers
|
||||||
|
barca vs atletico
|
||||||
|
kemi badenoch
|
||||||
|
warren zaïre-emery
|
||||||
|
barca
|
||||||
|
samuel west
|
||||||
|
barcelona fc
|
||||||
|
lamine yamal
|
||||||
|
hbomax
|
||||||
|
noah okafor
|
||||||
|
casemiro
|
||||||
|
talksport
|
||||||
|
lazio
|
||||||
|
leeds united fixtures
|
||||||
|
bruno fernandes
|
||||||
|
afc champions league
|
||||||
|
meteor
|
||||||
|
carlos queiroz
|
||||||
|
travel warning
|
||||||
|
tori amos
|
||||||
|
cloud
|
||||||
|
reading
|
||||||
|
rolls-royce smr
|
||||||
|
istanbul airport
|
||||||
|
a27
|
||||||
|
bridget phillipson
|
||||||
|
tottenham standings
|
||||||
|
may bank holiday 2026
|
||||||
|
toto wolff
|
||||||
london weather today
|
london weather today
|
||||||
bbc news latest
|
bbc news latest
|
||||||
premier league fixtures
|
premier league fixtures
|
||||||
|
|||||||
@@ -1,6 +1,49 @@
|
|||||||
|
los angeles dodgers
|
||||||
|
vandenberg launch schedule
|
||||||
|
ryan dunn
|
||||||
|
alex vesia
|
||||||
|
ken jennings
|
||||||
|
ucla baseball
|
||||||
|
padres standings
|
||||||
|
mets vs dodgers match player stats
|
||||||
|
bo bichette
|
||||||
|
jorge polanco
|
||||||
|
psg
|
||||||
|
barca
|
||||||
|
vix
|
||||||
|
fcb
|
||||||
|
barcelona schedule
|
||||||
|
univision
|
||||||
|
paramount
|
||||||
|
tarjeta roja
|
||||||
|
a knight of the seven kingdoms season 2
|
||||||
|
charlotte flair
|
||||||
|
usa network
|
||||||
|
natalie sago
|
||||||
|
carlos queiroz
|
||||||
|
carlos batista
|
||||||
|
katie boulter
|
||||||
|
levante - getafe
|
||||||
|
levante vs getafe
|
||||||
|
mcilroy green jacket presentation
|
||||||
|
man united vs leeds
|
||||||
|
7-eleven closing locations
|
||||||
|
cloud
|
||||||
|
sports
|
||||||
|
sony playstation
|
||||||
|
alaska airline
|
||||||
|
toronto
|
||||||
|
sydney
|
||||||
|
paris
|
||||||
|
tokyo
|
||||||
|
delhi
|
||||||
|
sykkuno drama
|
||||||
Los Angeles weather today
|
Los Angeles weather today
|
||||||
S&P 500 stock chart
|
S&P 500 stock chart
|
||||||
local coffee shops near me
|
local coffee shops near me
|
||||||
latest tech news
|
latest tech news
|
||||||
California traffic updates
|
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
|
||||||
|
|||||||
25
data/keywords/kw_VN.txt
Normal file
25
data/keywords/kw_VN.txt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
phan văn giang
|
||||||
|
nhà ở xã hội
|
||||||
|
club america
|
||||||
|
giàu
|
||||||
|
đỗ mỹ linh
|
||||||
|
sun group
|
||||||
|
hưng yên
|
||||||
|
nvl
|
||||||
|
américa đấu với nashville
|
||||||
|
neymar
|
||||||
|
VnExpress
|
||||||
|
Zing News
|
||||||
|
Thời tiết Hà Nội
|
||||||
|
Giá vàng hôm nay
|
||||||
|
Shopee VN
|
||||||
|
Tiki
|
||||||
|
Vietjet Air
|
||||||
|
Vietnam Airlines
|
||||||
|
Bóng đá trực tuyến
|
||||||
|
Lịch thi đấu Euro
|
||||||
|
Xổ số miền Bắc
|
||||||
|
Grab Vietnam
|
||||||
|
VTV Go
|
||||||
|
Học tiếng Anh
|
||||||
|
Du lịch Đà Lạt
|
||||||
263
data/map.json
263
data/map.json
@@ -1,101 +1,176 @@
|
|||||||
{
|
{
|
||||||
"version": "3.1.0",
|
"version": "3.5.0",
|
||||||
"updated_at": "2026-04-11",
|
"updated_at": "2026-04-15",
|
||||||
"countries": [
|
"continents": [
|
||||||
{
|
{
|
||||||
"id": "US",
|
"id": "ASIA",
|
||||||
"name": "United States (美国)",
|
"name": "亚太战区 (Asia-Pacific)",
|
||||||
"keyword_file": "kw_US.txt",
|
"countries": [
|
||||||
"states": [
|
{
|
||||||
|
"id": "JP",
|
||||||
|
"name": "Japan (日本)",
|
||||||
|
"keyword_file": "kw_JP.txt",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "Default",
|
||||||
|
"name": "Default State",
|
||||||
|
"cities": [
|
||||||
|
{ "id": "Tokyo", "name": "Tokyo (东京)" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "SG",
|
||||||
|
"name": "Singapore (新加坡)",
|
||||||
|
"keyword_file": "kw_SG.txt",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "Default",
|
||||||
|
"name": "Default State",
|
||||||
|
"cities": [
|
||||||
|
{ "id": "Singapore", "name": "Singapore (新加坡)" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "HK",
|
||||||
|
"name": "Hong Kong (香港)",
|
||||||
|
"keyword_file": "kw_HK.txt",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "Default",
|
||||||
|
"name": "Default State",
|
||||||
|
"cities": [
|
||||||
|
{ "id": "HongKong", "name": "Hong Kong (香港)" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "VN",
|
||||||
|
"name": "Vietnam (越南)",
|
||||||
|
"keyword_file": "kw_VN.txt",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "Default",
|
||||||
|
"name": "Default State",
|
||||||
|
"cities": [
|
||||||
|
{ "id": "Hanoi", "name": "Hanoi (河内)" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "EUROPE",
|
||||||
|
"name": "欧洲战区 (Europe)",
|
||||||
|
"countries": [
|
||||||
|
{
|
||||||
|
"id": "UK",
|
||||||
|
"name": "United Kingdom (英国)",
|
||||||
|
"keyword_file": "kw_UK.txt",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "Default",
|
||||||
|
"name": "Default State",
|
||||||
|
"cities": [
|
||||||
|
{ "id": "London", "name": "London (伦敦)" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "DE",
|
||||||
|
"name": "Germany (德国)",
|
||||||
|
"keyword_file": "kw_DE.txt",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "Default",
|
||||||
|
"name": "Default State",
|
||||||
|
"cities": [
|
||||||
|
{ "id": "Frankfurt", "name": "Frankfurt (法兰克福)" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FR",
|
||||||
|
"name": "France (法国)",
|
||||||
|
"keyword_file": "kw_FR.txt",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "Default",
|
||||||
|
"name": "Default State",
|
||||||
|
"cities": [
|
||||||
|
{ "id": "Paris", "name": "Paris (巴黎)" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "NL",
|
||||||
|
"name": "Netherlands (荷兰)",
|
||||||
|
"keyword_file": "kw_NL.txt",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "Default",
|
||||||
|
"name": "Default State",
|
||||||
|
"cities": [
|
||||||
|
{ "id": "Amsterdam", "name": "Amsterdam (阿姆斯特丹)" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ES",
|
||||||
|
"name": "Spain (西班牙)",
|
||||||
|
"keyword_file": "kw_ES.txt",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "Default",
|
||||||
|
"name": "Default State",
|
||||||
|
"cities": [
|
||||||
|
{ "id": "Madrid", "name": "Madrid (马德里)" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "AMERICAS",
|
||||||
|
"name": "美洲战区 (Americas)",
|
||||||
|
"countries": [
|
||||||
|
{
|
||||||
|
"id": "US",
|
||||||
|
"name": "United States (美国)",
|
||||||
|
"keyword_file": "kw_US.txt",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "CA",
|
||||||
|
"name": "California (加州)",
|
||||||
|
"cities": [
|
||||||
|
{ "id": "Los_Angeles", "name": "Los Angeles (洛杉矶)" },
|
||||||
|
{ "id": "San_Jose", "name": "San Jose (圣何塞)" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "CA",
|
"id": "CA",
|
||||||
"name": "California (加州)",
|
"name": "Canada (加拿大)",
|
||||||
"cities": [
|
"keyword_file": "kw_CA.txt",
|
||||||
{ "id": "Los_Angeles", "name": "Los Angeles (洛杉矶)" }
|
"states": [
|
||||||
]
|
{
|
||||||
}
|
"id": "Default",
|
||||||
]
|
"name": "Default State",
|
||||||
},
|
"cities": [
|
||||||
{
|
{ "id": "Toronto", "name": "Toronto (多伦多)" }
|
||||||
"id": "JP",
|
]
|
||||||
"name": "Japan (日本)",
|
}
|
||||||
"keyword_file": "kw_JP.txt",
|
|
||||||
"states": [
|
|
||||||
{
|
|
||||||
"id": "Default",
|
|
||||||
"name": "Default State",
|
|
||||||
"cities": [
|
|
||||||
{ "id": "Tokyo", "name": "Tokyo (东京)" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "UK",
|
|
||||||
"name": "United Kingdom (英国)",
|
|
||||||
"keyword_file": "kw_UK.txt",
|
|
||||||
"states": [
|
|
||||||
{
|
|
||||||
"id": "Default",
|
|
||||||
"name": "Default State",
|
|
||||||
"cities": [
|
|
||||||
{ "id": "London", "name": "London (伦敦)" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DE",
|
|
||||||
"name": "Germany (德国)",
|
|
||||||
"keyword_file": "kw_DE.txt",
|
|
||||||
"states": [
|
|
||||||
{
|
|
||||||
"id": "Default",
|
|
||||||
"name": "Default State",
|
|
||||||
"cities": [
|
|
||||||
{ "id": "Frankfurt", "name": "Frankfurt (法兰克福)" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "FR",
|
|
||||||
"name": "France (法国)",
|
|
||||||
"keyword_file": "kw_FR.txt",
|
|
||||||
"states": [
|
|
||||||
{
|
|
||||||
"id": "Default",
|
|
||||||
"name": "Default State",
|
|
||||||
"cities": [
|
|
||||||
{ "id": "Paris", "name": "Paris (巴黎)" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "SG",
|
|
||||||
"name": "Singapore (新加坡)",
|
|
||||||
"keyword_file": "kw_SG.txt",
|
|
||||||
"states": [
|
|
||||||
{
|
|
||||||
"id": "Default",
|
|
||||||
"name": "Default State",
|
|
||||||
"cities": [
|
|
||||||
{ "id": "Singapore", "name": "Singapore (新加坡)" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "HK",
|
|
||||||
"name": "Hong Kong (香港)",
|
|
||||||
"keyword_file": "kw_HK.txt",
|
|
||||||
"states": [
|
|
||||||
{
|
|
||||||
"id": "Default",
|
|
||||||
"name": "Default State",
|
|
||||||
"cities": [
|
|
||||||
{ "id": "HongKong", "name": "Hong Kong (香港)" }
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
20
data/regions/CA/Default/Toronto.json
Normal file
20
data/regions/CA/Default/Toronto.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"region_name": "Canada - Toronto",
|
||||||
|
"google_module": {
|
||||||
|
"base_lat": 43.6532,
|
||||||
|
"base_lon": -79.3832,
|
||||||
|
"lang_params": "hl=en&gl=CA",
|
||||||
|
"valid_url_suffix": "ca"
|
||||||
|
},
|
||||||
|
"trust_module": {
|
||||||
|
"white_urls": [
|
||||||
|
"https://en.wikipedia.org/wiki/Special:Random",
|
||||||
|
"https://www.canada.ca/en.html",
|
||||||
|
"https://www.cbc.ca/",
|
||||||
|
"https://www.thestar.com/",
|
||||||
|
"https://www.ctvnews.ca/",
|
||||||
|
"https://www.canadapost-postescanada.ca/",
|
||||||
|
"https://www.td.com/"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
20
data/regions/ES/Default/Madrid.json
Normal file
20
data/regions/ES/Default/Madrid.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"region_name": "Spain - Madrid",
|
||||||
|
"google_module": {
|
||||||
|
"base_lat": 40.4168,
|
||||||
|
"base_lon": -3.7038,
|
||||||
|
"lang_params": "hl=es&gl=ES",
|
||||||
|
"valid_url_suffix": "es"
|
||||||
|
},
|
||||||
|
"trust_module": {
|
||||||
|
"white_urls": [
|
||||||
|
"https://es.wikipedia.org/wiki/Especial:Aleatoria",
|
||||||
|
"https://www.elmundo.es/",
|
||||||
|
"https://www.elpais.com/",
|
||||||
|
"https://www.marca.com/",
|
||||||
|
"https://www.rtve.es/",
|
||||||
|
"https://www.zara.com/es/",
|
||||||
|
"https://www.elcorteingles.es/"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
20
data/regions/NL/Default/Amsterdam.json
Normal file
20
data/regions/NL/Default/Amsterdam.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"region_name": "Netherlands - Amsterdam",
|
||||||
|
"google_module": {
|
||||||
|
"base_lat": 52.3676,
|
||||||
|
"base_lon": 4.9041,
|
||||||
|
"lang_params": "hl=nl&gl=NL",
|
||||||
|
"valid_url_suffix": "nl"
|
||||||
|
},
|
||||||
|
"trust_module": {
|
||||||
|
"white_urls": [
|
||||||
|
"https://nl.wikipedia.org/wiki/Speciaal:Willekeurig",
|
||||||
|
"https://www.rijksoverheid.nl/",
|
||||||
|
"https://www.nos.nl/",
|
||||||
|
"https://www.bol.com/",
|
||||||
|
"https://www.nu.nl/",
|
||||||
|
"https://www.buienradar.nl/",
|
||||||
|
"https://www.telegraaf.nl/"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
21
data/regions/US/CA/San_Jose.json
Normal file
21
data/regions/US/CA/San_Jose.json
Normal 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/"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
20
data/regions/VN/Default/Hanoi.json
Normal file
20
data/regions/VN/Default/Hanoi.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"region_name": "Vietnam - Hanoi",
|
||||||
|
"google_module": {
|
||||||
|
"base_lat": 21.0285,
|
||||||
|
"base_lon": 105.8542,
|
||||||
|
"lang_params": "hl=vi&gl=VN",
|
||||||
|
"valid_url_suffix": "vn"
|
||||||
|
},
|
||||||
|
"trust_module": {
|
||||||
|
"white_urls": [
|
||||||
|
"https://vi.wikipedia.org/wiki/Đặc_biệt:Ngẫu_nhiên",
|
||||||
|
"https://chinhphu.vn/",
|
||||||
|
"https://vnexpress.net/",
|
||||||
|
"https://tuoitre.vn/",
|
||||||
|
"https://vtv.vn/",
|
||||||
|
"https://shopee.vn/",
|
||||||
|
"https://tiki.vn/"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
4004
data/user_agents.txt
4004
data/user_agents.txt
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,28 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# [新增] 提取仓库直链前缀变量,方便后续在官方库和私库间一键切换
|
# ==========================================================
|
||||||
|
# 脚本名称: install_master.sh (IP-Sentinel 控制中枢部署脚本 - 动态锚点版)
|
||||||
|
# 核心功能: 部署/卸载调度中枢、SQLite 资产管理、平滑热更新引擎
|
||||||
|
# ==========================================================
|
||||||
|
|
||||||
|
# 你的 GitHub 仓库 Raw 数据直链前缀
|
||||||
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||||
# 临时改为私库地址用于测试
|
# 临时改为私库地址用于测试
|
||||||
# REPO_RAW_URL="https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main"
|
# REPO_RAW_URL="https://git.94211762.xyz/hotyue/IP-Sentinel/raw/branch/main"
|
||||||
|
|
||||||
|
# [核心: 动态提取 Master 专属版本锚点 (KV 解析法)]
|
||||||
|
# 通过 grep 定位 MASTER_VERSION 行,再通过 cut 提取等号右侧的值
|
||||||
|
TARGET_VERSION=$(curl -s -m 3 "${REPO_RAW_URL}/version.txt" | grep "^MASTER_VERSION=" | cut -d'=' -f2 | tr -d '[:space:]')
|
||||||
|
|
||||||
|
# 🛡️ 兜底防线:如果网络波动拉取失败,启用内置的安全兜底版本
|
||||||
|
TARGET_VERSION=${TARGET_VERSION:-"3.5.0"}
|
||||||
|
|
||||||
MASTER_DIR="/opt/ip_sentinel_master"
|
MASTER_DIR="/opt/ip_sentinel_master"
|
||||||
DB_FILE="${MASTER_DIR}/sentinel.db"
|
DB_FILE="${MASTER_DIR}/sentinel.db"
|
||||||
|
|
||||||
echo "========================================================"
|
echo "========================================================"
|
||||||
# [修改] 将欢迎语改为更通用的文案,因为现在不仅能部署,还能卸载
|
# [修改] 将欢迎语改为更通用的文案,因为现在不仅能部署,还能卸载
|
||||||
echo " 🧠 欢迎使用 IP-Sentinel Master (控制中枢)"
|
echo " 🧠 欢迎使用 IP-Sentinel Master (控制中枢) v${TARGET_VERSION}"
|
||||||
echo "========================================================"
|
echo "========================================================"
|
||||||
|
|
||||||
# [新增] 交互式操作菜单:支持选择部署或调用卸载程序
|
# [新增] 交互式操作菜单:支持选择部署或调用卸载程序
|
||||||
@@ -29,8 +41,59 @@ if [ "$ACTION_CHOICE" == "2" ]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ================== [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"
|
||||||
|
|
||||||
|
# [v3.4.0 核心] 升级后立即同步/补录版本号至配置文件
|
||||||
|
if grep -q "^MASTER_VERSION=" "${MASTER_DIR}/master.conf"; then
|
||||||
|
sed -i "s/^MASTER_VERSION=.*/MASTER_VERSION=\"$TARGET_VERSION\"/" "${MASTER_DIR}/master.conf"
|
||||||
|
else
|
||||||
|
echo "MASTER_VERSION=\"$TARGET_VERSION\"" >> "${MASTER_DIR}/master.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "\033[32m✅ 已激活 [平滑升级模式],版本已锚定为 v${TARGET_VERSION}...\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. 环境依赖安装
|
# 1. 环境依赖安装
|
||||||
echo "[1/4] 安装核心依赖 (curl, jq, sqlite3)..."
|
echo -e "\n[1/4] 安装核心依赖 (curl, jq, sqlite3)..."
|
||||||
if [ -f /etc/debian_version ]; then
|
if [ -f /etc/debian_version ]; then
|
||||||
apt-get update -y >/dev/null 2>&1
|
apt-get update -y >/dev/null 2>&1
|
||||||
apt-get install -y curl jq sqlite3 procps >/dev/null 2>&1
|
apt-get install -y curl jq sqlite3 procps >/dev/null 2>&1
|
||||||
@@ -40,17 +103,25 @@ fi
|
|||||||
|
|
||||||
mkdir -p "$MASTER_DIR"
|
mkdir -p "$MASTER_DIR"
|
||||||
|
|
||||||
# 2. 交互配置机器人
|
# ==========================================================
|
||||||
echo -e "\n[2/4] 配置控制中枢机器人:"
|
# 🛑 如果是全新部署,才询问 Token 并写入配置
|
||||||
read -p "请输入 Telegram Bot Token: " TG_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
|
||||||
|
# IP-Sentinel Master 本地固化配置 (v${TARGET_VERSION})
|
||||||
|
MASTER_VERSION="$TARGET_VERSION"
|
||||||
TG_TOKEN="$TG_TOKEN"
|
TG_TOKEN="$TG_TOKEN"
|
||||||
DB_FILE="$DB_FILE"
|
DB_FILE="$DB_FILE"
|
||||||
MASTER_DIR="$MASTER_DIR"
|
MASTER_DIR="$MASTER_DIR"
|
||||||
EOF
|
EOF
|
||||||
|
fi
|
||||||
|
# 🛑 拦截块结束
|
||||||
|
|
||||||
# 3. 初始化 SQLite 数据库
|
# 3. 初始化 SQLite 数据库 (幂等操作,升级模式下可安全修补表结构)
|
||||||
echo -e "\n[3/4] 正在初始化 SQLite 数据库表结构..."
|
echo -e "\n[3/4] 正在初始化 SQLite 数据库表结构..."
|
||||||
sqlite3 "$DB_FILE" <<EOF
|
sqlite3 "$DB_FILE" <<EOF
|
||||||
CREATE TABLE IF NOT EXISTS nodes (
|
CREATE TABLE IF NOT EXISTS nodes (
|
||||||
@@ -84,7 +155,25 @@ rm -f /tmp/cron_master
|
|||||||
# 立刻启动
|
# 立刻启动
|
||||||
pgrep -f tg_master.sh >/dev/null || nohup bash "${MASTER_DIR}/tg_master.sh" >/dev/null 2>&1 &
|
pgrep -f tg_master.sh >/dev/null || nohup bash "${MASTER_DIR}/tg_master.sh" >/dev/null 2>&1 &
|
||||||
|
|
||||||
|
# ================== [v3.2.2 优化: 战报文案分流] ==================
|
||||||
echo "========================================================"
|
echo "========================================================"
|
||||||
echo "🎉 Master 控制中枢部署完成!"
|
if [ "$UPGRADE_MODE" == "true" ]; then
|
||||||
echo "🤖 机器人现已开始全局接客,等待边缘节点注册。"
|
echo "🎉 Master 控制中枢平滑热更新完成!"
|
||||||
|
echo "🤖 新版中枢引擎已接管数据库,继续等待边缘节点汇报。"
|
||||||
|
else
|
||||||
|
echo "🎉 Master 控制中枢部署完成!"
|
||||||
|
echo "🤖 机器人现已开始全局接客,等待边缘节点注册。"
|
||||||
|
fi
|
||||||
echo "========================================================"
|
echo "========================================================"
|
||||||
|
# =================================================================
|
||||||
|
|
||||||
|
# ================== [v3.1.2 新增: 玻璃房透明装机统计] ==================
|
||||||
|
echo -e "\n📡 正在向开源社区汇报装机量 (完全匿名,不收集IP)..."
|
||||||
|
MASTER_COUNT=$(curl -s -m 3 "https://ip-sentinel-count.samanthaestime296.workers.dev/ping/master" || echo "")
|
||||||
|
|
||||||
|
if [ -n "$MASTER_COUNT" ] && [[ "$MASTER_COUNT" =~ ^[0-9]+$ ]]; then
|
||||||
|
echo -e "\033[32m✅ 感谢您成为全球第 ${MASTER_COUNT} 名 IP-Sentinel 指挥官!\033[0m"
|
||||||
|
else
|
||||||
|
echo -e "\033[32m✅ 感谢您建立 IP-Sentinel 司令部!\033[0m"
|
||||||
|
fi
|
||||||
|
echo -e "\n"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
# 脚本名称: tg_master.sh (Master 端调度枢纽 V3.0.4 动态签名版)
|
# 脚本名称: tg_master.sh (Master 端调度枢纽 - 动态锚点版)
|
||||||
# 核心功能: 监听 TG、操作 SQLite、Webhook 精准调度、403权限拦截、僵尸节点清理
|
# 核心功能: 监听 TG、操作 SQLite、Webhook 精准调度、403权限拦截、僵尸节点清理
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
|
|
||||||
@@ -9,7 +9,13 @@ CONF="/opt/ip_sentinel_master/master.conf"
|
|||||||
[ ! -f "$CONF" ] && exit 1
|
[ ! -f "$CONF" ] && exit 1
|
||||||
source "$CONF"
|
source "$CONF"
|
||||||
|
|
||||||
OFFSET_FILE="/tmp/tg_master_offset"
|
# [核心: 运行态版本继承与云通信地址]
|
||||||
|
REPO_RAW_URL="https://raw.githubusercontent.com/hotyue/IP-Sentinel/main"
|
||||||
|
# MASTER_VERSION 已经在上方的 source "$CONF" 中被载入
|
||||||
|
# 如果本地极度陈旧没有该变量,才给定一个基础兜底值,避免变量为空导致崩溃
|
||||||
|
MASTER_VERSION=${MASTER_VERSION:-"3.5.0"}
|
||||||
|
|
||||||
|
OFFSET_FILE="${MASTER_DIR}/.tg_offset"
|
||||||
[[ -f $OFFSET_FILE ]] || echo "0" > $OFFSET_FILE
|
[[ -f $OFFSET_FILE ]] || echo "0" > $OFFSET_FILE
|
||||||
|
|
||||||
# --- 工具函数 ---
|
# --- 工具函数 ---
|
||||||
@@ -54,6 +60,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
|
while true; do
|
||||||
OFFSET=$(cat $OFFSET_FILE)
|
OFFSET=$(cat $OFFSET_FILE)
|
||||||
@@ -79,33 +90,60 @@ while true; do
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 1. 节点注册通道 (v3.0.1 终极防注入补丁)
|
# 1. 节点注册通道 (V3.1.3 大区拓扑升级版)
|
||||||
# ==========================================
|
# ==========================================
|
||||||
if [[ "$TEXT" == *"#REGISTER#"* ]]; then
|
if [[ "$TEXT" == *"#REGISTER#"* ]]; then
|
||||||
REG_LINE=$(echo "$TEXT" | grep "#REGISTER#" | head -n 1 | tr -d '\` ')
|
REG_LINE=$(echo "$TEXT" | grep "#REGISTER#" | head -n 1 | tr -d '\` ')
|
||||||
IFS='|' read -r MAGIC RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
|
|
||||||
|
|
||||||
# 🛡️ 强制字符白名单过滤:物理抹杀所有 SQL 注入特殊字符
|
# V3.1.3 兼容性拆包: 判断是新版协议 (5个字段) 还是老版协议 (4个字段)
|
||||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-') # ChatID 只能是数字和负号
|
FIELD_COUNT=$(echo "$REG_LINE" | awk -F'|' '{print NF}')
|
||||||
NODE_NAME=$(echo "$RAW_NODE" | tr -cd 'a-zA-Z0-9_.-' | cut -c 1-30) # 节点名限字母数字横杠下划线
|
if [ "$FIELD_COUNT" -ge 5 ]; then
|
||||||
AGENT_IP=$(echo "$RAW_IP" | tr -cd 'a-zA-Z0-9.:\[\]-' | cut -c 1-50) # IP 格式限制
|
IFS='|' read -r MAGIC RAW_REGION RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
|
||||||
AGENT_PORT=$(echo "$RAW_PORT" | tr -cd '0-9' | cut -c 1-5) # 端口只能是数字
|
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
|
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 攻击渗透。"
|
send_msg "$CHAT_ID" "⛔ **安全拦截**:禁止注册内网或回环 IP,防止 SSRF 攻击渗透。"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 异常拦截:如果核心字段被过滤成了空值,说明是恶意请求,直接抛弃
|
|
||||||
if [ -z "$NODE_NAME" ] || [ -z "$AGENT_IP" ] || [ -z "$AGENT_PORT" ] || [ -z "$CHAT_ID" ]; then
|
if [ -z "$NODE_NAME" ] || [ -z "$AGENT_IP" ] || [ -z "$AGENT_PORT" ] || [ -z "$CHAT_ID" ]; then
|
||||||
send_msg "$CHAT_ID" "⛔ **安全拦截**:检测到非法注册载荷,请求已拒绝。"
|
send_msg "$CHAT_ID" "⛔ **安全拦截**:检测到非法注册载荷,请求已拒绝。"
|
||||||
continue
|
continue
|
||||||
fi
|
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 字段
|
||||||
send_msg "$CHAT_ID" "✅ 司令部已确认!节点接入成功: \`$NODE_NAME\` ($AGENT_IP:$AGENT_PORT)"
|
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" "✅ **司令部确认 (v${MASTER_VERSION})**\n节点接入成功: \`$NODE_NAME\`\n地址: \`$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="🇫🇷" ;;
|
||||||
|
"CA") FLAG="🇨🇦" ;; "AU") FLAG="🇦🇺" ;; "KR") FLAG="🇰🇷" ;; "NL") FLAG="🇳🇱" ;; "BR") FLAG="🇧🇷" ;; "IN") FLAG="🇮🇳" ;; "TW") 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
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -114,8 +152,16 @@ while true; do
|
|||||||
# ==========================================
|
# ==========================================
|
||||||
case "$TEXT" in
|
case "$TEXT" in
|
||||||
"/start"|"/menu")
|
"/start"|"/menu")
|
||||||
|
# [核心: 抓取云端最新 Master 版本 (KV 解析法)]
|
||||||
|
REMOTE_VER=$(curl -s -m 2 "${REPO_RAW_URL}/version.txt" | grep "^MASTER_VERSION=" | cut -d'=' -f2 | tr -d '[:space:]')
|
||||||
|
VER_INFO="当前版本: \`v${MASTER_VERSION}\`"
|
||||||
|
|
||||||
|
if [ -n "$REMOTE_VER" ] && [ "$REMOTE_VER" != "$MASTER_VERSION" ]; then
|
||||||
|
VER_INFO="${VER_INFO}\n✨ **发现新版本**: \`v${REMOTE_VER}\` (请尽快更新主控)"
|
||||||
|
fi
|
||||||
|
|
||||||
BTNS="[[{\"text\":\"🖥️ 我的节点列表\",\"callback_data\":\"list_nodes\"}], [{\"text\":\"🚀 全节点日报汇总\",\"callback_data\":\"all_reports\"}], [{\"text\":\"🛠️ 全节点一键维护\",\"callback_data\":\"all_run\"}]]"
|
BTNS="[[{\"text\":\"🖥️ 我的节点列表\",\"callback_data\":\"list_nodes\"}], [{\"text\":\"🚀 全节点日报汇总\",\"callback_data\":\"all_reports\"}], [{\"text\":\"🛠️ 全节点一键维护\",\"callback_data\":\"all_run\"}]]"
|
||||||
send_ui "$CHAT_ID" "🛡️ **IP-Sentinel 司令部**\n欢迎回来,长官。请下达指令:" "$BTNS"
|
send_ui "$CHAT_ID" "🛡️ **IP-Sentinel 司令部**\n${VER_INFO}\n\n欢迎回来,长官。请下达指令:" "$BTNS"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
"all_reports")
|
"all_reports")
|
||||||
@@ -149,16 +195,57 @@ while true; do
|
|||||||
# ====================================================================
|
# ====================================================================
|
||||||
|
|
||||||
"list_nodes")
|
"list_nodes")
|
||||||
NODE_LIST=$(db_exec "SELECT node_name FROM nodes WHERE chat_id='$CHAT_ID';")
|
# 【V3.1.3】一级菜单:大区聚合并列出数量
|
||||||
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" "⚠️ 您名下暂无在线节点,请先在边缘机执行部署。"
|
send_msg "$CHAT_ID" "⚠️ 您名下暂无在线节点,请先在边缘机执行部署。"
|
||||||
else
|
else
|
||||||
BTNS="["
|
BTNS="["
|
||||||
for N in $NODE_LIST; do
|
while IFS='|' read -r REGION_NAME NODE_COUNT; do
|
||||||
BTNS="$BTNS[{\"text\":\"🖥️ $N\",\"callback_data\":\"manage:$N\"}],"
|
[ -z "$REGION_NAME" ] && REGION_NAME="UNKNOWN"
|
||||||
done
|
FLAG="🌐"
|
||||||
|
case "$REGION_NAME" in
|
||||||
|
"US") FLAG="🇺🇸" ;; "JP") FLAG="🇯🇵" ;; "HK") FLAG="🇭🇰" ;;
|
||||||
|
"SG") FLAG="🇸🇬" ;; "UK"|"GB") FLAG="🇬🇧" ;; "DE") FLAG="🇩🇪" ;; "FR") FLAG="🇫🇷" ;;
|
||||||
|
"CA") FLAG="🇨🇦" ;; "AU") FLAG="🇦🇺" ;; "KR") FLAG="🇰🇷" ;; "NL") FLAG="🇳🇱" ;; "BR") FLAG="🇧🇷" ;; "IN") FLAG="🇮🇳" ;; "TW") FLAG="🇹🇼" ;;
|
||||||
|
esac
|
||||||
|
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
|
||||||
|
done <<< "$REGION_DATA"
|
||||||
BTNS="${BTNS%,}]"
|
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
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@@ -166,7 +253,7 @@ while true; do
|
|||||||
# 🛡️ 强制过滤节点名,防止面板渲染时发生 XSS 或注入
|
# 🛡️ 强制过滤节点名,防止面板渲染时发生 XSS 或注入
|
||||||
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
||||||
# 【核心升级】拆分下发按钮,精准对应 Google 与 Trust 两个模块,并排版为 3 行 2 列
|
# 【核心升级】拆分下发按钮,精准对应 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"
|
send_ui "$CHAT_ID" "⚙️ **目标锁定**: \`$TARGET_NODE\`\n请选择战术动作:" "$BTNS"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@@ -178,16 +265,23 @@ while true; do
|
|||||||
db_exec "DELETE FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
|
db_exec "DELETE FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
|
||||||
send_msg "$CHAT_ID" "🗑️ 节点 \`$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" "⚠️ 当前司令部已无任何节点挂载。"
|
send_msg "$CHAT_ID" "⚠️ 当前司令部已无任何节点挂载。"
|
||||||
else
|
else
|
||||||
BTNS="["
|
BTNS="["
|
||||||
for N in $NODE_LIST; do
|
while IFS='|' read -r REGION_NAME NODE_COUNT; do
|
||||||
BTNS="$BTNS[{\"text\":\"🖥️ $N\",\"callback_data\":\"manage:$N\"}],"
|
[ -z "$REGION_NAME" ] && REGION_NAME="UNKNOWN"
|
||||||
done
|
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%,}]"
|
BTNS="${BTNS%,}]"
|
||||||
send_ui "$CHAT_ID" "🔍 刷新后的节点列表:" "$BTNS"
|
send_ui "$CHAT_ID" "🌍 刷新后的全视界雷达:" "$BTNS"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
# 脚本名称: uninstall_master.sh (IP-Sentinel Master 一键卸载脚本)
|
# 脚本名称: uninstall_master.sh (IP-Sentinel Master 一键卸载脚本 - 动态锚点版)
|
||||||
# 核心功能: 终止调度进程、清理看门狗定时任务、抹除数据库与配置
|
# 核心功能: 终止调度进程、清理看门狗定时任务、抹除数据库与配置
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
|
|
||||||
MASTER_DIR="/opt/ip_sentinel_master"
|
MASTER_DIR="/opt/ip_sentinel_master"
|
||||||
|
CONF_FILE="${MASTER_DIR}/master.conf"
|
||||||
|
|
||||||
echo "========================================================"
|
echo "========================================================"
|
||||||
echo " 🗑️ 准备卸载 IP-Sentinel Master (控制中枢)"
|
echo " 🗑️ 准备卸载 IP-Sentinel Master (控制中枢)"
|
||||||
|
|
||||||
|
# [v3.4.0 优化] 卸载前读取并播报中枢版本号
|
||||||
|
if [ -f "$CONF_FILE" ]; then
|
||||||
|
MASTER_VER=$(grep "^MASTER_VERSION=" "$CONF_FILE" | cut -d'"' -f2)
|
||||||
|
[ -n "$MASTER_VER" ] && echo " 📍 目标版本: v${MASTER_VER}"
|
||||||
|
fi
|
||||||
echo "========================================================"
|
echo "========================================================"
|
||||||
|
|
||||||
echo -e "\n⚠️ 警告: 此操作将永久删除包含所有节点档案的 SQLite 数据库!"
|
echo -e "\n⚠️ 警告: 此操作将永久删除包含所有节点档案的 SQLite 数据库!"
|
||||||
@@ -20,7 +27,8 @@ fi
|
|||||||
|
|
||||||
# 1. 停止运行中的 Master 守护进程
|
# 1. 停止运行中的 Master 守护进程
|
||||||
echo "[1/3] 正在终止后台中枢调度进程..."
|
echo "[1/3] 正在终止后台中枢调度进程..."
|
||||||
pgrep -f tg_master.sh | xargs -r kill -9 >/dev/null 2>&1
|
# [优化] 使用 pkill 替代 pgrep | xargs,指令更短、容错率更高
|
||||||
|
pkill -9 -f "tg_master.sh" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
# 2. 清除看门狗定时任务 (Cron)
|
# 2. 清除看门狗定时任务 (Cron)
|
||||||
echo "[2/3] 正在清理系统定时任务 (Cron)..."
|
echo "[2/3] 正在清理系统定时任务 (Cron)..."
|
||||||
|
|||||||
83
scripts/fetch_trends.py
Normal file
83
scripts/fetch_trends.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import urllib.request
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
|
# ================== [路径防弹装甲] ==================
|
||||||
|
# 无论在哪里执行该脚本,都能精准反推项目根目录
|
||||||
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
PROJECT_ROOT = os.path.dirname(SCRIPT_DIR)
|
||||||
|
|
||||||
|
MAP_JSON_PATH = os.path.join(PROJECT_ROOT, "data", "map.json")
|
||||||
|
DATA_DIR = os.path.join(PROJECT_ROOT, "data", "keywords")
|
||||||
|
# ====================================================
|
||||||
|
|
||||||
|
# 特殊战区代码映射 (Google Trends RSS 要求)
|
||||||
|
GEO_FIX = {'UK': 'GB'}
|
||||||
|
|
||||||
|
HEADERS = {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_active_regions():
|
||||||
|
"""动态提取 map.json 中的战区 (适配 v3.5.0 大洲战区降维架构)"""
|
||||||
|
try:
|
||||||
|
with open(MAP_JSON_PATH, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
regions = []
|
||||||
|
# 第一层穿透:遍历所有大洲战区 (continents)
|
||||||
|
for continent in data.get('continents', []):
|
||||||
|
# 第二层穿透:遍历大洲下的所有国家 (countries)
|
||||||
|
for country in continent.get('countries', []):
|
||||||
|
if 'id' in country:
|
||||||
|
regions.append(country['id'])
|
||||||
|
return regions
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ [读取地图失败]: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def fetch_trends(region_code):
|
||||||
|
"""从 Google Trends 抓取当日热搜"""
|
||||||
|
geo = GEO_FIX.get(region_code, region_code)
|
||||||
|
url = f"https://trends.google.com/trending/rss?geo={geo}"
|
||||||
|
try:
|
||||||
|
req = urllib.request.Request(url, headers=HEADERS)
|
||||||
|
with urllib.request.urlopen(req, timeout=10) as response:
|
||||||
|
xml_data = response.read()
|
||||||
|
root = ET.fromstring(xml_data)
|
||||||
|
return [re.sub(r'[\n\r\t]', ' ', item.find('title').text).strip()
|
||||||
|
for item in root.findall('./channel/item')
|
||||||
|
if item.find('title') is not None]
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ {region_code} 抓取异常: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def update_file(region, new_words):
|
||||||
|
"""滑动窗口更新,保留 200 条最热记录"""
|
||||||
|
os.makedirs(DATA_DIR, exist_ok=True)
|
||||||
|
file_path = os.path.join(DATA_DIR, f"kw_{region}.txt")
|
||||||
|
old_words = []
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
with open(file_path, 'r', encoding='utf-8') as f:
|
||||||
|
old_words = [l.strip() for l in f if l.strip()]
|
||||||
|
|
||||||
|
# 新词排在最前面,去重
|
||||||
|
combined = new_words + [w for w in old_words if w not in new_words]
|
||||||
|
final_list = combined[:200]
|
||||||
|
|
||||||
|
with open(file_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write('\n'.join(final_list) + '\n')
|
||||||
|
print(f"✅ [同步完成] {region}: 注入 {len(new_words)} 条新热点")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
regions = get_active_regions()
|
||||||
|
if not regions:
|
||||||
|
print("🛑 未发现活跃战区,请检查 map.json")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
for r in regions:
|
||||||
|
print(f"📡 正在拉取 {r} 战区情报...")
|
||||||
|
words = fetch_trends(r)
|
||||||
|
if words:
|
||||||
|
update_file(r, words)
|
||||||
77
scripts/ua_generator.py
Normal file
77
scripts/ua_generator.py
Normal 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 条高保真绝对坐标指纹库!")
|
||||||
75
telemetry/worker.js
Normal file
75
telemetry/worker.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
// IP-Sentinel Glasshouse Telemetry (全透明装机量统计中枢)
|
||||||
|
// 部署环境: Cloudflare Workers + KV
|
||||||
|
// 隐私声明: 绝对不采集、不存储用户的 IP 地址、Header、Token 及任何系统特征参数。仅做纯粹的原子累加。
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async fetch(request, env) {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const path = url.pathname;
|
||||||
|
|
||||||
|
// 全局跨域头,确保 GitHub README 的 Shields.io 徽章能正常读取
|
||||||
|
const corsHeaders = {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Allow-Methods": "GET",
|
||||||
|
};
|
||||||
|
|
||||||
|
// 核心原子操作:无情的 +1 机器
|
||||||
|
async function incrementCounter(key) {
|
||||||
|
let count = await env.SENTINEL_KV.get(key);
|
||||||
|
count = count ? parseInt(count) + 1 : 1;
|
||||||
|
await env.SENTINEL_KV.put(key, count.toString());
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCounter(key) {
|
||||||
|
let count = await env.SENTINEL_KV.get(key);
|
||||||
|
return count ? parseInt(count) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Agent (哨兵) 部署触发接口
|
||||||
|
if (path === '/ping/agent') {
|
||||||
|
const count = await incrementCounter('agent_count');
|
||||||
|
return new Response(count.toString(), { headers: corsHeaders });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Master (指挥部) 部署触发接口
|
||||||
|
if (path === '/ping/master') {
|
||||||
|
const count = await incrementCounter('master_count');
|
||||||
|
return new Response(count.toString(), { headers: corsHeaders });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. GitHub README Agent 徽章接口 (输出给 Shields.io)
|
||||||
|
if (path === '/stats/agent') {
|
||||||
|
const count = await getCounter('agent_count');
|
||||||
|
const shield = {
|
||||||
|
schemaVersion: 1,
|
||||||
|
label: "Agent Nodes",
|
||||||
|
message: count.toString(),
|
||||||
|
color: "blue"
|
||||||
|
};
|
||||||
|
return new Response(JSON.stringify(shield), {
|
||||||
|
headers: { ...corsHeaders, "Content-Type": "application/json" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. GitHub README Master 徽章接口 (输出给 Shields.io)
|
||||||
|
if (path === '/stats/master') {
|
||||||
|
const count = await getCounter('master_count');
|
||||||
|
const shield = {
|
||||||
|
schemaVersion: 1,
|
||||||
|
label: "Master Commands",
|
||||||
|
message: count.toString(),
|
||||||
|
color: "orange"
|
||||||
|
};
|
||||||
|
return new Response(JSON.stringify(shield), {
|
||||||
|
headers: { ...corsHeaders, "Content-Type": "application/json" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response("IP-Sentinel Glasshouse Telemetry API (No IP Logged, 100% Transparent)", { status: 200 });
|
||||||
|
} catch (err) {
|
||||||
|
return new Response("Error", { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
2
version.txt
Normal file
2
version.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
AGENT_VERSION=3.5.1
|
||||||
|
MASTER_VERSION=3.5.1
|
||||||
Reference in New Issue
Block a user