Compare commits

..

42 Commits

Author SHA1 Message Date
hotyue
539de62eee chore(release): bump version to v3.5.3 2026-04-17 03:00:11 +00:00
hotyue
d514106e65 docs: [v3.5.3] 重构 README 极客特征文案,合并冗余架构特性并引入常青化排版 2026-04-17 03:00:05 +00:00
hotyue
7f9c8a4dea feat(master): [v3.5.3] 引入L5降维控制面板与内联UI重绘,实装红绿灯状态机与模块动态启停 (Resolves #17, Resolves #19) 2026-04-17 02:40:27 +00:00
hotyue
31014e571d feat(master): [v3.5.3] 升级 SQLite 初始表结构,新增模块状态追踪字段以支持红绿灯 UI (Resolves #19) 2026-04-17 02:40:23 +00:00
hotyue
f5aa68a8dc feat(core): [v3.5.3] Webhook 引擎新增模块动态启停(Toggle)路由,支持安全重写本地配置 (Resolves #19) 2026-04-17 02:40:19 +00:00
hotyue
63bbbd549e feat(agent): [v3.5.3] 优化安装向导,默认全量部署养护模块以支持远端动态控制 (Resolves #19) 2026-04-17 02:40:14 +00:00
hotyue
5e40ed426b chore: revert failed v3.6.0 attempts, rollback to safe state 2026-04-17 02:24:07 +00:00
hotyue
aebf3a9e90 fix(core): [v3.6.0] 终极修复 OTA 死锁:解决 curl 管道吞噬脚本 Bug,并应用底层网络脱壳机制 2026-04-17 02:17:32 +00:00
hotyue
8a3d7c305b fix(core): [v3.6.0] 彻底重构 OTA 升级调度,引入外挂延时脚本实现 100% 的网络脱壳与静默重启 2026-04-17 02:09:05 +00:00
hotyue
2d580eaea2 fix(core): [v3.6.0] 彻底解决 OTA 升级 TCP 通讯超时 Bug,引入 close_fds 剥离套接字继承机制 2026-04-17 01:59:29 +00:00
hotyue
d7ab695372 fix(core): [v3.6.0] 彻底解决 OTA 升级 TCP 通讯超时 Bug,引入异步守护线程与套接字剥离机制 2026-04-17 01:48:46 +00:00
hotyue
0c250dfd17 fix(core): [v3.6.0] 修复 OTA 升级时的竞态自杀 Bug,引入 TCP 强制刷新与 3 秒延迟脱壳机制 2026-04-17 01:44:40 +00:00
hotyue
52b12c7057 feat(master): [v3.6.0] 深度重构中枢调度,引入L5降维控制面板、红绿灯模块启停与全网 OTA 静默升级 (Resolves #17, Resolves #19) 2026-04-17 01:19:27 +00:00
hotyue
2166753569 feat(master): [v3.6.0] 司令部部署引入 OTA 下发权限控制,重构 SQLite 初始表结构以支持状态机 (Resolves #17, Resolves #19) 2026-04-17 01:19:20 +00:00
hotyue
46e418dfd0 feat(core): [v3.6.0] Webhook 引擎新增 OTA 升级与动态启停路由,实装底层权限物理熔断机制 (Resolves #17, Resolves #19) 2026-04-17 01:19:13 +00:00
hotyue
673e0ce3e6 feat(agent): [v3.6.0] 重构安装向导,引入静默 OTA 嗅探机制与双端授权防线,默认全量部署养护模块 (Resolves #17, Resolves #19) 2026-04-17 01:19:06 +00:00
github-actions[bot]
6203249782 chore(data): 🤖 自动机兵:刷新全战区热点词库 [2026-04-16] 2026-04-16 19:25:36 +00:00
hotyue
05e708ed21 fix(master): [v3.5.2] 修复交互向导首选项的默认值丢失问题,防止直接回车导致主控数据库被误删 2026-04-16 07:57:02 +00:00
hotyue
c4faa102cf fix(installer): [v3.5.2] 修复交互向导首选项的默认值丢失问题,防止直接回车导致平滑升级失效被误删档 2026-04-16 07:52:45 +00:00
hotyue
4a1b32278c fix(core): [v3.5.2] 补充 version.txt 尾部换行符,防止环境变量读取截断 2026-04-16 06:54:29 +00:00
hotyue
584ead387e docs: [v3.5.2] 更新 README 战报日志,全系版本号正式锚定 v3.5.2 2026-04-16 06:52:06 +00:00
hotyue
efdc62ba68 feat(map): 更新全球战区拓扑,挂载考文垂与蒙特利尔节点索引 2026-04-16 06:32:47 +00:00
hotyue
67618587e4 feat(data): 新增节点数据 - 加拿大蒙特利尔 (CA, Montreal) 2026-04-16 06:32:42 +00:00
hotyue
a2c045a841 feat(data): 新增节点数据 - 英国考文垂 (UK, Coventry) 2026-04-16 06:32:37 +00:00
hotyue
9b35c87cb4 fix(master): [v3.5.2] 修复 Telegram 纯文本 API 下发消息时的换行符 (%0A) 转义问题 2026-04-16 06:02:51 +00:00
hotyue
e6c6e66e4b feat(agent): [v3.5.2] 剥除改名后的冗余 TG 推送报文,实现静默修改配置与底层 HTTP 回执 2026-04-16 05:51:04 +00:00
hotyue
f1d36a2148 feat(master): [v3.5.2] 实现全自动丝滑改名,接收 Agent 成功回执后瞬间刷新本地 SQLite 数据库 2026-04-16 05:50:58 +00:00
hotyue
41906d0570 revert: 紧急回滚至稳定版 9768bed (修复加密升级导致的通讯中断) 2026-04-16 03:24:11 +00:00
hotyue
dc7d1c0f40 feat: [v3.5.2] 终极安全与逻辑闭环补丁
1. 升级军工级 HMAC 签名:将数据负载 (Base64) 卷入哈希,封死中间人篡改路径。
2. 引入高熵复合密钥:结合 CHAT_ID 与 TG_TOKEN,大幅提升抗暴力破解强度。
3. 实现全自动改名闭环:Master 确认 Agent 修改成功后自动同步数据库,彻底告别手动复制。
2026-04-16 03:14:12 +00:00
hotyue
9768bed637 fix(master): [v3.5.2] 废除原生 tr 命令的失效中文白名单,改用黑名单过滤,确保中文字符完美送入 Base64 编码 2026-04-16 02:43:52 +00:00
hotyue
fa202a0405 fix(core): [v3.5.2] 采用 Base64 编码彻底重构别名同步链路,免疫 WAF 拦截与中英文符号解析崩溃 2026-04-16 02:24:50 +00:00
hotyue
b8bcd09134 fix(agent): [v3.5.2] 修复别名同步时的中文 UTF-8 编码崩溃,改用 curl 绕过 WAF 拦截,并拦截下划线防止 TG 面板渲染异常 2026-04-16 01:55:57 +00:00
hotyue
ef8dc033cc feat(master): [v3.5.2] 中枢支持解析 6 字段注册包,面板适配别名并新增重命名状态机 (Resolves #9) 2026-04-16 01:35:16 +00:00
hotyue
03a54bcca0 chore(master): [v3.5.2] 完善全新部署时的 SQLite 表结构 (增加 region 与 node_alias) 2026-04-16 01:35:16 +00:00
hotyue
f4f93d8955 feat(agent): [v3.5.2] 每日战报全面适配 NODE_ALIAS 展示字段 2026-04-16 01:35:16 +00:00
hotyue
07cac792f0 feat(agent): [v3.5.2] 守护进程新增 /trigger_rename 接口,实现免注入安全重命名 2026-04-16 01:35:16 +00:00
hotyue
50edad9e25 feat(agent): [v3.5.2] 部署引擎引入双轨身份,支持自定义节点展示别名 2026-04-16 01:35:16 +00:00
hotyue
69edf12620 feat(map): [v3.5.2] 全球大地图更新,正式集成台湾 (TW) 及美国本土 6 大新州节点 2026-04-16 01:35:16 +00:00
hotyue
0b448e2b7e data(US): [v3.5.2] 深度扩容美国版图,新增 IL, UT, WA, NV, NC, OR 六大节点 (Resolves #15, 感谢 NQ 论坛 @yct002, @Ideal 提交的需求) 2026-04-16 01:35:16 +00:00
github-actions[bot]
d7b95136dd data(TW): [v3.5.2] 新增台湾战区数据与台北锚点 (Resolves #11) 2026-04-16 01:35:16 +00:00
github-actions[bot]
80d74111c4 chore(data): 🤖 自动机兵:刷新全战区热点词库 [2026-04-15] 2026-04-15 19:37:24 +00:00
hotyue
223d7c4b56 docs(readme): 更新项目架构图,同步 v3.5.1 双端版本解耦信标说明 2026-04-15 06:47:43 +00:00
29 changed files with 744 additions and 133 deletions

View File

@@ -10,25 +10,18 @@
专为解决 VPS IP 被 Google 等数据库错误定位到中国大陆/香港俗称“送中”等问题而生。IP-Sentinel 已从单机脚本全面跃升为 **Master-Agent 分布式架构**。它像影子一样潜伏在全球各地的服务器后台,通过高度拟真的真实用户行为为你默默积累 IP 权重,并允许你通过 Telegram 随时随地对整个舰队进行毫秒级“点名”与“遥控”。
## ✨ 核心极客特性 (Evolution History)
## ✨ 核心极客特性 (Core Architecture)
- 🌍 **[v3.5.0] 大洲战区与降维引擎 (Continental Grouping)**:随着全球版图的极速扩张,彻底重构底层地图索引。引入“战区(大洲)-国家-省州-城市”四级降维解析菜单,完美承载未来数十个国家的扩容,终端交互界面永远清爽干练
- 🧬 **[v3.5.0] SSOT 动态版本溯源 (Single Source of Truth)**:全系脚本彻底消灭硬编码版本号!引入企业级 DevOps 理念,部署时动态抓取云端信标并固化落盘,常驻进程与日志绝对继承本地基因,实现“改一处,全网同步”的极致架构
- 🎯 **[v3.4.0] 版本锚点与路由中枢 (Version-Linked Epoch)**:彻底告别“盲盒式更新”,全系引入全局版本号机制。边缘节点具备“身份自知”能力,安装脚本根据本地版本执行精准的路由跳转,实现新老架构的智能化无损跃迁
- 📡 **[v3.4.0] OTA 实时版本探针 (Version-Aware Radar)**:边缘哨兵现已接入云端“北极星”校准。每日战报自动扫描 GitHub 最新发布状态,发现版本落后即刻在 Telegram 战报底部亮起 OTA 预警,消除指挥官与前线的信息差
- 📡 **[v3.3.0] OTA 动态活体词库 (Dynamic Trends)**:彻底废弃静态搜索词,引入 GitHub Actions 云端流水线。每天自动抓取全战区当日 Google 热搜榜单,并通过边缘节点每日静默同步,让搜索行为永远贴合当地当天的真实网络脉搏
- 🔀 **[v3.3.0] 智能错峰调度 (Thundering Herd Mitigation)**:首创节点部署时间戳锚定逻辑。边缘节点按需智能分频(每日拉取词库,每月按 30 天周期错峰拉取千万级指纹库),化解“惊群效应”,抹平统一升级时的并发特征,隐匿于无形。
- 🎯 **[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)**:守护版图横跨亚、欧、美三大洲。为每个国家注入极其硬核的“原生本地化”搜索词库与本土高权重站点(如政府、权威媒体、高铁网),真正实现拟真融入。
- 🎛️ **L5 降维中枢与动态状态机 (Microservices Console)**:引入四级战区降维解析与双轨身份制。全 Inline Keyboard 原位重绘交互,支持在 TG 面板一键下发毫秒级模块热启停 (Toggle)、丝滑改名与日志抓取,底层数据库实时刷新,彻底告别刷屏烦恼
- 🔄 **SSOT 溯源与热更新装甲 (Smooth Upgrade Engine)**:全系脚本彻底消灭硬编码,部署时动态抓取云端版本信标。搭载状态机嗅探逻辑与 OTA 预警探针,一键回车瞬间完成配置继承、数据同步与无损换代
- 🗺️ **全球拓扑矩阵与活体词库 (Global Nexus)**:守护版图横跨亚欧美三大洲。接入 GitHub Actions 云端流水线,每日静默同步全球各大区当日 Google 真实热搜榜单与高权重本土站点,让伪装行为永远贴合当地网络脉搏
- 👻 **资产持久化与错峰调度 (Hash-Seeded Persona)**:摒弃随机抽取指纹,基于节点物理 IP 哈希永久锁定 3 个绝对专属设备,完美构建高权重真实家庭内网画像。叠加按需智能分频与随机防并发休眠,化解“惊群效应”
- 🖧 **底层路由死锁与高精度探针 (Hard-Bind Routing)**:底层探测引擎强力接管 curl 核心参数 (`--interface`),将发出的每一滴伪装流量死死绑定在物理网卡或隧道 IP 上。配合多级 ISP 容灾链路,彻底杜绝双栈环境下的流量溢出与 API 误判
**—— 💎 骨干基建特征 ——**
- 🏭 **自动化指纹兵工厂**:依托 GitHub Actions CI/CD 流水线,每月 1 日无人值守锻造 4000+ 带绝对物理分区的真实终端设备数据。
- 🔒 **叹息之墙 (Zero-Trust HMAC)**:底层通讯引入 时间戳 + HMAC-SHA256 军用级动态签名。指令有效期仅 60 秒(阅后即焚),彻底免疫中间人抓包与重放攻击。
- 🔒 **叹息之墙 (Zero-Trust HMAC)**:底层通讯引入 时间戳 + HMAC-SHA256 军用级动态签名。指令有效期仅 60 秒(阅后即焚),未授权请求直接触发系统级 403 物理熔断,彻底免疫中间人抓包与重放攻击。
- ☁️ **云端中枢 (Public Master)**:官方公共机器人 @OmniBeacon_bot,新手免自建,一键接入极速入伍!同时支持硬核极客私有化 SQLite 分布式部署。
- 🎮 **TG 战术面板 (Command Center)**:全 Inline Keyboard 交互,一键下发伪装指令、索要战报、毫秒级抓取边缘节点实时运行日志。
- 👁️‍🗨️ **玻璃房透明遥测 (Glasshouse)**:基于 Cloudflare Workers 的全透明计数中枢,绝对零隐私收集,仅作原子累加,底层网关源码全开源。
## 📂 项目架构 (Monorepo)
@@ -46,7 +39,7 @@
┃ ┣ 📂 regions/ # 🧊 冷数据:按 [国家/省州/城市] 深度细分的 LBS 锚点
┃ ┣ 📂 keywords/ # 🔥 热数据:按国家归类的动态搜索词库 (OTA 自动更新)
┃ ┗ 📜 user_agents.txt # 🔥 热数据:由兵工厂每月锻造的绝对坐标专属设备库
┣ 📜 version.txt # 🚩 全球版本信标:SSOT 单一事实来源锚点 (v3.5.0)
┣ 📜 version.txt # 🚩 双端版本信标:Agent/Master 独立解耦的 KV 环境配置 (v3.5.1)
┗ 📂 telemetry/ # 👁️‍🗨️ 玻璃房计划Cloudflare Workers 透明计数器网关源码
```

View File

@@ -17,9 +17,12 @@ source "$CONFIG_FILE"
# 默认 Webhook 监听端口
AGENT_PORT=${AGENT_PORT:-9527}
# [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}"
# [v3.5.2 核心] 载入不可变主键与可变展示名 (双轨身份)
if [ -z "$NODE_NAME" ]; then
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}"
fi
NODE_ALIAS="${NODE_ALIAS:-$NODE_NAME}"
# --- [重点升级 1: 守护进程防冲突自检] ---
if pgrep -f "webhook.py $AGENT_PORT" > /dev/null; then
@@ -47,8 +50,8 @@ if [ -n "$AGENT_IP" ]; then
# 只有当这是第一次运行,或者公网 IP 发生变动时,才发送 Telegram 申请
if [ "$AGENT_IP" != "$LAST_IP" ]; then
# 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}\`"
# [v3.5.2 核心] 携带 6 字段双轨身份发起注册申请 (展示别名,暗号尾部追加 NODE_ALIAS)
REG_MSG="👋 **[边缘节点接入申请]**%0A大区: \`${REGION_CODE}\`%0A节点: \`${NODE_ALIAS}\`%0A地址: \`${AGENT_IP}:${AGENT_PORT}\`%0A%0A⚠ **安全验证**: 为防止非法节点接入,请长按复制下方代码,并**发送给我**以完成最终授权录入:%0A%0A\`#REGISTER#|${REGION_CODE}|${NODE_NAME}|${AGENT_IP}|${AGENT_PORT}|${NODE_ALIAS}\`"
curl -s -m 5 -X POST "${TG_API_URL}" \
-d "chat_id=${CHAT_ID}" \
@@ -206,13 +209,11 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
if lines:
log_data = html.escape("".join(lines[-15:]))
# [v3.4.0 核心] 获取版本与主机
# [v3.5.2 核心] 获取版本与节点展示别
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}"
node_alias = config.get('NODE_ALIAS', config.get('NODE_NAME', 'Unknown-Node'))
text_msg = f"📄 <b>[{full_node_name}] 实时日志 (v{local_ver}):</b>\n<pre><code>{log_data}</code></pre>"
text_msg = f"📄 <b>[{node_alias}] 实时日志 (v{local_ver}):</b>\n<pre><code>{log_data}</code></pre>"
data = urllib.parse.urlencode({
'chat_id': config.get('CHAT_ID', ''),
@@ -230,7 +231,109 @@ class AgentHandler(http.server.BaseHTTPRequestHandler):
except Exception as e:
print(f"Log transmission failed: {e}")
# 路由 5: 节点重命名展示别名同步接口 (Base64 终极防御版)
elif req_path == '/trigger_rename':
b64_alias = query.get('b64', [''])[0]
if not b64_alias:
self.send_response(400)
self.end_headers()
self.wfile.write(b"400 Bad Request: Alias is empty\n")
return
import re
import base64
try:
# 1. 还原 URL 安全的 Base64 字符并解码 (杜绝乱码与 WAF 拦截)
pad = len(b64_alias) % 4
if pad > 0:
b64_alias += '=' * (4 - pad)
b64_alias = b64_alias.replace('-', '+').replace('_', '/')
raw_alias = base64.b64decode(b64_alias).decode('utf-8', errors='ignore')
# 2. 强清洗:杜绝 TG Markdown 崩溃严格限制中英数最大20字符
decoded_alias = raw_alias.replace('_', '-')
safe_alias = re.sub(r'[^a-zA-Z0-9\-\u4e00-\u9fa5]', '', decoded_alias)[:20]
if safe_alias:
# 3. 强容错读写 config.conf
config_path = '/opt/ip_sentinel/config.conf'
with open(config_path, 'r', encoding='utf-8', errors='ignore') as f:
lines = f.readlines()
alias_found = False
config_dict = {}
for i, line in enumerate(lines):
if line.startswith('NODE_ALIAS='):
lines[i] = f'NODE_ALIAS="{safe_alias}"\n'
alias_found = True
elif '=' in line and not line.startswith('#'):
key, val = line.strip().split('=', 1)
config_dict[key] = val.strip('"\'')
if not alias_found:
lines.append(f'NODE_ALIAS="{safe_alias}"\n')
with open(config_path, 'w', encoding='utf-8') as f:
f.writelines(lines)
# [v3.5.2 极致丝滑] 移除向 TG 推送冗余报文的逻辑,直接向 Master 回执成功状态即可
self.send_response(200)
self.send_header("Content-type", "text/plain")
self.end_headers()
self.wfile.write(b"Action Accepted: trigger_rename\n")
return
except Exception as e:
self.send_response(500)
self.end_headers()
self.wfile.write(f"500 Internal Error: {str(e)}\n".encode('utf-8'))
return
self.send_response(400)
self.end_headers()
self.wfile.write(b"400 Bad Request: Invalid Characters\n")
# ================== [v3.5.3 新增: 模块动态启停接口] ==================
elif req_path == '/trigger_toggle':
mod_name = query.get('mod', [''])[0]
target_state = query.get('state', [''])[0].lower()
if mod_name not in ['google', 'trust'] or target_state not in ['true', 'false']:
self.send_response(400)
self.end_headers()
self.wfile.write(b"400 Bad Request: Invalid parameters\n")
return
config_key = f"ENABLE_{mod_name.upper()}="
try:
config_path = '/opt/ip_sentinel/config.conf'
with open(config_path, 'r', encoding='utf-8', errors='ignore') as f:
lines = f.readlines()
found = False
for i, line in enumerate(lines):
if line.startswith(config_key):
lines[i] = f'{config_key}"{target_state}"\n'
found = True
break
if not found:
lines.append(f'{config_key}"{target_state}"\n')
with open(config_path, 'w', encoding='utf-8') as f:
f.writelines(lines)
self.send_response(200)
self.send_header("Content-type", "text/plain")
self.end_headers()
self.wfile.write(b"Action Accepted: trigger_toggle\n")
except Exception as e:
self.send_response(500)
self.end_headers()
self.wfile.write(f"500 Internal Error: {str(e)}\n".encode('utf-8'))
else:
self.send_response(404)
self.end_headers()

View File

@@ -53,6 +53,9 @@ echo " 1) 🚀 部署边缘节点 (进入全球节点配置)"
echo " 2) 🗑️ 一键卸载 IP-Sentinel"
read -p "请输入选择 [1-2] (默认1): " ACTION_CHOICE
# [v3.5.2 修复] 防止用户直接回车导致变量为空,从而漏过下方的平滑升级判定
ACTION_CHOICE=${ACTION_CHOICE:-1}
if [ "$ACTION_CHOICE" == "2" ]; then
echo -e "\n⏳ 正在拉取卸载程序..."
curl -sL "${REPO_RAW_URL}/core/uninstall.sh" -o "/tmp/ip_uninstall.sh"
@@ -203,20 +206,10 @@ if [ "$UPGRADE_MODE" == "false" ]; then
mkdir -p "${INSTALL_DIR}/data/regions/${COUNTRY_ID}/${STATE_ID}"
mkdir -p "${INSTALL_DIR}/logs"
# 3. 功能模块前置开关 (按需加载)
echo -e "\n[3/7] 请选择需要开启的养护模块 (按需开启,节省资源):"
echo " 1) 📍 仅开启 [Google 区域纠偏] (默认,适合流媒体解锁机位漂移)"
echo " 2) 🛡️ 仅开启 [IP 信用净化] (适合高风险机房 IP 降低 Scamalytics 分数)"
echo " 3) 🔥 双管齐下 (同时开启以上两项)"
read -p "请输入选择 [1-3] (默认1): " MODULE_CHOICE
# 3. 功能模块前置开关 (v3.5.3 默认全量加载,后续经由 TG 动态启停)
echo -e "\n[3/7] 正在初始化养护模块 (默认全量部署,支持 TG 远程动态启停)..."
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
ENABLE_TRUST="true"
# 4. 接入 Master 中枢配置
echo -e "\n[4/7] 是否接入 Master 司令部?(需要配置与主控相同的 TG 机器人) (y/n)"
@@ -362,6 +355,25 @@ if [ "$UPGRADE_MODE" == "false" ]; then
echo -e "\033[32m✅ 哨兵对外联络点已永久锁定至: $SAFE_PUBLIC_IP\033[0m"
# ========================================================================
# ================== [v3.5.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}"
NODE_ALIAS="$NODE_NAME"
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
echo -e "\n\033[36m[4.8/7] 节点展示别名设定 (用于面板友好显示)...\033[0m"
echo -e "💡 系统底层的不可变主键为: \033[33m${NODE_NAME}\033[0m"
read -p "请输入节点展示别名 (如'纽约机房', 回车使用默认): " CUSTOM_ALIAS
if [ -n "$CUSTOM_ALIAS" ]; then
# 🛡️ 强制字符清洗:防御 Shell 注入,并限制长度防刷屏
NODE_ALIAS=$(echo "$CUSTOM_ALIAS" | tr -d '"'\''\`\$\|&;<>\n\r' | cut -c 1-20)
[ -z "$NODE_ALIAS" ] && NODE_ALIAS="$NODE_NAME"
fi
echo -e "✅ 已锁定节点展示别名: \033[32m$NODE_ALIAS\033[0m"
fi
# ========================================================================
# 5. 远程拉取冷数据并解析固化
echo -e "\n[5/7] 正在从云端数据仓库拉取 [${CITY_NAME}] 节点的底层规则..."
REGION_JSON_FILE="${INSTALL_DIR}/data/regions/${COUNTRY_ID}/${STATE_ID}/${CITY_ID}.json"
@@ -405,6 +417,10 @@ LOG_FILE="${INSTALL_DIR}/logs/sentinel.log"
IP_PREF="$IP_PREF"
PUBLIC_IP="$SAFE_PUBLIC_IP"
BIND_IP="$BIND_IP"
# [v3.5.2新增: 双轨身份系统]
NODE_NAME="$NODE_NAME"
NODE_ALIAS="$NODE_ALIAS"
EOF
# ================== [v3.0.3 变更: 敏感配置文件权限收敛] ==================
@@ -450,6 +466,22 @@ if [ "$UPGRADE_MODE" == "true" ]; then
# 如果是未来再升级,配置文件已是最新,直接提取变量供安装脚本尾部使用
SAFE_PUBLIC_IP=$(grep "^PUBLIC_IP=" "$CONFIG_FILE" | cut -d'"' -f2)
fi
# [v3.5.2 热修复] 兼容老版本没有 NODE_NAME 和 NODE_ALIAS 的情况,无损补齐
if ! grep -q "^NODE_NAME=" "$CONFIG_FILE"; then
TMP_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)-${TMP_HASH}"
NODE_ALIAS="$NODE_NAME"
echo "NODE_NAME=\"$NODE_NAME\"" >> "$CONFIG_FILE"
echo "NODE_ALIAS=\"$NODE_ALIAS\"" >> "$CONFIG_FILE"
else
NODE_NAME=$(grep "^NODE_NAME=" "$CONFIG_FILE" | cut -d'"' -f2)
NODE_ALIAS=$(grep "^NODE_ALIAS=" "$CONFIG_FILE" | cut -d'"' -f2)
if [ -z "$NODE_ALIAS" ]; then
NODE_ALIAS="$NODE_NAME"
echo "NODE_ALIAS=\"$NODE_ALIAS\"" >> "$CONFIG_FILE"
fi
fi
fi
# ========================================================================
@@ -520,11 +552,9 @@ rm -f /tmp/cron_backup
# ================== [v3.4.0 核心: 状态机驱动的热更新路由] ==================
if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
# 构造当前节点的唯一代号 (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}"
REG_MSG="#REGISTER#|${REGION_CODE}|${NODE_NAME}|${SAFE_PUBLIC_IP}|${AGENT_PORT}"
# [v3.5.2 核心] 发送携带双轨身份的注册指令 (追加 NODE_ALIAS 作为第 6 个字段)
REG_MSG="#REGISTER#|${REGION_CODE}|${NODE_NAME}|${SAFE_PUBLIC_IP}|${AGENT_PORT}|${NODE_ALIAS}"
if [ "$UPGRADE_MODE" == "true" ]; then
# 读取本地老版本号,如果没有则视为远古版本 v3.3.1
@@ -539,7 +569,7 @@ if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
-d "chat_id=${CHAT_ID}" \
-d "parse_mode=Markdown" \
-d "text=✨ *IP-Sentinel 引擎热更新完成!*
📍 节点:\`${NODE_NAME}\`
📍 节点:\`${NODE_ALIAS}\`
🌐 IP\`${SAFE_PUBLIC_IP}\`
🚀 状态v${TARGET_VERSION} OTA 动态活体引擎已部署
@@ -554,7 +584,7 @@ if [[ -n "$TG_TOKEN" ]] && [[ -n "$CHAT_ID" ]]; then
-d "chat_id=${CHAT_ID}" \
-d "parse_mode=Markdown" \
-d "text=✨ *IP-Sentinel 引擎热更新完成!*
📍 节点:\`${NODE_NAME}\`
📍 节点:\`${NODE_ALIAS}\`
🌐 IP\`${SAFE_PUBLIC_IP}\`
🚀 状态v${TARGET_VERSION} OTA 动态活体引擎已部署" >/dev/null 2>&1
echo -e "\033[32m✅ 升级成功通知已推送到您的 Telegram\033[0m"

View File

@@ -19,9 +19,12 @@ if [ -z "$TG_TOKEN" ] || [ -z "$CHAT_ID" ]; then
fi
# 2. 节点元数据抓取 (v3.2.2 协议自适应与多级容灾版)
# [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}"
# [v3.5.2 核心: 引入双轨身份架构]
if [ -z "$NODE_NAME" ]; then
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}"
fi
NODE_ALIAS="${NODE_ALIAS:-$NODE_NAME}"
# --- [防线 1: 底层路由锁定与协议自适应] ---
CURL_BIND_OPT=""
@@ -93,7 +96,7 @@ if [ -z "$LOG_CONTENT" ]; then
read -r -d '' MSG <<EOT
🛑 **[IP-Sentinel] 告警:节点异常**
----------------------------
📍 **节点名称**: \`${NODE_NAME}\`
📍 **节点名称**: \`${NODE_ALIAS}\`
⚠️ **警告**: 过去 24 小时无运行日志!
🛠️ **建议**: 节点可能刚部署完毕,请在面板手动执行一次养护动作。
EOT
@@ -111,7 +114,7 @@ else
# 开始组装战报头部
MSG="📊 **IP-Sentinel 每日简报 (${FLAG} ${REGION_NAME})**
----------------------------
📍 **节点名称**: \`${NODE_NAME}\`
📍 **节点名称**: \`${NODE_ALIAS}\`
📡 **出口 IP**: \`${CURRENT_IP}\`
🛡️ **IP 属性**: ${IP_TYPE}"

View File

@@ -1,3 +1,23 @@
syria
leylah fernandez
highway hotline sk
real betis vs braga
vincent bolloré
europa conference league
resident alien
battlefield 6
aston villa vs bologna
nottm forest vs porto
soccer
lamour est dans le pré
luis suárez
listeria
strc
bayern
arda güler
aleksandar pavlović
kooora
yalla kora
ina garten
jordan goodwin
jerami grant

View File

@@ -1,3 +1,23 @@
rayo vallecano
harry meghan
irland
betis sevilla
vermisste person
konferenz league
garda
judith hoersch
jörg pilawa
strasbourg
real madrid vs bayern
kicker
mbappe
flashscore
sport1
vini jr
bet365
kompany
jude bellingham
upamecano
gute zeiten, schlechte zeiten
inflation
fog warning

View File

@@ -1,3 +1,23 @@
armengol
pau víctor
braga fc
fiorentina vs crystal palace
morante hoy
iago aspas
aston villa
real betis vs braga
ministerio de sanidad
az - shakhtar
arsenal
tiktok
harry kane
sudan
lunin
airef
tiempo de juego
fc bayern
militao
bellingham
supervivientes
jalen green
rockstar games

View File

@@ -1,3 +1,23 @@
programme télé
tchernobyl
géraldine maillet
biot
racing
liga europa
tv ce soir
programme tv de ce soir
brad pitt
aston villa
michael olise
robert ménard
match ce soir
sporting
ester exposito
bellingham
iptv
militao
jeff goldblum
lunin
kiev
julien royal
viktor orbán

View File

@@ -1,3 +1,23 @@
歐聯
神戶勝利船
潘宏彬
姚正菁
木乃伊
ios 26
李克寧木乃伊
江美儀
田啟文
曼寧加
arsenal
ucl
歐洲聯賽冠軍盃
arsenal vs sporting
bayern vs real madrid
real madrid
皇馬
拜仁慕尼黑 對 皇家馬德里
claude
補貼
nba 直播
航空公司
向華強
@@ -25,7 +45,6 @@ prediction market
polymarket
巴基斯坦
sndk
江美儀
楊何蓓茵
樂珈嘉
姜濤

View File

@@ -1,3 +1,23 @@
小芝風花
中井亜美
afc u20女子アジアカップ
ネオジオ
uefaヨーロッパリーグ
加藤史帆
町田ゼルビア
志田未来
伊藤英明
島田麻央
al-nassr vs al-ettifaq
arsenal
レアル・マドリード
バイエルン
abema
real madrid
lucknow super giants vs royal challengers bengaluru standings
bayern vs real madrid
給付
wowow
小泉進次郎
政権
ミキティ
@@ -9,9 +29,7 @@
山本由伸
アレックス・ベシア
リバプール
abema
champions league
wowow
エルニーニョ
アトレティコ 対 バルセロナ
オープンワールド

View File

@@ -1,3 +1,23 @@
real betis
sean connery
l1 nieuws
ronaldinho
demi de boer
bondgenoten
frank masmeijer
real betis - braga
ethereum
aston villa - bologna
manuel neuer
neuer
olise
mbappe
sporting
live tv
bayern munchen
arda güler
ziggo
arda guler
netflix
frenkie de jong
kanye west

View File

@@ -1,3 +1,23 @@
aston villa vs bologna
mumbai indians vs punjab kings standings
al sadd vs vissel kobe
amd share price
opus 4.7
pete hegseth
naman dhir
yen singapore dollar
mayank rawat
dji pocket 4
real madrid
al-nassr vs al-ettifaq
bayern vs real madrid
arsenal vs sporting
lucknow super giants vs royal challengers bengaluru standings
is claude down
claude
allbirds
red sea
rcb vs lsg
retirement
asia flights delays cancellations
suns vs trail blazers

25
data/keywords/kw_TW.txt Normal file
View File

@@ -0,0 +1,25 @@
歐聯
菡生婦幼診所
台鐵訂票
飛機
東光路
货币
amd
rklb
航空母艦
axti
Yahoo奇摩
天氣
蝦皮購物
PChome
Momo購物網
Mobile01
Dcard
巴哈姆特
中時電子報
聯合新聞網
台灣高鐵
台鐵時刻表
中華電信
統一發票
勞動部

View File

@@ -1,3 +1,23 @@
europa conference league
bromley fc
paul merton
chris wood
istanbul
turkey
lucy watson
thiago silva
bednarek
jan bednarek
vincent kompany
mbappe
luis suarez sporting
madrid fc
andriy lunin
what did bec say to rachel mafs
yalla kora
geovany quenda
sporting cp
pavlovic
talktalk
arne slot drops mohamed salah
suns vs trail blazers

View File

@@ -1,3 +1,23 @@
giancarlo stanton
real betis
prosecution of daniel duggan
liv morgan
mikey williams
indiana fever sophie cunningham baptism
gregory donnell morgan jr
why are the sirens going off
leylah fernandez
strasbourg vs mainz
michael olise
ريال مدريد
dazn
paramount
univision
jude bellingham
sam antonacci
real madrid
bayern
arda güler
los angeles dodgers
vandenberg launch schedule
ryan dunn
@@ -13,8 +33,6 @@ barca
vix
fcb
barcelona schedule
univision
paramount
tarjeta roja
a knight of the seven kingdoms season 2
charlotte flair
@@ -47,3 +65,6 @@ AI startups in Silicon ValleySan Jose weather this weekend
Silicon Valley tech news
best tacos in San Jose
Apple park visitor center hours
Seattle Weather
Las Vegas strip
Charlotte Hornets

View File

@@ -1,3 +1,23 @@
porto vs
crystal palace
porto
uefa europa conference
betis đấu với braga
real betis vs braga
aston villa đấu với bologna
fiorentina đấu với crystal palace
c2
cup c2
thể thao
arda güler
aleksandar pavlović
ars
90
real
real madrid
xoi
luong sơn
fpt
phan văn giang
nhà ở xã hội
club america

View File

@@ -1,6 +1,6 @@
{
"version": "3.5.0",
"updated_at": "2026-04-15",
"version": "3.5.2",
"updated_at": "2026-04-16",
"continents": [
{
"id": "ASIA",
@@ -11,13 +11,7 @@
"name": "Japan (日本)",
"keyword_file": "kw_JP.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "Tokyo", "name": "Tokyo (东京)" }
]
}
{ "id": "Default", "name": "Default State", "cities": [ { "id": "Tokyo", "name": "Tokyo (东京)" } ] }
]
},
{
@@ -25,13 +19,7 @@
"name": "Singapore (新加坡)",
"keyword_file": "kw_SG.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "Singapore", "name": "Singapore (新加坡)" }
]
}
{ "id": "Default", "name": "Default State", "cities": [ { "id": "Singapore", "name": "Singapore (新加坡)" } ] }
]
},
{
@@ -39,13 +27,7 @@
"name": "Hong Kong (香港)",
"keyword_file": "kw_HK.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "HongKong", "name": "Hong Kong (香港)" }
]
}
{ "id": "Default", "name": "Default State", "cities": [ { "id": "HongKong", "name": "Hong Kong (香港)" } ] }
]
},
{
@@ -53,13 +35,15 @@
"name": "Vietnam (越南)",
"keyword_file": "kw_VN.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "Hanoi", "name": "Hanoi (河内)" }
]
}
{ "id": "Default", "name": "Default State", "cities": [ { "id": "Hanoi", "name": "Hanoi (河内)" } ] }
]
},
{
"id": "TW",
"name": "Taiwan (台湾)",
"keyword_file": "kw_TW.txt",
"states": [
{ "id": "Default", "name": "Default State", "cities": [ { "id": "Taipei", "name": "Taipei (台北)" } ] }
]
}
]
@@ -77,7 +61,8 @@
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "London", "name": "London (伦敦)" }
{ "id": "London", "name": "London (伦敦)" },
{ "id": "Coventry", "name": "Coventry (考文垂)" }
]
}
]
@@ -87,13 +72,7 @@
"name": "Germany (德国)",
"keyword_file": "kw_DE.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "Frankfurt", "name": "Frankfurt (法兰克福)" }
]
}
{ "id": "Default", "name": "Default State", "cities": [ { "id": "Frankfurt", "name": "Frankfurt (法兰克福)" } ] }
]
},
{
@@ -101,13 +80,7 @@
"name": "France (法国)",
"keyword_file": "kw_FR.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "Paris", "name": "Paris (巴黎)" }
]
}
{ "id": "Default", "name": "Default State", "cities": [ { "id": "Paris", "name": "Paris (巴黎)" } ] }
]
},
{
@@ -115,13 +88,7 @@
"name": "Netherlands (荷兰)",
"keyword_file": "kw_NL.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "Amsterdam", "name": "Amsterdam (阿姆斯特丹)" }
]
}
{ "id": "Default", "name": "Default State", "cities": [ { "id": "Amsterdam", "name": "Amsterdam (阿姆斯特丹)" } ] }
]
},
{
@@ -129,13 +96,7 @@
"name": "Spain (西班牙)",
"keyword_file": "kw_ES.txt",
"states": [
{
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "Madrid", "name": "Madrid (马德里)" }
]
}
{ "id": "Default", "name": "Default State", "cities": [ { "id": "Madrid", "name": "Madrid (马德里)" } ] }
]
}
]
@@ -156,6 +117,48 @@
{ "id": "Los_Angeles", "name": "Los Angeles (洛杉矶)" },
{ "id": "San_Jose", "name": "San Jose (圣何塞)" }
]
},
{
"id": "IL",
"name": "Illinois (伊利诺伊州)",
"cities": [
{ "id": "Warrenville", "name": "Warrenville (沃伦维尔)" }
]
},
{
"id": "NC",
"name": "North Carolina (北卡罗来纳州)",
"cities": [
{ "id": "Charlotte", "name": "Charlotte (夏洛特)" }
]
},
{
"id": "NV",
"name": "Nevada (内华达州)",
"cities": [
{ "id": "Las_Vegas", "name": "Las Vegas (拉斯维加斯)" }
]
},
{
"id": "OR",
"name": "Oregon (俄勒冈州)",
"cities": [
{ "id": "Bend", "name": "Bend (本德)" }
]
},
{
"id": "UT",
"name": "Utah (犹他州)",
"cities": [
{ "id": "Salt_Lake_City", "name": "Salt Lake City (盐湖城)" }
]
},
{
"id": "WA",
"name": "Washington (华盛顿州)",
"cities": [
{ "id": "Seattle", "name": "Seattle (西雅图)" }
]
}
]
},
@@ -168,7 +171,8 @@
"id": "Default",
"name": "Default State",
"cities": [
{ "id": "Toronto", "name": "Toronto (多伦多)" }
{ "id": "Toronto", "name": "Toronto (多伦多)" },
{ "id": "Montreal", "name": "Montreal (蒙特利尔)" }
]
}
]

View File

@@ -0,0 +1,17 @@
{
"region_name": "Canada - Montreal",
"google_module": {
"base_lat": 45.5017,
"base_lon": -73.5673,
"lang_params": "hl=en&gl=CA",
"valid_url_suffix": "ca"
},
"trust_module": {
"white_urls": [
"https://en.wikipedia.org/wiki/Special:Random",
"https://www.cbc.ca/",
"https://www.amazon.ca/",
"https://www.theweathernetwork.com/ca"
]
}
}

View File

@@ -0,0 +1,20 @@
{
"region_name": "Taiwan - Taipei",
"google_module": {
"base_lat": 25.0330,
"base_lon": 121.5654,
"lang_params": "hl=zh-TW&gl=TW",
"valid_url_suffix": "com.tw"
},
"trust_module": {
"white_urls": [
"https://zh.wikipedia.org/wiki/Special:Random",
"https://tw.yahoo.com/",
"https://www.pchome.com.tw/",
"https://www.momoshop.com.tw/",
"https://www.ruten.com.tw/",
"https://www.mobile01.com/",
"https://www.dcard.tw/"
]
}
}

View File

@@ -0,0 +1,17 @@
{
"region_name": "United Kingdom - Coventry",
"google_module": {
"base_lat": 52.4068,
"base_lon": -1.5197,
"lang_params": "hl=en&gl=GB",
"valid_url_suffix": "co.uk"
},
"trust_module": {
"white_urls": [
"https://en.wikipedia.org/wiki/Special:Random",
"https://www.bbc.co.uk/",
"https://www.amazon.co.uk/",
"https://www.theguardian.com/uk"
]
}
}

View File

@@ -0,0 +1,5 @@
{
"region_name": "United States - Warrenville",
"google_module": { "base_lat": 41.8164, "base_lon": -88.1748, "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/" ] }
}

View File

@@ -0,0 +1,5 @@
{
"region_name": "United States - Charlotte",
"google_module": { "base_lat": 35.2271, "base_lon": -80.8431, "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/" ] }
}

View File

@@ -0,0 +1,5 @@
{
"region_name": "United States - Las Vegas",
"google_module": { "base_lat": 36.1699, "base_lon": -115.1398, "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/" ] }
}

View File

@@ -0,0 +1,5 @@
{
"region_name": "United States - Bend",
"google_module": { "base_lat": 44.0582, "base_lon": -121.3153, "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/" ] }
}

View File

@@ -0,0 +1,5 @@
{
"region_name": "United States - Salt Lake City",
"google_module": { "base_lat": 40.7608, "base_lon": -111.8910, "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/" ] }
}

View File

@@ -0,0 +1,5 @@
{
"region_name": "United States - Seattle",
"google_module": { "base_lat": 47.6062, "base_lon": -122.3321, "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/" ] }
}

View File

@@ -31,6 +31,9 @@ echo " 1) 🚀 部署 Master 控制中枢"
echo " 2) 🗑️ 一键卸载 Master 中枢"
read -p "请输入选择 [1-2] (默认1): " ACTION_CHOICE
# [v3.5.2 修复] 防止用户直接回车导致变量为空,从而漏过下方的平滑升级判定被误删档
ACTION_CHOICE=${ACTION_CHOICE:-1}
if [ "$ACTION_CHOICE" == "2" ]; then
echo -e "\n⏳ 正在拉取卸载程序..."
# [新增逻辑] 使用上面定义的 REPO_RAW_URL 动态拉取卸载脚本,执行后自动销毁临时文件
@@ -121,7 +124,7 @@ EOF
fi
# 🛑 拦截块结束
# 3. 初始化 SQLite 数据库 (幂等操作,升级模式下可安全修补表结构)
# 3. 初始化 SQLite 数据库 (幂等操作,升级模式下由 tg_master.sh 负责热修补)
echo -e "\n[3/4] 正在初始化 SQLite 数据库表结构..."
sqlite3 "$DB_FILE" <<EOF
CREATE TABLE IF NOT EXISTS nodes (
@@ -130,6 +133,10 @@ CREATE TABLE IF NOT EXISTS nodes (
agent_ip TEXT,
agent_port TEXT,
last_seen DATETIME DEFAULT CURRENT_TIMESTAMP,
region TEXT DEFAULT 'UNKNOWN',
node_alias TEXT,
enable_google TEXT DEFAULT 'true',
enable_trust TEXT DEFAULT 'true',
PRIMARY KEY(chat_id, node_name)
);
EOF

View File

@@ -36,6 +36,13 @@ edit_msg() {
-d "chat_id=$1" -d "message_id=$2" -d "text=$3" -d "parse_mode=Markdown" > /dev/null
}
# [v3.5.3 新增: 支持内联键盘的原位 UI 重绘函数]
edit_ui() {
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/editMessageText" \
-H "Content-Type: application/json" \
-d "{\"chat_id\":\"$1\",\"message_id\":\"$2\",\"text\":\"$3\",\"parse_mode\":\"Markdown\",\"reply_markup\":{\"inline_keyboard\":$4}}" > /dev/null
}
# 数据库执行函数
db_exec() {
sqlite3 "$DB_FILE" "$1"
@@ -60,9 +67,12 @@ generate_signed_url() {
}
# ========================================================================
# ================== [v3.1.3 核心: 数据库结构无损热升级] ==================
# 自动探测并增加 region 字段,屏蔽已存在的报错,保护老节点数据
# ================== [v3.1.3/v3.5.3 核心: 数据库结构无损热升级] ==================
# 自动探测并增加缺失字段,屏蔽已存在的报错,保护老节点数据
db_exec "ALTER TABLE nodes ADD COLUMN region TEXT DEFAULT 'UNKNOWN';" 2>/dev/null
db_exec "ALTER TABLE nodes ADD COLUMN node_alias TEXT;" 2>/dev/null
db_exec "ALTER TABLE nodes ADD COLUMN enable_google TEXT DEFAULT 'true';" 2>/dev/null
db_exec "ALTER TABLE nodes ADD COLUMN enable_trust TEXT DEFAULT 'true';" 2>/dev/null
# ========================================================================
# --- 核心轮询循环 ---
@@ -79,6 +89,23 @@ while true; do
CHAT_ID=$(echo "$UPDATE" | jq -r '.message.chat.id // .callback_query.message.chat.id')
TEXT=$(echo "$UPDATE" | jq -r '.message.text // .callback_query.data')
REPLY_TO_TEXT=$(echo "$UPDATE" | jq -r '.message.reply_to_message.text // empty')
# ================== [v3.5.2 新增: 拦截别名修改的对话回复] ==================
if [[ "$REPLY_TO_TEXT" == *"✏️ 请回复本消息以重命名节点:"* ]]; then
# 精准提取被回复消息中的节点主键名
TARGET_NODE=$(echo "$REPLY_TO_TEXT" | grep -v "✏️" | grep -v "仅限" | tr -d '\` ' | tr -cd 'a-zA-Z0-9_.-' | head -n 1)
# [v3.5.2 热修复] 废除 Bash 原生 tr 命令的中文白名单 (不支持 Unicode 会误删中文)。
# 改用黑名单策略:仅自动转化下划线,剔除引号、特殊符号和冒号(防止破坏内部路由)
# 将完整的中文原样送入 Base64 编码,最终严格正则清洗交由 Agent 的 Python 引擎处理!
NEW_ALIAS=$(echo "$TEXT" | sed 's/_/-/g' | tr -d '"'\''\`\$\|&;<>\n\r:' | cut -c 1-30)
if [ -n "$TARGET_NODE" ] && [ -n "$NEW_ALIAS" ]; then
# 强行重写内部路由
TEXT="do_rename:${TARGET_NODE}:${NEW_ALIAS}"
fi
fi
# ================== [v3.0.1 新增: 消除转圈圈与获取消息ID] ==================
CB_ID=$(echo "$UPDATE" | jq -r '.callback_query.id // empty')
@@ -95,21 +122,27 @@ while true; do
if [[ "$TEXT" == *"#REGISTER#"* ]]; then
REG_LINE=$(echo "$TEXT" | grep "#REGISTER#" | head -n 1 | tr -d '\` ')
# V3.1.3 兼容性拆包: 判断是新版协议 (5个字段) 还是老版协议 (4个字段)
# V3.5.2 兼容性拆包: 支持 6字段(双轨)、5字段(单轨)、4字段(远古)
FIELD_COUNT=$(echo "$REG_LINE" | awk -F'|' '{print NF}')
if [ "$FIELD_COUNT" -ge 5 ]; then
if [ "$FIELD_COUNT" -ge 6 ]; then
IFS='|' read -r MAGIC RAW_REGION RAW_NODE RAW_IP RAW_PORT RAW_ALIAS <<< "$REG_LINE"
elif [ "$FIELD_COUNT" -eq 5 ]; then
IFS='|' read -r MAGIC RAW_REGION RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
RAW_ALIAS="$RAW_NODE"
else
IFS='|' read -r MAGIC RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
RAW_REGION="UNKNOWN"
RAW_ALIAS="$RAW_NODE"
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) # 提取国家大区
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)
NODE_ALIAS=$(echo "$RAW_ALIAS" | tr -d '"'\''\`\$\|&;<>\n\r' | cut -c 1-30)
[ -z "$NODE_ALIAS" ] && NODE_ALIAS="$NODE_NAME"
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 攻击渗透。"
@@ -121,9 +154,9 @@ while true; do
continue
fi
# 入库时追加 region 字段
db_exec "INSERT INTO nodes (chat_id, node_name, agent_ip, agent_port, last_seen, region) VALUES ('$CHAT_ID', '$NODE_NAME', '$AGENT_IP', '$AGENT_PORT', CURRENT_TIMESTAMP, '$AGENT_REGION') ON CONFLICT(chat_id, node_name) DO UPDATE SET agent_ip='$AGENT_IP', agent_port='$AGENT_PORT', last_seen=CURRENT_TIMESTAMP, region='$AGENT_REGION';"
send_msg "$CHAT_ID" "✅ **司令部确认 (v${MASTER_VERSION})**\n节点接入成功: \`$NODE_NAME\`\n地址: \`$AGENT_IP:$AGENT_PORT\`"
# [核心] 入库时追加 node_alias 字段
db_exec "INSERT INTO nodes (chat_id, node_name, agent_ip, agent_port, last_seen, region, node_alias) VALUES ('$CHAT_ID', '$NODE_NAME', '$AGENT_IP', '$AGENT_PORT', CURRENT_TIMESTAMP, '$AGENT_REGION', '$NODE_ALIAS') ON CONFLICT(chat_id, node_name) DO UPDATE SET agent_ip='$AGENT_IP', agent_port='$AGENT_PORT', last_seen=CURRENT_TIMESTAMP, region='$AGENT_REGION', node_alias='$NODE_ALIAS';"
send_msg "$CHAT_ID" "✅ **司令部确认 (v${MASTER_VERSION})**%0A节点 \`${NODE_ALIAS}\` 档案已录入!"
# ================== [v3.1.3 丝滑连招: 直接呼出全球大区雷达] ==================
REGION_DATA=$(db_exec "SELECT region, COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID' GROUP BY region;")
@@ -221,15 +254,17 @@ while true; do
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';")
# [v3.5.2] 提取物理主键和展示别名
NODE_LIST=$(db_exec "SELECT node_name, IFNULL(node_alias, 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\"},"
while IFS='|' read -r N_NAME N_ALIAS; do
[ -z "$N_NAME" ] && continue
ROW_STR="$ROW_STR{\"text\":\"🖥️ $N_ALIAS\",\"callback_data\":\"manage:$N_NAME\"},"
COL=$((COL+1))
if [ $COL -eq 2 ]; then
ROW_STR="${ROW_STR%,}]"
@@ -237,7 +272,7 @@ while true; do
COL=0
ROW_STR="["
fi
done
done <<< "$NODE_LIST"
# 如果是奇数,补齐最后的尾巴
if [ $COL -eq 1 ]; then
ROW_STR="${ROW_STR%,}]"
@@ -250,11 +285,76 @@ while true; do
;;
manage:*)
# 🛡️ 强制过滤节点名,防止面板渲染时发生 XSS 或注入
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
# 【核心升级】拆分下发按钮,精准对应 Google 与 Trust 两个模块,并排版为 3 行 2 列
BTNS="[[{\"text\":\"📍 Google 纠偏\",\"callback_data\":\"google:$TARGET_NODE\"}, {\"text\":\"🛡️ 信用净化\",\"callback_data\":\"trust:$TARGET_NODE\"}], [{\"text\":\"📜 实时日志\",\"callback_data\":\"log:$TARGET_NODE\"}, {\"text\":\"📊 统计战报\",\"callback_data\":\"report:$TARGET_NODE\"}], [{\"text\":\"🗑️ 剔除失联节点\",\"callback_data\":\"del:$TARGET_NODE\"}, {\"text\":\"⬅️ 返回大区目录\",\"callback_data\":\"list_nodes\"}]]"
send_ui "$CHAT_ID" "⚙️ **目标锁定**: \`$TARGET_NODE\`\n请选择战术动作" "$BTNS"
TARGET_ALIAS=$(db_exec "SELECT IFNULL(node_alias, node_name) FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
[ -z "$TARGET_ALIAS" ] && TARGET_ALIAS="$TARGET_NODE"
# [v3.5.3] 将改名收纳至 L5 高级面板,替换为“高级控制功能”
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\":\"adv:$TARGET_NODE\"}, {\"text\":\"🗑️ 剔除失联节点\",\"callback_data\":\"del:$TARGET_NODE\"}], [{\"text\":\"⬅️ 返回大区目录\",\"callback_data\":\"list_nodes\"}]]"
if [ -n "$MSG_ID" ]; then
edit_ui "$CHAT_ID" "$MSG_ID" "⚙️ **目标锁定**: \`$TARGET_ALIAS\`\n*(身份标识: $TARGET_NODE)*\n请选择战术动作" "$BTNS"
else
send_ui "$CHAT_ID" "⚙️ **目标锁定**: \`$TARGET_ALIAS\`\n*(身份标识: $TARGET_NODE)*\n请选择战术动作" "$BTNS"
fi
;;
adv:*)
# [L5 高级控制面板渲染]
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
TARGET_ALIAS=$(db_exec "SELECT IFNULL(node_alias, node_name) FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
[ -z "$TARGET_ALIAS" ] && TARGET_ALIAS="$TARGET_NODE"
# 抓取当前节点的开关状态
TOGGLE_INFO=$(db_exec "SELECT enable_google, enable_trust FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
ST_GOOGLE=$(echo "$TOGGLE_INFO" | cut -d'|' -f1)
ST_TRUST=$(echo "$TOGGLE_INFO" | cut -d'|' -f2)
# 动态渲染状态机红绿灯 UI
[ "$ST_GOOGLE" == "true" ] && BTN_G="🔴 停用 Google 纠偏" && ACT_G="false" || { BTN_G="🟢 启用 Google 纠偏"; ACT_G="true"; }
[ "$ST_TRUST" == "true" ] && BTN_T="🔴 停用信用净化" && ACT_T="false" || { BTN_T="🟢 启用信用净化"; ACT_T="true"; }
BTNS="[[{\"text\":\"$BTN_G\",\"callback_data\":\"toggle:google:$TARGET_NODE:$ACT_G\"}], [{\"text\":\"$BTN_T\",\"callback_data\":\"toggle:trust:$TARGET_NODE:$ACT_T\"}], [{\"text\":\"✏️ 修改节点备注\",\"callback_data\":\"rename:$TARGET_NODE\"}], [{\"text\":\"⬅️ 返回节点面板\",\"callback_data\":\"manage:$TARGET_NODE\"}]]"
if [ -n "$MSG_ID" ]; then
edit_ui "$CHAT_ID" "$MSG_ID" "⚙️ **高级控制** | \`$TARGET_ALIAS\`\n请下达控制指令" "$BTNS"
else
send_ui "$CHAT_ID" "⚙️ **高级控制** | \`$TARGET_ALIAS\`\n请下达控制指令" "$BTNS"
fi
;;
toggle:*)
# [动态启停通信闭环]
IFS=':' read -r CMD MOD_NAME TARGET_NODE TARGET_STATE <<< "$TEXT"
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
AGENT_INFO=$(db_exec "SELECT agent_ip, agent_port FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
AGENT_IP=$(echo "$AGENT_INFO" | cut -d'|' -f1)
AGENT_PORT=$(echo "$AGENT_INFO" | cut -d'|' -f2)
if [ -n "$AGENT_IP" ] && [ -n "$AGENT_PORT" ]; then
TARGET_URL=$(generate_signed_url "$AGENT_IP" "$AGENT_PORT" "/trigger_toggle")
TARGET_URL="${TARGET_URL}&mod=${MOD_NAME}&state=${TARGET_STATE}"
RESPONSE=$(curl -s -m 5 "$TARGET_URL" || echo "FAILED")
if [[ "$RESPONSE" == *"Action Accepted"* ]]; then
# 下发成功,更新 DB原位重绘
db_exec "UPDATE nodes SET enable_${MOD_NAME}='$TARGET_STATE' WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
TOGGLE_INFO=$(db_exec "SELECT enable_google, enable_trust FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
ST_GOOGLE=$(echo "$TOGGLE_INFO" | cut -d'|' -f1)
ST_TRUST=$(echo "$TOGGLE_INFO" | cut -d'|' -f2)
[ "$ST_GOOGLE" == "true" ] && BTN_G="🔴 停用 Google 纠偏" && ACT_G="false" || { BTN_G="🟢 启用 Google 纠偏"; ACT_G="true"; }
[ "$ST_TRUST" == "true" ] && BTN_T="🔴 停用信用净化" && ACT_T="false" || { BTN_T="🟢 启用信用净化"; ACT_T="true"; }
BTNS="[[{\"text\":\"$BTN_G\",\"callback_data\":\"toggle:google:$TARGET_NODE:$ACT_G\"}], [{\"text\":\"$BTN_T\",\"callback_data\":\"toggle:trust:$TARGET_NODE:$ACT_T\"}], [{\"text\":\"✏️ 修改节点备注\",\"callback_data\":\"rename:$TARGET_NODE\"}], [{\"text\":\"⬅️ 返回节点面板\",\"callback_data\":\"manage:$TARGET_NODE\"}]]"
TARGET_ALIAS=$(db_exec "SELECT IFNULL(node_alias, node_name) FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
edit_ui "$CHAT_ID" "$MSG_ID" "⚙️ **高级控制** | \`$TARGET_ALIAS\`\n✅ 成功:模块 [$MOD_NAME] 状态已切换为 $TARGET_STATE" "$BTNS"
else
send_msg "$CHAT_ID" "❌ 指令下发失败,节点可能离线或未更新至 v3.5.3。"
fi
fi
;;
del:*)
@@ -285,6 +385,50 @@ while true; do
fi
;;
rename:*)
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
# [v3.5.2] 发送 ForceReply 引导用户回复
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
-H "Content-Type: application/json" \
-d "{\"chat_id\":\"$CHAT_ID\",\"text\":\"✏️ 请回复本消息以重命名节点:\n\`$TARGET_NODE\`\n(仅限中英文、数字最长20字符)\",\"parse_mode\":\"Markdown\",\"reply_markup\":{\"force_reply\":true}}" > /dev/null
;;
do_rename:*)
# [v3.5.2] 内部重命名路由 (已被第2处的代码拦截并格式化)
IFS=':' read -r CMD TARGET_NODE NEW_ALIAS <<< "$TEXT"
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
AGENT_INFO=$(db_exec "SELECT agent_ip, agent_port FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE' LIMIT 1;")
AGENT_IP=$(echo "$AGENT_INFO" | cut -d'|' -f1)
AGENT_PORT=$(echo "$AGENT_INFO" | cut -d'|' -f2)
if [ -n "$AGENT_IP" ] && [ -n "$AGENT_PORT" ]; then
send_msg "$CHAT_ID" "⏳ 正在向 \`$TARGET_NODE\` 下发重命名指令,正在建立加密隧道..."
TARGET_URL=$(generate_signed_url "$AGENT_IP" "$AGENT_PORT" "/trigger_rename")
# [绝密防线: Base64 编码绕过一切传输限制与 WAF 拦截]
ALIAS_B64=$(echo -n "$NEW_ALIAS" | base64 | tr -d '\n' | tr '+/' '-_')
TARGET_URL="${TARGET_URL}&b64=${ALIAS_B64}"
RESPONSE=$(curl -s -m 5 "$TARGET_URL" || echo "FAILED")
if [ "$RESPONSE" == "FAILED" ]; then
send_msg "$CHAT_ID" "❌ 指令下发超时!请检查节点连通性。"
elif [[ "$RESPONSE" == *"Action Accepted"* ]]; then
# [v3.5.2 极致丝滑] 确认 Agent 修改成功后Master 立即自动同步本地 SQLite 数据库!
db_exec "UPDATE nodes SET node_alias='$NEW_ALIAS' WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
send_msg "$CHAT_ID" "✅ 通讯成功!节点别名已下发: \`$NEW_ALIAS\`%0A*(司令部档案已自动刷新,雷达面板已同步)*"
else
# 增加输出 RESPONSE 调试信息,排查任何拦截死因
send_msg "$CHAT_ID" "⚠️ 节点拒绝了请求,请确保 Agent 已更新至 v3.5.2%0A(回传信息: \`${RESPONSE}\`)"
fi
else
send_msg "$CHAT_ID" "❌ 数据库中未找到该节点的通讯地址。"
fi
;;
# 【核心升级】增加拦截规则,支持 google 和 trust 前缀
google:*|trust:*|run:*|report:*|log:*)
# 🛡️ 提取并强制过滤动作参数、节点名与 CHAT_ID

View File

@@ -1,2 +1,2 @@
AGENT_VERSION=3.5.1
MASTER_VERSION=3.5.1
AGENT_VERSION=3.5.3
MASTER_VERSION=3.5.3