import {createIcons, Send, Stars} from "lucide" import removeDiacritics from "./removeDiacritics.ts" export type ResponseDictType = { trigger?: Array; startsWith?: Array; endsWith?: Array; response: string; errors?: Record priority: number; special?: string; } const lang = window.lang const doors = window.doors const auth = window.auth const doorAction = window.doorAction const responses = lang === "de" ? (await import("./chatDict_de")).default : (await import("./chatDict_en")).default const templateInjectChat = `
` type ChatMessage = { type: "bot" | "user", message: string, special?: boolean; } const chat: Array = [] const chatWaiting = { value: false, } const timeouts: { wait: number | null; message: number | null; } = { wait: null, message: null, } function refreshChat() { const templateBot = (message: string) => `
${message}
` const templateUser = (message: string) => `
${message}
` const chatElement: HTMLDivElement = document.querySelector("#chat")! const chatWaitElement: HTMLDivElement = document.querySelector("#chat-waiting")! chatElement.innerHTML = "" chat.forEach(chatItem => { switch (chatItem.type) { case "bot": chatElement?.insertAdjacentHTML("beforeend", templateBot(chatItem.message)) break case "user": chatElement?.insertAdjacentHTML("beforeend", templateUser(chatItem.message)) break } }) chatWaitElement.classList.toggle("hidden", !chatWaiting.value) chatElement.scrollTo({ top: chatElement.scrollHeight, }) } function sendMessage() { const chatTextElement: HTMLTextAreaElement = document.querySelector("#chat-text")! const text = chatTextElement.value chatTextElement.value = "" if (text.trim() === "") return chat.push({ type: "user", message: text, }) refreshChat() timeouts.wait !== null && clearTimeout(timeouts.wait) timeouts.message !== null && clearTimeout(timeouts.message) timeouts.wait = setTimeout(() => { chatWaiting.value = true refreshChat() }, 500) timeouts.message = setTimeout(() => { chatWaiting.value = false getResponse(text) refreshChat() }, 2000) } function getResponse(message: string) { const sortedResponses = responses.sort( (a, b) => b.priority - a.priority) const msg = removeDiacritics(message.trim().normalize()).toLowerCase() const matchingResponses: Array<{ message: string, priority: number, }> = [] let highestMatchingPriority = 0 for (const response of sortedResponses) { let match = false for (const trigger of response.trigger ?? []) { if (msg.includes(trigger)) match = true } for (const start of response.startsWith ?? []) { if (msg.startsWith(start)) match = true } for (const end of response.endsWith ?? []) { if (msg.endsWith(end)) match = true } if (match) { if (response.priority > highestMatchingPriority) highestMatchingPriority = response.priority if (response.special) { // clear previous matches while (matchingResponses.length) { matchingResponses.pop() } if (response.special === "unlock" || response.special === "lock") { let matchedDoors = [] const action = response.special let doorListElement = `
    ` for (let door of doors) { if (msg.includes(door.label.toLowerCase())) { matchedDoors.push(door) } doorListElement += `
  • ${door.label}
  • ` } doorListElement += `
` let message = "" if (matchedDoors.length == 0 || !auth.authorized || !auth.authenticated) { if (matchedDoors.length == 0) { message = response.errors ? response.errors["unknownDoor"].replace("{doors}", doorListElement) : "" } if (!auth.authorized) { message = response.errors ? response.errors["unauthorized"] : "" } if (!auth.authenticated) { message = response.errors ? response.errors["unauthenticated"] : "" } } else { matchedDoors.forEach(door => { doorAction(action, door.id) }) message = response.response. replace("{door}", matchedDoors .map(door => `${door.label}`) .join()) } matchingResponses.push({ message: message, priority: response.priority, }) } // stop searching for more matches break } else { matchingResponses.push({ message: response.response, priority: response.priority, }) } } } if (matchingResponses.length > 0) { const matchingMessages = matchingResponses .filter(response => response.priority == highestMatchingPriority) .map(response => response.message) const chatMessages = matchingMessages.map(message => { return { type: "bot" as ChatMessage["type"], message: message, } }) chat.push(...chatMessages) } else { chat.push({ type: "bot", message: sortedResponses.at(-1)?.response ?? "", }) } } document.querySelector("body")?.insertAdjacentHTML("beforeend", templateInjectChat) createIcons({ nameAttr: "data-lucide", root: document.querySelector(".fab")!, icons: { Stars, Send, }, }) document.querySelector("#send-chat")!.addEventListener("click", sendMessage) document.querySelector("#chat-text")!.addEventListener("keydown", (e: Event) => { if ((e as KeyboardEvent).key === "Enter") { sendMessage() } })