#!/data/data/com.termux/files/usr/bin/bash # WeChat Viewer 看门狗 — 检查 Flask + Hermes Gateway + 微信 Watcher 是否存活 # 由 Hermes cron 每 5 分钟调用一次 export PATH=/data/data/com.termux/files/usr/bin:$PATH FLASK_PORT=5001 APP_DIR=/data/data/com.termux/files/home/wechat-viewer LOG=$APP_DIR/watchdog.log HOME_DIR=/data/data/com.termux/files/home # ── 1. Flask ────────────────────────────────────── ALIVE=$(su -c "ss -tlnp" 2>/dev/null | grep -c ":$FLASK_PORT") if [ "$ALIVE" -eq 0 ]; then echo "[$(date)] Flask 不在线,重启中..." | tee -a "$LOG" cd "$APP_DIR" pkill -f 'python3 app.py' 2>/dev/null sleep 1 fuser -k "$FLASK_PORT/tcp" 2>/dev/null nohup python3 app.py > $APP_DIR/flask.log 2>&1 & sleep 3 ALIVE2=$(su -c "ss -tlnp" 2>/dev/null | grep -c ":$FLASK_PORT") if [ "$ALIVE2" -gt 0 ]; then echo "[$(date)] Flask 重启成功" | tee -a "$LOG" else echo "[$(date)] Flask 重启失败!" | tee -a "$LOG" fi fi # ── 2. Hermes Gateway ────────────────────────────── GATEWAY_ALIVE=$(pgrep -f "hermes.*gateway" 2>/dev/null | wc -l) if [ "$GATEWAY_ALIVE" -eq 0 ]; then echo "[$(date)] Gateway 不在线,重启中..." | tee -a "$LOG" nohup hermes gateway > /tmp/hermes-gateway.log 2>&1 & sleep 3 GATEWAY_ALIVE2=$(pgrep -f "hermes.*gateway" 2>/dev/null | wc -l) if [ "$GATEWAY_ALIVE2" -gt 0 ]; then echo "[$(date)] Gateway 重启成功" | tee -a "$LOG" else echo "[$(date)] Gateway 重启失败!" | tee -a "$LOG" fi fi # ── 3. Stuck Job Reaper ────────────────────────── # 清理因 Flask 重启而孤儿化的 hermes_jobs python3 "$APP_DIR/reap_stuck_jobs.py" >> "$LOG" 2>&1 # ── 4. Orphan Report Reaper ───────────────────── # 清理因 Flask 重启丢回调而未入库的日报文件 python3 "$APP_DIR/reap_orphan_reports.py" >> "$LOG" 2>&1 # ── 5. 微信自动回复 Watcher ──────────────────────── # 监听微信 DB 变化,写入 incoming.jsonl 队列 # 单例保护:每次都先 pkill 清场再起一个,不能只信一次 ps|grep 的探测结果—— # 探测在系统过载时会漏检/超时返回空,若不清场会在卡死的旧实例上再叠一个, # 雪球式堆积出几百个进程拖垮整机(2026-07-25 事故:堆到 877 个吃满内存+swap)。 REPLY_DIR=$HOME_DIR/.hermes/wechat_reply WATCHER_COUNT=$(su -c 'ps -A' 2>/dev/null | grep -c '[w]echat_reply_watcher') if [ "$WATCHER_COUNT" -ne 1 ]; then echo "[$(date)] 微信 Watcher 存活数=$WATCHER_COUNT(期望 1),重置中..." | tee -a "$LOG" su -c "pkill -f wechat_reply_watcher.py" 2>/dev/null sleep 1 su -c "/data/data/com.termux/files/usr/bin/python3 $REPLY_DIR/wechat_reply_watcher.py >> $REPLY_DIR/watcher.log 2>&1 &" sleep 2 WATCHER_COUNT2=$(su -c 'ps -A' 2>/dev/null | grep -c '[w]echat_reply_watcher') if [ "$WATCHER_COUNT2" -eq 1 ]; then echo "[$(date)] 微信 Watcher 重置成功" | tee -a "$LOG" else echo "[$(date)] 微信 Watcher 重置后存活数异常: $WATCHER_COUNT2" | tee -a "$LOG" fi fi # ── 6. 微信自动回复 Orchestrator Drain ────────────── # 消费 incoming.jsonl 队列,处理候选事件 ORCH_LOCK=$REPLY_DIR/orch.lock # 清理超过 5 分钟的残留锁 if [ -f "$ORCH_LOCK" ]; then LOCK_AGE=$(($(date +%s) - $(stat -c %Y "$ORCH_LOCK" 2>/dev/null || echo 0))) if [ "$LOCK_AGE" -gt 300 ]; then rm -f "$ORCH_LOCK" fi fi su -c "/data/data/com.termux/files/usr/bin/python3 $REPLY_DIR/wechat_reply_orchestrator.py --drain >> $REPLY_DIR/orchestrator.log 2>&1"