Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
539de62eee | ||
|
|
d514106e65 | ||
|
|
7f9c8a4dea | ||
|
|
31014e571d | ||
|
|
f5aa68a8dc | ||
|
|
63bbbd549e | ||
|
|
5e40ed426b | ||
|
|
aebf3a9e90 | ||
|
|
8a3d7c305b | ||
|
|
2d580eaea2 | ||
|
|
d7ab695372 | ||
|
|
0c250dfd17 | ||
|
|
52b12c7057 | ||
|
|
2166753569 | ||
|
|
46e418dfd0 | ||
|
|
673e0ce3e6 | ||
|
|
6203249782 | ||
|
|
05e708ed21 | ||
|
|
c4faa102cf | ||
|
|
4a1b32278c | ||
|
|
584ead387e | ||
|
|
efdc62ba68 | ||
|
|
67618587e4 | ||
|
|
a2c045a841 | ||
|
|
9b35c87cb4 | ||
|
|
e6c6e66e4b | ||
|
|
f1d36a2148 | ||
|
|
41906d0570 | ||
|
|
dc7d1c0f40 | ||
|
|
9768bed637 | ||
|
|
fa202a0405 | ||
|
|
b8bcd09134 | ||
|
|
ef8dc033cc | ||
|
|
03a54bcca0 | ||
|
|
f4f93d8955 | ||
|
|
07cac792f0 | ||
|
|
50edad9e25 | ||
|
|
69edf12620 | ||
|
|
0b448e2b7e | ||
|
|
d7b95136dd | ||
|
|
80d74111c4 | ||
|
|
223d7c4b56 |
23
README.md
23
README.md
@@ -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 透明计数器网关源码
|
||||
```
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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}"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
江美儀
|
||||
楊何蓓茵
|
||||
樂珈嘉
|
||||
姜濤
|
||||
|
||||
@@ -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
|
||||
エルニーニョ
|
||||
アトレティコ 対 バルセロナ
|
||||
オープンワールド
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
25
data/keywords/kw_TW.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
歐聯
|
||||
菡生婦幼診所
|
||||
台鐵訂票
|
||||
飛機
|
||||
東光路
|
||||
货币
|
||||
amd
|
||||
rklb
|
||||
航空母艦
|
||||
axti
|
||||
Yahoo奇摩
|
||||
天氣
|
||||
蝦皮購物
|
||||
PChome
|
||||
Momo購物網
|
||||
Mobile01
|
||||
Dcard
|
||||
巴哈姆特
|
||||
中時電子報
|
||||
聯合新聞網
|
||||
台灣高鐵
|
||||
台鐵時刻表
|
||||
中華電信
|
||||
統一發票
|
||||
勞動部
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
124
data/map.json
124
data/map.json
@@ -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 (蒙特利尔)" }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
17
data/regions/CA/Default/Montreal.json
Normal file
17
data/regions/CA/Default/Montreal.json
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
20
data/regions/TW/Default/Taipei.json
Normal file
20
data/regions/TW/Default/Taipei.json
Normal 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/"
|
||||
]
|
||||
}
|
||||
}
|
||||
17
data/regions/UK/Default/Coventry.json
Normal file
17
data/regions/UK/Default/Coventry.json
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
5
data/regions/US/IL/Warrenville.json
Normal file
5
data/regions/US/IL/Warrenville.json
Normal 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/" ] }
|
||||
}
|
||||
5
data/regions/US/NC/Charlotte.json
Normal file
5
data/regions/US/NC/Charlotte.json
Normal 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/" ] }
|
||||
}
|
||||
5
data/regions/US/NV/Las_Vegas.json
Normal file
5
data/regions/US/NV/Las_Vegas.json
Normal 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/" ] }
|
||||
}
|
||||
5
data/regions/US/OR/Bend.json
Normal file
5
data/regions/US/OR/Bend.json
Normal 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/" ] }
|
||||
}
|
||||
5
data/regions/US/UT/Salt_Lake_City.json
Normal file
5
data/regions/US/UT/Salt_Lake_City.json
Normal 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/" ] }
|
||||
}
|
||||
5
data/regions/US/WA/Seattle.json
Normal file
5
data/regions/US/WA/Seattle.json
Normal 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/" ] }
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
AGENT_VERSION=3.5.1
|
||||
MASTER_VERSION=3.5.1
|
||||
AGENT_VERSION=3.5.3
|
||||
MASTER_VERSION=3.5.3
|
||||
|
||||
Reference in New Issue
Block a user