Commit 4f519e39f16cdbe60f64620bee3cc92c24c0e69a
1 parent
b683af4a
feat(wechat-auto-reply): auto_send 总开关搬进 viewer /whitelist UI
- app.py 加 GET/POST /api/auto_send:读写 policy_config.json 的 auto_send_enabled(root 属主 644,写经 tmp+su cp,只翻该字段保留 block_keywords) - whitelist.html 顶部加开关卡片(开=白名单自动发/敏感词仍拦,关=全审批),开启二次确认 - orchestrator 每轮 load_config() 即时生效,无需重启 - 真机验证:读接口 enabled=false、POST true/false 文件正确翻转其余字段完整、/whitelist 200 - README 待收尾 #4 标完成 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Showing
3 changed files
with
62 additions
and
1 deletions
wechat_plain/scripts/auto-reply/README.md
| ... | ... | @@ -88,7 +88,7 @@ python wechat_reply_orchestrator.py --drain # 手动跑一轮编排 |
| 88 | 88 | 1. **原生引用气泡 / 群@昵称前缀**:现发纯文本;微信原生引用(A11y 长按→引用)是环节5 增强。 |
| 89 | 89 | 2. **日志轮转**:watcher/orchestrator log 24/7 会长(ponytail 已标)。 |
| 90 | 90 | 3. **lifelog 人物增强**(二期):回链 `com.lemon.lifelog` 的 Person 库让回复带人物记忆。 |
| 91 | -4. **auto_send 开关搬进 viewer UI**:目前在 policy_config.json,可加到 /whitelist 页。 | |
| 91 | +4. ~~auto_send 开关搬进 viewer UI~~ ✅ **已做(2026-07-12)**:`/whitelist` 页顶部开关卡片,`GET/POST /api/auto_send` 读写 `policy_config.json`。 | |
| 92 | 92 | |
| 93 | 93 | ## 相关文档 |
| 94 | 94 | - `../../../md/wechat-auto-reply-requirements.md` — 需求 + 逐环节设计/状态 | ... | ... |
wechat_plain/scripts/auto-reply/viewer_templates/whitelist.html
| ... | ... | @@ -36,9 +36,37 @@ body{overflow-y:auto!important;height:auto!important;} |
| 36 | 36 | .btn-confirm{background:#27ae60;color:white;border:none;border-radius:8px;padding:9px 20px;font-size:14px;cursor:pointer;} |
| 37 | 37 | .btn-confirm:disabled{opacity:.5;cursor:not-allowed;} |
| 38 | 38 | .loading{text-align:center;padding:30px;color:#999;} |
| 39 | +/* 总开关卡片 */ | |
| 40 | +.as-card{background:white;border-radius:8px;padding:14px 16px;margin-bottom:14px;box-shadow:0 1px 2px rgba(0,0,0,0.05);} | |
| 41 | +.as-row{display:flex;align-items:center;gap:12px;} | |
| 42 | +.as-txt{flex:1;} | |
| 43 | +.as-title{font-weight:600;font-size:15px;color:#2c3e50;} | |
| 44 | +.as-state{font-size:12px;margin-top:2px;} | |
| 45 | +.as-state.on{color:#27ae60;font-weight:600;} | |
| 46 | +.as-state.off{color:#999;} | |
| 47 | +.switch{position:relative;display:inline-block;width:52px;height:30px;flex-shrink:0;} | |
| 48 | +.switch input{opacity:0;width:0;height:0;} | |
| 49 | +.slider{position:absolute;cursor:pointer;inset:0;background:#ccc;border-radius:30px;transition:.2s;} | |
| 50 | +.slider:before{content:"";position:absolute;height:24px;width:24px;left:3px;bottom:3px;background:white;border-radius:50%;transition:.2s;} | |
| 51 | +.switch input:checked+.slider{background:#27ae60;} | |
| 52 | +.switch input:checked+.slider:before{transform:translateX(22px);} | |
| 53 | +.switch input:disabled+.slider{opacity:.5;cursor:not-allowed;} | |
| 54 | +.as-warn{font-size:12px;color:#b9770e;background:#fef9e7;border-radius:6px;padding:8px 10px;margin-top:10px;line-height:1.5;} | |
| 39 | 55 | </style> |
| 40 | 56 | <div class="wl-container"> |
| 41 | 57 | <div class="wl-header"><h1>⭐ 回复白名单</h1><button class="btn-add" onclick="openPicker()">➕ 添加</button></div> |
| 58 | + | |
| 59 | +<div class="as-card"> | |
| 60 | + <div class="as-row"> | |
| 61 | + <div class="as-txt"> | |
| 62 | + <div class="as-title">自动发送总开关</div> | |
| 63 | + <div class="as-state off" id="asState">加载中…</div> | |
| 64 | + </div> | |
| 65 | + <label class="switch"><input type="checkbox" id="asToggle" onchange="toggleAuto(this)" disabled><span class="slider"></span></label> | |
| 66 | + </div> | |
| 67 | + <div class="as-warn">开=白名单里的联系人/群<b>直接自动发</b>(钱/承诺/约时间/隐私等敏感词仍强制转人工,绝不误发)。关=一切都进「审批」等你手动点发。建议审核模式跑几天、草稿质量稳了再开。</div> | |
| 68 | +</div> | |
| 69 | + | |
| 42 | 70 | <div class="wl-hint">白名单里的联系人 / 群,消息可被<b>自动回复</b>(仍受敏感词等安全规则拦截,命中转人工)。不在名单里的一律进「自动回复审批」等你手动发。</div> |
| 43 | 71 | <div id="list"></div> |
| 44 | 72 | </div> |
| ... | ... | @@ -75,6 +103,30 @@ function load(){ |
| 75 | 103 | } |
| 76 | 104 | function del(id){fetch('/api/whitelist/'+id+'/delete',{method:'POST'}).then(function(r){return r.json()}).then(function(j){if(j.ok)load();else alert('失败');});} |
| 77 | 105 | |
| 106 | +function renderAuto(on){ | |
| 107 | + var st=document.getElementById('asState'),tg=document.getElementById('asToggle'); | |
| 108 | + tg.checked=!!on;tg.disabled=false; | |
| 109 | + st.textContent=on?'已开启 · 白名单自动发':'已关闭 · 全部人工审批'; | |
| 110 | + st.className='as-state '+(on?'on':'off'); | |
| 111 | +} | |
| 112 | +function loadAuto(){ | |
| 113 | + fetch('/api/auto_send').then(function(r){return r.json()}).then(function(j){ | |
| 114 | + if(j.ok)renderAuto(j.enabled); | |
| 115 | + else{document.getElementById('asState').textContent='读取失败';} | |
| 116 | + }).catch(function(){document.getElementById('asState').textContent='读取失败';}); | |
| 117 | +} | |
| 118 | +function toggleAuto(cb){ | |
| 119 | + var want=cb.checked;cb.disabled=true; | |
| 120 | + if(want&&!confirm('确定开启自动发送?白名单里的消息将无需你确认直接发出(敏感词仍会拦截转人工)。')){ | |
| 121 | + cb.checked=false;cb.disabled=false;return; | |
| 122 | + } | |
| 123 | + fetch('/api/auto_send',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({enabled:want})}) | |
| 124 | + .then(function(r){return r.json()}).then(function(j){ | |
| 125 | + if(j.ok)renderAuto(j.enabled); | |
| 126 | + else{alert('失败: '+(j.error||''));cb.checked=!want;cb.disabled=false;} | |
| 127 | + }).catch(function(){alert('网络失败');cb.checked=!want;cb.disabled=false;}); | |
| 128 | +} | |
| 129 | + | |
| 78 | 130 | function openPicker(){ |
| 79 | 131 | selected={};updateCnt(); |
| 80 | 132 | document.getElementById('q').value=''; |
| ... | ... | @@ -120,5 +172,6 @@ function confirmAdd(){ |
| 120 | 172 | }); |
| 121 | 173 | } |
| 122 | 174 | load(); |
| 175 | +loadAuto(); | |
| 123 | 176 | </script> |
| 124 | 177 | {% endblock %} | ... | ... |
wechat_plain/scripts/auto-reply/wechat-viewer-changes.md
| ... | ... | @@ -34,6 +34,14 @@ CREATE TABLE reply_queue ( -- 策略判定后落地 |
| 34 | 34 | | `GET /api/contacts` | 弹窗默认列表:会话过的联系人+群(排除公众号),去重带昵称,最近排序 | |
| 35 | 35 | | `GET /api/contacts/search?q=` | 按名字/wxid 搜全量 rcontact+群(`wechat_db.search_talker`) | |
| 36 | 36 | | `POST /api/whitelist`(升级) | 支持批量 `{items:[{wxid,name,kind}]}`(弹窗多选) | |
| 37 | +| `GET /api/auto_send` | 读自动发总开关(`policy_config.json` 的 `auto_send_enabled`) | | |
| 38 | +| `POST /api/auto_send` | 设总开关 `{enabled:bool}`;只翻转该字段保留其余 | | |
| 39 | + | |
| 40 | +**自动发总开关搬进 UI(2026-07-12)**:`/whitelist` 页顶部加开关卡片(读 `/api/auto_send`, | |
| 41 | +拨动 POST 回写)。开=白名单可自动发(敏感词仍强制转人工),关=全部进审批。开启时前端二次确认。 | |
| 42 | +`policy_config.json` 是 root 属主 644,viewer(termux 用户)可直接读、写时经 `_write_auto_send` | |
| 43 | +写 tmp 再 `su cp` 回(沿用本 app 的 `su` 模式,只改 `auto_send_enabled` 不动 block_keywords 等)。 | |
| 44 | +orchestrator 每轮 `load_config()` 读文件即时生效,无需重启。 | |
| 37 | 45 | |
| 38 | 46 | **白名单选人弹窗(2026-07-10 升级)**:`/whitelist` 页「添加」不再手填 wxid,改为弹窗: |
| 39 | 47 | 默认列出你会话过的联系人/群(带昵称),顶部搜索框可查全量通讯录/群,复选框**多选**, | ... | ... |