feat(dashboard): merge lists button, drop STT test, add bot/log settings, barge-in

Dashboard changes requested by the user:
- Merge 화이트리스트/블랙리스트 into one "👤 유저 등록/관리" button (the modal
  already holds both lists).
- Remove the 음성 인식(STT) 테스트 section and its client JS.
- Add collapsible "⚙️ 봇 관련 설정" with a barge-in toggle: "유저 목소리 들을 때
  봇이 말하던 것 중지" (default on). Stored via /api/bot/settings; the bot reads
  it on its report round-trip and calls voicePlayer.stop() when an allowed user
  starts speaking (dave/bot.mjs).
- Add collapsible "🧹 로그 관련 설정" with a "잡음 로그 표시 안 함" toggle (default
  on, localStorage-backed): hides turns whose 들음 is (빈 결과) or 답변 is [잡음].
- Fix the fixed event/error log dock covering the bottom-most turn: syncDockPad()
  pads main by the dock height (on load, log render, toggle, resize).

BotControl gains a settings store; /api/bot/report now also returns settings.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
EJClaw
2026-08-26 23:19:21 +09:00
parent b782ca70bd
commit b0272d2171
3 changed files with 104 additions and 60 deletions

View File

@@ -177,6 +177,7 @@ let currentGuildId = null, currentChannelId = null, currentChannelName = null;
const speakingSet = new Set(); // userIds currently speaking (for participant list)
const activeSubs = new Set(); // userIds with an in-flight receive subscription
let listsByGuild = {}; // guildId -> {whitelistUsers, blacklistUsers, whitelistRoles, blacklistRoles}
let botSettings = { bargeIn: true }; // behaviour toggles from the dashboard
// Attach the bot's audio player (so it can speak) to a fresh connection.
function setupPlayer(connection) {
@@ -201,6 +202,11 @@ function setupReceiver(connection) {
return;
}
}
// Barge-in: the moment an allowed user speaks, stop the bot's current TTS
// so it doesn't talk over them (toggleable from the dashboard, default on).
if (botSettings.bargeIn !== false && voicePlayer) {
try { voicePlayer.stop(true); } catch {}
}
activeSubs.add(userId);
if (!perUser.has(userId)) perUser.set(userId, { opusPackets: 0, pcmFrames: 0 });
const opusStream = receiver.subscribe(userId, {
@@ -314,6 +320,7 @@ async function reportLoop() {
j = await r.json();
} catch { return; } // dashboard down: keep running, retry next tick
if (j?.lists) listsByGuild = j.lists; // latest whitelist/blacklist config
if (j?.settings) botSettings = j.settings; // latest behaviour toggles
for (const cmd of (j?.commands || [])) {
try { await handleCommand(cmd); } catch (e) { log(`command ${cmd?.type} failed: ${e.message}`); }
}