zcbot/web/static/js/wechat.js

188 lines
6.5 KiB
JavaScript

// 微信绑定 modal(ClawBot 扫码,DESIGN §8.7):扫码绑定 + 自检发送 + 解绑。
// 左侧 rail「微信」按钮触发。后端:POST /v1/wechat/bind/qrcode、GET /v1/wechat/bind/status、
// GET /v1/wechat/bind、DELETE /v1/wechat/bind、POST /v1/wechat/test。
import { $ } from "./dom.js";
import { api } from "./api.js";
let _polling = false;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
function setState(cls, msg) {
const el = $("wx-state");
el.className = "wx-status " + cls;
el.textContent = msg;
}
async function refresh() {
try {
const b = await api("GET", "/v1/wechat/bind");
if (b.bound) {
const push = b.can_push ? "可主动推送" : "需在微信里发条消息以开启主动推送";
setState("ok", "已绑定" + (b.user_im_id ? "" : "(待首次开口)") + " · " + push);
$("wx-test").disabled = false;
$("wx-unbind").disabled = false;
} else {
setState("wait", "尚未绑定。点「绑定」生成二维码,用手机微信扫。");
$("wx-test").disabled = true;
$("wx-unbind").disabled = true;
}
} catch (e) {
setState("err", "查询失败: " + e.message +
"(确认已上 migration 0012 + 开 ZCBOT_WECHAT_BOT_ENABLED)");
$("wx-test").disabled = true;
$("wx-unbind").disabled = true;
}
}
function openWechatModal() {
$("wechat-modal").classList.add("show");
$("wx-qrbox").hidden = true;
refresh();
refreshWecom();
}
// ───── 企业微信(仅推送)─────
function setWc(cls, msg) {
const el = $("wc-state");
el.className = "wx-status " + cls;
el.textContent = msg;
}
async function refreshWecom() {
try {
const b = await api("GET", "/v1/wecom/bind");
if (!b.configured) {
setWc("wait", "未配置:管理员需建自建应用 + 配 WECOM_CORPID/AGENTID/SECRET。");
$("wc-bind").disabled = true; $("wc-test").disabled = true; $("wc-unbind").disabled = true;
$("wc-bind-id").disabled = true;
return;
}
$("wc-bind").disabled = false; $("wc-bind-id").disabled = false;
if (b.bound) {
setWc("ok", "已绑定 · 成员 " + (b.wecom_userid || ""));
$("wc-userid").value = b.wecom_userid || "";
$("wc-test").disabled = false; $("wc-unbind").disabled = false;
} else {
setWc("wait", "未绑定。扫码授权(需 HTTPS 域名),或下方手填 userid 保存。");
$("wc-test").disabled = true; $("wc-unbind").disabled = true;
}
} catch (e) {
setWc("err", "查询失败: " + e.message);
}
}
async function wecomBind() {
$("wc-bind").disabled = true;
try {
const r = await api("GET", "/v1/wecom/oauth/url");
window.open(r.authorize_url, "_blank");
setWc("wait", "已打开授权页…扫码 / 确认后会自动刷新(也可手动重开本面板)。");
for (let i = 0; i < 30; i++) { // 轮询绑定结果 ~60s
await sleep(2000);
try {
const b = await api("GET", "/v1/wecom/bind");
if (b.bound) { await refreshWecom(); return; }
} catch (e) { /* 继续轮询 */ }
}
await refreshWecom();
} catch (e) {
setWc("err", "绑定出错: " + e.message);
} finally {
$("wc-bind").disabled = false;
}
}
export function closeWechatModal() {
_polling = false;
$("wechat-modal").classList.remove("show");
}
async function bindFlow() {
if (_polling) return;
_polling = true;
$("wx-bind").disabled = true;
try {
while (_polling) { // 二维码过期自动换新
const q = await api("POST", "/v1/wechat/bind/qrcode");
$("wx-qrimg").src = q.qr_png;
$("wx-qrbox").hidden = false;
setState("wait", "等待扫码…(二维码约 1 分钟过期,会自动换新)");
let expired = false;
while (_polling && !expired) {
let s;
try {
s = await api("GET", "/v1/wechat/bind/status?qrcode_id=" +
encodeURIComponent(q.qrcode_id));
} catch (e) { await sleep(2000); continue; }
if (s.status === "confirmed") {
_polling = false;
$("wx-qrbox").hidden = true;
setState("ok", "绑定成功!去微信「微信 ClawBot」发句话试试。");
await refresh();
return;
}
if (s.status === "expired") { expired = true; break; }
await sleep(1000);
}
}
} catch (e) {
setState("err", "绑定出错: " + e.message);
} finally {
_polling = false;
$("wx-bind").disabled = false;
}
}
// ───── 顶层绑定 ─────
// 弹框现在由「新建任务」下方的渠道卡片触发(点绑定/管理按钮),不再需要 rail 按钮。
$("wx-close").onclick = closeWechatModal;
$("wechat-modal").addEventListener("click", (e) => {
if (e.target.id === "wechat-modal") closeWechatModal(); // 点遮罩关闭
});
$("wx-bind").onclick = bindFlow;
$("wx-unbind").onclick = async () => {
_polling = false;
if (!confirm("确定解绑微信?")) return;
try {
await api("DELETE", "/v1/wechat/bind");
$("wx-qrbox").hidden = true;
await refresh();
} catch (e) { setState("err", "解绑失败: " + e.message); }
};
$("wx-test").onclick = async () => {
$("wx-test").disabled = true;
try {
const r = await api("POST", "/v1/wechat/test");
if (r.ok) setState("ok", "测试消息已发送,去微信查收。");
else setState("err", "推送未送达(" + r.reason +
")。多半是超 24h 没在微信互动:发条消息再试。");
} catch (e) {
setState("err", "测试失败: " + e.message);
} finally { $("wx-test").disabled = false; }
};
$("wc-bind").onclick = wecomBind;
$("wc-bind-id").onclick = async () => {
const uid = ($("wc-userid").value || "").trim();
if (!uid) { setWc("err", "请先填入 userid。"); return; }
$("wc-bind-id").disabled = true;
try {
await api("PUT", "/v1/wecom/bind/userid", { wecom_userid: uid });
await refreshWecom();
} catch (e) { setWc("err", "保存失败: " + e.message); }
finally { $("wc-bind-id").disabled = false; }
};
$("wc-unbind").onclick = async () => {
if (!confirm("确定解绑企业微信?")) return;
try { await api("DELETE", "/v1/wecom/bind"); await refreshWecom(); }
catch (e) { setWc("err", "解绑失败: " + e.message); }
};
$("wc-test").onclick = async () => {
$("wc-test").disabled = true;
try {
const r = await api("POST", "/v1/wecom/test");
if (r.ok) setWc("ok", "测试消息已发送,去企业微信查收。");
else setWc("err", "推送未送达(" + r.reason + ")。");
} catch (e) { setWc("err", "测试失败: " + e.message); }
finally { $("wc-test").disabled = false; }
};