Add wakeword command routing and the reply-only agent client

This commit is contained in:
Fisto
2026-09-22 09:23:11 +02:00
parent a4d61781e9
commit d99d614c60
14 changed files with 399 additions and 34 deletions
+58
View File
@@ -26,6 +26,7 @@ const state = {
visibleGuildsSaved: null,
timeZoneOverride: localStorage.getItem("veskslop-timezone") ?? "",
speech: { provider: null, language: null, voice: null },
commands: { wakeword: "!s" },
profileDefaults: {},
expressionDefault: null,
scope: "guild",
@@ -1788,6 +1789,45 @@ async function refreshAudio() {
await refreshSpeech();
}
/* Command wakeword (Settings -> Commands). */
const WAKEWORD_CHOICES = ["!s", "!x", "!c", "!z", "!q", "!e", "!r", "!f", "!v", "!t", "!g", "!b"];
function fillWakewordControls() {
const wakeword = state.commands?.wakeword || "!s";
const select = $("#wakeword-select");
const customRow = $("#wakeword-custom-row");
if (!select || !customRow) return;
if (WAKEWORD_CHOICES.includes(wakeword)) {
select.value = wakeword;
customRow.hidden = true;
} else {
select.value = "custom";
$("#wakeword-custom").value = wakeword;
customRow.hidden = false;
}
$("#wakeword-status").textContent = wakeword;
}
async function saveWakeword(value) {
const wakeword = String(value ?? "").trim();
if (!wakeword || wakeword.length > 8 || /\s/.test(wakeword)) {
toast("the wakeword must be 1-8 characters with no spaces", "error");
fillWakewordControls();
return;
}
try {
const prefs = await VK.api.post("/api/preferences", { commands: { wakeword } });
state.commands = prefs.commands ?? { wakeword };
fillWakewordControls();
} catch (error) {
toast(`wakeword save failed: ${error.message}`, "error");
fillWakewordControls();
}
}
/* Discord voice processing gates (Settings -> TTS panel). */
function fillVoiceGates(voice) {
const gates = voice ?? {};
@@ -1846,7 +1886,9 @@ async function refreshSpeech() {
try {
const prefs = await VK.api.get("/api/preferences");
state.speech = prefs.tts ?? { provider: null, language: null, voice: null, speed: null, pitch: null };
state.commands = prefs.commands ?? { wakeword: "!s" };
fillVoiceGates(prefs.voice);
fillWakewordControls();
} catch {
// preferences are optional
}
@@ -2764,6 +2806,22 @@ function init() {
$(id)?.addEventListener("change", () => void saveVoiceGates());
}
$("#wakeword-select")?.addEventListener("change", () => {
const value = $("#wakeword-select").value;
if (value === "custom") {
$("#wakeword-custom").value = WAKEWORD_CHOICES.includes(state.commands?.wakeword)
? "!"
: state.commands?.wakeword || "!";
$("#wakeword-custom-row").hidden = false;
$("#wakeword-custom").focus();
return;
}
void saveWakeword(value);
});
$("#wakeword-custom")?.addEventListener("change", () => void saveWakeword($("#wakeword-custom").value));
$("#wakeword-save")?.addEventListener("click", () => void saveWakeword($("#wakeword-custom").value));
$("#tts-duck").addEventListener("click", async () => {
const on = !$("#tts-duck").classList.contains("vk-chip-on");
try {