Adding ClientId; Adding Teacher Panel to start/end event recording
parent
fb3d497aa3
commit
53d1767a2d
|
@ -12,9 +12,6 @@ dist
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# config
|
|
||||||
config.json
|
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
.env.local
|
.env.local
|
||||||
.env.development.local
|
.env.development.local
|
||||||
|
|
|
@ -6,11 +6,13 @@
|
||||||
"author": "Your Name",
|
"author": "Your Name",
|
||||||
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage"
|
"storage",
|
||||||
|
"alarms"
|
||||||
],
|
],
|
||||||
|
|
||||||
"host_permissions": [
|
"host_permissions": [
|
||||||
"http://localhost:5000/*"
|
"http://localhost:5000/*",
|
||||||
|
"https://zpdai.rkg.lv/*"
|
||||||
],
|
],
|
||||||
|
|
||||||
"content_security_policy": {
|
"content_security_policy": {
|
||||||
|
|
70
options.js
70
options.js
|
@ -1,3 +1,5 @@
|
||||||
|
const browserAPI = typeof browser !== 'undefined' ? browser : chrome;
|
||||||
|
|
||||||
function saveOptions(event) {
|
function saveOptions(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const serverUrl = document.querySelector("#server-url").value;
|
const serverUrl = document.querySelector("#server-url").value;
|
||||||
|
@ -7,21 +9,63 @@ function saveOptions(event) {
|
||||||
alert("URL сервера и пароль не могут быть пустыми!");
|
alert("URL сервера и пароль не могут быть пустыми!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
browser.storage.sync.set({ serverUrl, password }).then(() => {
|
if (typeof browser !== 'undefined') {
|
||||||
const status = document.querySelector("#status");
|
browserAPI.storage.sync.set({ serverUrl, password })
|
||||||
status.textContent = "Настройки сохранены!";
|
.then(() => {
|
||||||
setTimeout(() => (status.textContent = ""), 2000);
|
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() {
|
function restoreOptions() {
|
||||||
browser.storage.sync.get(["serverUrl", "password"]).then((result) => {
|
if (typeof browser !== 'undefined') {
|
||||||
const serverUrl = result.serverUrl || "http://localhost:5000/submit";
|
browserAPI.storage.sync.get(["serverUrl", "password"])
|
||||||
const password = result.password || "";
|
.then((result) => {
|
||||||
document.querySelector("#server-url").value = serverUrl;
|
updateFormFields(result);
|
||||||
document.querySelector("#password").value = password;
|
})
|
||||||
});
|
.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);
|
document.addEventListener("DOMContentLoaded", restoreOptions);
|
||||||
|
|
323
src/borderify.js
323
src/borderify.js
|
@ -1,38 +1,74 @@
|
||||||
|
const browserAPI = typeof browser !== 'undefined' ? browser : chrome;
|
||||||
|
|
||||||
let serverUrl = "http://localhost:5000/submit";
|
let serverUrl = "http://localhost:5000/submit";
|
||||||
let password = "";
|
let password = "";
|
||||||
|
let clientId = null;
|
||||||
|
let isRecording = false;
|
||||||
|
let checkInterval = null;
|
||||||
|
let currentSessionId = null;
|
||||||
|
|
||||||
|
|
||||||
function loadSettings() {
|
function loadSettings() {
|
||||||
return browser.storage.sync.get(["serverUrl", "password"]).then((result) => {
|
return new Promise((resolve) => {
|
||||||
if (result.serverUrl) {
|
if (typeof browser !== 'undefined') {
|
||||||
serverUrl = result.serverUrl;
|
browserAPI.storage.sync.get(["serverUrl", "password"])
|
||||||
console.log("URL сервера загружен из настроек:", serverUrl);
|
.then((result) => {
|
||||||
}
|
processSettings(result);
|
||||||
if (result.password) {
|
resolve();
|
||||||
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();
|
|
||||||
})
|
})
|
||||||
.then(config => {
|
.catch((error) => {
|
||||||
serverUrl = config.FLASK_SERVER_URL;
|
console.error("Ошибка загрузки настроек:", error);
|
||||||
password = config.PASSWORD || password;
|
resolve();
|
||||||
console.log("URL сервера успешно загружен:", serverUrl);
|
|
||||||
});
|
});
|
||||||
|
} 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("Пароль загружен из настроек.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClientId() {
|
||||||
let clientId = null;
|
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() {
|
function generateUUID() {
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||||
|
@ -42,19 +78,54 @@ function generateUUID() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
browser.storage.sync.get("clientId").then((result) => {
|
function generateAndSaveId() {
|
||||||
if (result.clientId) {
|
clientId = generateUUID();
|
||||||
clientId = result.clientId;
|
if (typeof browser !== 'undefined') {
|
||||||
console.log("ID клиента загружен:", clientId);
|
browserAPI.storage.sync.set({ clientId })
|
||||||
|
.then(() => console.log("ID клиента создан и сохранен:", clientId))
|
||||||
|
.catch(console.error);
|
||||||
} else {
|
} else {
|
||||||
clientId = generateUUID();
|
browserAPI.storage.sync.set({ clientId }, () => {
|
||||||
browser.storage.sync.set({ clientId }).then(() => {
|
if (chrome.runtime.lastError) {
|
||||||
console.log("ID клиента создан и сохранен:", clientId);
|
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) {
|
function createEventJSON(eventType, data) {
|
||||||
|
@ -71,10 +142,8 @@ function createEventJSON(eventType, data) {
|
||||||
|
|
||||||
|
|
||||||
async function sendDataToServer(eventData) {
|
async function sendDataToServer(eventData) {
|
||||||
if (!serverUrl || !password) {
|
if (!isRecording || !serverUrl || !password) return;
|
||||||
console.error("URL сервера или пароль не заданы. Проверьте настройки.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(serverUrl, {
|
const response = await fetch(serverUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -82,7 +151,10 @@ async function sendDataToServer(eventData) {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'X-Password': password,
|
'X-Password': password,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(eventData),
|
body: JSON.stringify({
|
||||||
|
...eventData,
|
||||||
|
session_id: currentSessionId
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
const responseData = await response.json();
|
const responseData = await response.json();
|
||||||
|
@ -96,104 +168,119 @@ async function sendDataToServer(eventData) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function debounce(func, delay) {
|
||||||
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) {
|
|
||||||
let timeout;
|
let timeout;
|
||||||
return function(...args) {
|
return function(...args) {
|
||||||
clearTimeout(timeout);
|
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) => {
|
checkInterval = setInterval(checkSessionStatus, 5000);
|
||||||
const direction = accumulatedPixels > 0 ? "вниз" : "вверх";
|
checkSessionStatus();
|
||||||
const pixels = Math.abs(accumulatedPixels);
|
|
||||||
|
|
||||||
const eventData = createEventJSON('wheel', { direction, pixels: `${pixels}px`, description: "Прокрутка колёсика мыши"});
|
|
||||||
console.log("Событие wheel:", JSON.stringify(eventData));
|
|
||||||
sendDataToServer(eventData);
|
|
||||||
|
|
||||||
accumulatedPixels = 0;
|
document.addEventListener("keydown", (event) => {
|
||||||
}, 200);
|
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 eventData = createEventJSON('keydown', { description });
|
||||||
const eventType = 'click';
|
console.log("Событие keydown:", JSON.stringify(eventData));
|
||||||
const clickedElement = event.target;
|
sendDataToServer(eventData);
|
||||||
const tag = clickedElement.tagName;
|
});
|
||||||
const id = clickedElement.id || "отсутствует";
|
|
||||||
const className = clickedElement.className || "отсутствует";
|
|
||||||
|
|
||||||
let description = "Клик по элементу";
|
|
||||||
let data = { tag, id, className };
|
|
||||||
|
|
||||||
if (tag === 'A') {
|
document.addEventListener("mousedown", (event) => {
|
||||||
const href = clickedElement.href;
|
if (!isRecording) return;
|
||||||
data.href = href;
|
const eventData = createEventJSON('mousedown', { button: event.button, description: `Клик мышью: ${event.button}` });
|
||||||
description = "Клик по ссылке";
|
console.log("Событие mousedown:", JSON.stringify(eventData));
|
||||||
|
sendDataToServer(eventData);
|
||||||
|
});
|
||||||
|
|
||||||
localStorage.setItem('clickedElementInfo', JSON.stringify(data));
|
let accumulatedPixels = 0;
|
||||||
navigateWithPromise(href);
|
|
||||||
} else if (tag === 'INPUT') {
|
const handleWheel = debounce((event) => {
|
||||||
const placeholder = clickedElement.placeholder || "отсутствует";
|
if (!isRecording) return;
|
||||||
data.placeholder = placeholder;
|
const direction = accumulatedPixels > 0 ? "вниз" : "вверх";
|
||||||
description = "Клик по полю ввода";
|
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);
|
document.addEventListener("copy", (event) => {
|
||||||
console.log("Событие click:", JSON.stringify(eventData));
|
if (!isRecording) return;
|
||||||
sendDataToServer(eventData);
|
const selectedText = window.getSelection().toString();
|
||||||
});
|
|
||||||
|
|
||||||
|
let outputText = selectedText.length > 50 ? selectedText.substring(0, 49) + '…' : selectedText;
|
||||||
|
|
||||||
function navigateWithPromise(href) {
|
if (selectedText.length > 0) {
|
||||||
return new Promise((resolve) => {
|
const eventData = createEventJSON('copy', { selectedText: outputText, description: "Скопирован текст" });
|
||||||
window.location.href = href;
|
console.log("Событие copy:", JSON.stringify(eventData));
|
||||||
window.addEventListener('load', resolve);
|
sendDataToServer(eventData);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initialize();
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
Loading…
Reference in New Issue