176 lines
5.9 KiB
JavaScript
176 lines
5.9 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;
|
|
return;
|
|
}
|
|
$("wc-bind").disabled = false;
|
|
if (b.bound) {
|
|
setWc("ok", "已绑定 · 成员 " + (b.wecom_userid || ""));
|
|
$("wc-test").disabled = false; $("wc-unbind").disabled = false;
|
|
} else {
|
|
setWc("wait", "未绑定。点「绑定企业微信」扫码授权。");
|
|
$("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;
|
|
}
|
|
}
|
|
|
|
// ───── 顶层绑定 ─────
|
|
$("hd-wechat").onclick = openWechatModal;
|
|
$("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-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; }
|
|
};
|