feat(wechat): 关注公众号后自动回复欢迎语(被动回复,可后台配置) #12

Merged
Blizzard merged 42 commits from feat/site into main 2026-07-22 07:24:18 +00:00
Showing only changes of commit a4b9897eaf - Show all commits
+34 -7
View File
@@ -201,15 +201,42 @@ export class VoiceClient {
if (!navigator.mediaDevices?.getUserMedia) { if (!navigator.mediaDevices?.getUserMedia) {
throw new Error("此窗口暂不支持麦克风(原生壳需授权/安全上下文)。用浏览器打开 localhost:5173 可直接说话。"); throw new Error("此窗口暂不支持麦克风(原生壳需授权/安全上下文)。用浏览器打开 localhost:5173 可直接说话。");
} }
// 先试带回声消除等约束(浏览器更好),WKWebView(原生壳)不认高级约束会抛 "Invalid constraint" // 先看有没有麦克风设备:Mac Studio/Mini 等无内置麦克风的机器上,getUserMedia 会以
// 退回最简 {audio:true}(最兼容;多数实现默认已开回声消除) // OverconstrainedError "Invalid constraint" 报错(误导),提前给准确提示
let stream: MediaStream; let devs: MediaDeviceInfo[] = [];
try { try {
stream = await navigator.mediaDevices.getUserMedia({ devs = await navigator.mediaDevices.enumerateDevices();
audio: { echoCancellation: true, noiseSuppression: true, channelCount: 1 },
});
} catch { } catch {
stream = await navigator.mediaDevices.getUserMedia({ audio: true }); /* 拿不到设备列表就跳过预检,直接试 getUserMedia */
}
if (devs.length > 0 && !devs.some((d) => d.kind === "audioinput")) {
throw new Error("没检测到麦克风设备。这台机器(如 Mac Studio/Mini)可能无内置麦克风——插个麦克风(带麦耳机 / USB 麦 / AirPods)再试。");
}
// 依次尝试多种约束形式:WKWebView(原生壳)对约束挑剔,不同实现接受的形式不同。
// 全失败则把**真实错误名**抛出来(区分是"约束不认"Overconstrained 还是"权限被拒"NotAllowed)。
const tries: MediaStreamConstraints[] = [
{ audio: true },
{ audio: {} },
{ audio: { echoCancellation: true, noiseSuppression: true } },
];
let stream: MediaStream | null = null;
let lastErr: unknown;
for (const c of tries) {
try {
stream = await navigator.mediaDevices.getUserMedia(c);
break;
} catch (e) {
lastErr = e;
}
}
if (!stream) {
const err = lastErr as { name?: string; message?: string };
const detail = `${err?.name ?? "Error"}: ${err?.message ?? String(lastErr)}`;
if (err?.name === "NotAllowedError" || err?.name === "SecurityError") {
throw new Error("麦克风权限被拒。请到 系统设置 → 隐私与安全性 → 麦克风 里允许本应用。");
}
throw new Error("麦克风打开失败:" + detail);
} }
this.micStream = stream; this.micStream = stream;
const ctx = new AudioContext(); const ctx = new AudioContext();