From 53d1767a2d53c6adfb50be669c0b4f7cf456db51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Am=C4=93lija=20I?= Date: Tue, 29 Apr 2025 20:09:41 +0300 Subject: [PATCH] Adding ClientId; Adding Teacher Panel to start/end event recording --- .gitignore | 3 - manifest.json | 6 +- options.js | 70 ++++++++-- src/borderify.js | 323 ++++++++++++++++++++++++++++++----------------- 4 files changed, 266 insertions(+), 136 deletions(-) diff --git a/.gitignore b/.gitignore index 36abec9..219b3b7 100644 --- a/.gitignore +++ b/.gitignore @@ -12,9 +12,6 @@ dist # misc .DS_Store -# config -config.json - # local env files .env.local .env.development.local diff --git a/manifest.json b/manifest.json index 8e2ac6c..306926d 100644 --- a/manifest.json +++ b/manifest.json @@ -6,11 +6,13 @@ "author": "Your Name", "permissions": [ - "storage" + "storage", + "alarms" ], "host_permissions": [ - "http://localhost:5000/*" + "http://localhost:5000/*", + "https://zpdai.rkg.lv/*" ], "content_security_policy": { diff --git a/options.js b/options.js index e300986..f31240e 100644 --- a/options.js +++ b/options.js @@ -1,3 +1,5 @@ +const browserAPI = typeof browser !== 'undefined' ? browser : chrome; + function saveOptions(event) { event.preventDefault(); const serverUrl = document.querySelector("#server-url").value; @@ -7,21 +9,63 @@ function saveOptions(event) { alert("URL сервера и пароль не могут быть пустыми!"); return; } - - browser.storage.sync.set({ serverUrl, password }).then(() => { - const status = document.querySelector("#status"); - status.textContent = "Настройки сохранены!"; - setTimeout(() => (status.textContent = ""), 2000); - }); + + if (typeof browser !== 'undefined') { + browserAPI.storage.sync.set({ serverUrl, password }) + .then(() => { + showStatus("Настройки сохранены!"); + }) + .catch((error) => { + console.error("Ошибка сохранения настроек:", error); + showStatus("Ошибка сохранения!", true); + }); + } else { + browserAPI.storage.sync.set({ serverUrl, password }, () => { + if (chrome.runtime.lastError) { + console.error("Ошибка сохранения настроек:", chrome.runtime.lastError); + showStatus("Ошибка сохранения!", true); + } else { + showStatus("Настройки сохранены!"); + } + }); + } } - + function restoreOptions() { - browser.storage.sync.get(["serverUrl", "password"]).then((result) => { - const serverUrl = result.serverUrl || "http://localhost:5000/submit"; - const password = result.password || ""; - document.querySelector("#server-url").value = serverUrl; - document.querySelector("#password").value = password; - }); + if (typeof browser !== 'undefined') { + browserAPI.storage.sync.get(["serverUrl", "password"]) + .then((result) => { + updateFormFields(result); + }) + .catch((error) => { + console.error("Ошибка загрузки настроек:", error); + updateFormFields({}); + }); + } else { + browserAPI.storage.sync.get(["serverUrl", "password"], (result) => { + if (chrome.runtime.lastError) { + console.error("Ошибка загрузки настроек:", chrome.runtime.lastError); + updateFormFields({}); + } else { + updateFormFields(result); + } + }); + } +} + +function updateFormFields(result) { + document.querySelector("#server-url").value = result.serverUrl || "http://localhost:5000/submit"; + document.querySelector("#password").value = result.password || ""; +} + +function showStatus(message, isError = false) { + const status = document.querySelector("#status"); + status.textContent = message; + status.style.color = isError ? "red" : "green"; + setTimeout(() => { + status.textContent = ""; + status.style.color = ""; + }, 2000); } document.addEventListener("DOMContentLoaded", restoreOptions); diff --git a/src/borderify.js b/src/borderify.js index b1a0749..7c48718 100644 --- a/src/borderify.js +++ b/src/borderify.js @@ -1,38 +1,74 @@ +const browserAPI = typeof browser !== 'undefined' ? browser : chrome; + let serverUrl = "http://localhost:5000/submit"; let password = ""; +let clientId = null; +let isRecording = false; +let checkInterval = null; +let currentSessionId = null; + function loadSettings() { - return browser.storage.sync.get(["serverUrl", "password"]).then((result) => { - if (result.serverUrl) { - serverUrl = result.serverUrl; - console.log("URL сервера загружен из настроек:", serverUrl); - } - if (result.password) { - password = result.password; - console.log("Пароль загружен из настроек."); - } else { - return fetch(browser.runtime.getURL("config.json")) - .then(response => { - if (!response.ok) { - throw new Error(`Ошибка загрузки config.json: ${response.statusText}`); - } - return response.json(); + return new Promise((resolve) => { + if (typeof browser !== 'undefined') { + browserAPI.storage.sync.get(["serverUrl", "password"]) + .then((result) => { + processSettings(result); + resolve(); }) - .then(config => { - serverUrl = config.FLASK_SERVER_URL; - password = config.PASSWORD || password; - console.log("URL сервера успешно загружен:", serverUrl); + .catch((error) => { + console.error("Ошибка загрузки настроек:", error); + resolve(); }); + } else { + browserAPI.storage.sync.get(["serverUrl", "password"], (result) => { + if (chrome.runtime.lastError) { + console.error("Ошибка загрузки настроек:", chrome.runtime.lastError); + } else { + processSettings(result); + } + resolve(); + }); } - }).catch((error) => { - console.error("Ошибка загрузки URL сервера:", error); }); } -loadSettings(); +function processSettings(result) { + if (result.serverUrl) { + serverUrl = result.serverUrl; + console.log("URL сервера загружен из настроек:", serverUrl); + } + if (result.password) { + password = result.password; + console.log("Пароль загружен из настроек."); + } +} - -let clientId = null; +function handleClientId() { + if (typeof browser !== 'undefined') { + browserAPI.storage.sync.get("clientId") + .then((result) => { + if (result.clientId) { + clientId = result.clientId; + console.log("ID клиента загружен:", clientId); + } else { + generateAndSaveId(); + } + }) + .catch(console.error); + } else { + browserAPI.storage.sync.get("clientId", (result) => { + if (chrome.runtime.lastError) { + console.error("Ошибка загрузки ID клиента:", chrome.runtime.lastError); + } else if (result.clientId) { + clientId = result.clientId; + console.log("ID клиента загружен:", clientId); + } else { + generateAndSaveId(); + } + }); + } +} function generateUUID() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { @@ -42,19 +78,54 @@ function generateUUID() { }); } -browser.storage.sync.get("clientId").then((result) => { - if (result.clientId) { - clientId = result.clientId; - console.log("ID клиента загружен:", clientId); +function generateAndSaveId() { + clientId = generateUUID(); + if (typeof browser !== 'undefined') { + browserAPI.storage.sync.set({ clientId }) + .then(() => console.log("ID клиента создан и сохранен:", clientId)) + .catch(console.error); } else { - clientId = generateUUID(); - browser.storage.sync.set({ clientId }).then(() => { - console.log("ID клиента создан и сохранен:", clientId); + browserAPI.storage.sync.set({ clientId }, () => { + if (chrome.runtime.lastError) { + console.error("Ошибка сохранения ID клиента:", chrome.runtime.lastError); + } else { + console.log("ID клиента создан и сохранен:", clientId); + } }); } -}).catch((error) => { - console.error("Ошибка загрузки ID клиента:", error); -}); +} + + +async function checkSessionStatus() { + try { + const statusUrl = 'http://127.0.0.1:5000/api/session_status'; + const response = await fetch(statusUrl, { + headers: { 'X-Password': password } + }); + + const { active, session_id } = await response.json(); + + if (active && !isRecording) { + startRecording(session_id); + } else if (!active && isRecording) { + stopRecording(); + } + } catch (error) { + console.error('Ошибка проверки сессии:', error); + } +} + +function startRecording(sessionId) { + isRecording = true; + currentSessionId = sessionId; + console.log('Запись действий начата для сессии:', sessionId); +} + +function stopRecording() { + isRecording = false; + currentSessionId = null; + console.log('Запись действий остановлена'); +} function createEventJSON(eventType, data) { @@ -71,10 +142,8 @@ function createEventJSON(eventType, data) { async function sendDataToServer(eventData) { - if (!serverUrl || !password) { - console.error("URL сервера или пароль не заданы. Проверьте настройки."); - return; - } + if (!isRecording || !serverUrl || !password) return; + try { const response = await fetch(serverUrl, { method: 'POST', @@ -82,7 +151,10 @@ async function sendDataToServer(eventData) { 'Content-Type': 'application/json', 'X-Password': password, }, - body: JSON.stringify(eventData), + body: JSON.stringify({ + ...eventData, + session_id: currentSessionId + }) }); const responseData = await response.json(); @@ -96,104 +168,119 @@ async function sendDataToServer(eventData) { } } - -document.addEventListener("keydown", (event) => { - const key = event.key; - const code = event.code; - let description; - - if (key.match(/^\p{Number}$/u)) { - description = "number"; - } else if (key.match(/^\p{Letter}$/u)) { - description = "letter"; - } else if (key.match(/^\p{Punctuation}$|^\p{Symbol}$/u)) { - description = "symbol"; - } else { - description = `Клавиша: ${key} Код: ${code}`; - } - - const eventData = createEventJSON('keydown', { description }); - console.log("Событие keydown:", JSON.stringify(eventData)); - sendDataToServer(eventData); -}); - - -document.addEventListener("mousedown", (event) => { - const eventData = createEventJSON('mousedown', { button: event.button, description: `Клик мышью: ${event.button}` }); - console.log("Событие mousedown:", JSON.stringify(eventData)); - sendDataToServer(eventData); -}); - -function debounce(func, wait) { +function debounce(func, delay) { let timeout; return function(...args) { clearTimeout(timeout); - timeout = setTimeout(() => func.apply(this, args), wait); + timeout = setTimeout(() => func(...args), delay); }; } -let accumulatedPixels = 0; +async function initialize() { + await loadSettings(); + handleClientId(); -const handleWheel = debounce((event) => { - const direction = accumulatedPixels > 0 ? "вниз" : "вверх"; - const pixels = Math.abs(accumulatedPixels); + checkInterval = setInterval(checkSessionStatus, 5000); + checkSessionStatus(); - const eventData = createEventJSON('wheel', { direction, pixels: `${pixels}px`, description: "Прокрутка колёсика мыши"}); - console.log("Событие wheel:", JSON.stringify(eventData)); - sendDataToServer(eventData); - accumulatedPixels = 0; -}, 200); + document.addEventListener("keydown", (event) => { + if (!isRecording) return; + const key = event.key; + const code = event.code; + let description; -document.addEventListener("wheel", (event) => { accumulatedPixels += event.deltaY; handleWheel(event);}); + if (key.match(/^\p{Number}$/u)) { + description = "number"; + } else if (key.match(/^\p{Letter}$/u)) { + description = "letter"; + } else if (key.match(/^\p{Punctuation}$|^\p{Symbol}$/u)) { + description = "symbol"; + } else { + description = `Клавиша: ${key} Код: ${code}`; + } -document.addEventListener("click", (event) => { - const eventType = 'click'; - const clickedElement = event.target; - const tag = clickedElement.tagName; - const id = clickedElement.id || "отсутствует"; - const className = clickedElement.className || "отсутствует"; + const eventData = createEventJSON('keydown', { description }); + console.log("Событие keydown:", JSON.stringify(eventData)); + sendDataToServer(eventData); + }); - let description = "Клик по элементу"; - let data = { tag, id, className }; - if (tag === 'A') { - const href = clickedElement.href; - data.href = href; - description = "Клик по ссылке"; + document.addEventListener("mousedown", (event) => { + if (!isRecording) return; + const eventData = createEventJSON('mousedown', { button: event.button, description: `Клик мышью: ${event.button}` }); + console.log("Событие mousedown:", JSON.stringify(eventData)); + sendDataToServer(eventData); + }); - localStorage.setItem('clickedElementInfo', JSON.stringify(data)); - navigateWithPromise(href); - } else if (tag === 'INPUT') { - const placeholder = clickedElement.placeholder || "отсутствует"; - data.placeholder = placeholder; - description = "Клик по полю ввода"; + let accumulatedPixels = 0; + + const handleWheel = debounce((event) => { + if (!isRecording) return; + const direction = accumulatedPixels > 0 ? "вниз" : "вверх"; + const pixels = Math.abs(accumulatedPixels); + + const eventData = createEventJSON('wheel', { direction, pixels: `${pixels}px`, description: "Прокрутка колёсика мыши"}); + console.log("Событие wheel:", JSON.stringify(eventData)); + sendDataToServer(eventData); + + accumulatedPixels = 0; + }, 200); + + document.addEventListener("wheel", (event) => { if (!isRecording) return; accumulatedPixels += event.deltaY; handleWheel(event);}); + + document.addEventListener("click", (event) => { + if (!isRecording) return; + const eventType = 'click'; + const clickedElement = event.target; + const tag = clickedElement.tagName; + const id = clickedElement.id || "отсутствует"; + const className = clickedElement.className || "отсутствует"; + + let description = "Клик по элементу"; + let data = { tag, id, className }; + + if (tag === 'A') { + const href = clickedElement.href; + data.href = href; + description = "Клик по ссылке"; + + localStorage.setItem('clickedElementInfo', JSON.stringify(data)); + navigateWithPromise(href); + } else if (tag === 'INPUT') { + const placeholder = clickedElement.placeholder || "отсутствует"; + data.placeholder = placeholder; + description = "Клик по полю ввода"; + } + + data.description = description; + + const eventData = createEventJSON(eventType, data); + console.log("Событие click:", JSON.stringify(eventData)); + sendDataToServer(eventData); + }); + + + function navigateWithPromise(href) { + return new Promise((resolve) => { + window.location.href = href; + window.addEventListener('load', resolve); + }); } - data.description = description; - const eventData = createEventJSON(eventType, data); - console.log("Событие click:", JSON.stringify(eventData)); - sendDataToServer(eventData); -}); + document.addEventListener("copy", (event) => { + if (!isRecording) return; + const selectedText = window.getSelection().toString(); + let outputText = selectedText.length > 50 ? selectedText.substring(0, 49) + '…' : selectedText; -function navigateWithPromise(href) { - return new Promise((resolve) => { - window.location.href = href; - window.addEventListener('load', resolve); + if (selectedText.length > 0) { + const eventData = createEventJSON('copy', { selectedText: outputText, description: "Скопирован текст" }); + console.log("Событие copy:", JSON.stringify(eventData)); + sendDataToServer(eventData); + } }); } - -document.addEventListener("copy", (event) => { - const selectedText = window.getSelection().toString(); - - let outputText = selectedText.length > 50 ? selectedText.substring(0, 49) + '…' : selectedText; - - if (selectedText.length > 0) { - const eventData = createEventJSON('copy', { selectedText: outputText, description: "Скопирован текст" }); - console.log("Событие copy:", JSON.stringify(eventData)); - sendDataToServer(eventData); - } -}); \ No newline at end of file +initialize(); \ No newline at end of file