Add clustered bot visitor filter

This commit is contained in:
rajchales-monito
2026-07-30 13:54:34 +02:00
parent d815fa6d0e
commit a0bef3baa3
3 changed files with 107 additions and 18 deletions
+9
View File
@@ -184,6 +184,7 @@
<legend>Signalizace navstevniku</legend>
<p class="ai-help">Zelena znamena aktivni. Po necinosti se prepne na oranzovou a po dalsim limitu na sedou.</p>
<label class="setting-switch full-row">Skryt boty <input id="hideBotsToggle" type="checkbox"></label>
<label class="setting-switch full-row">Hromadny bot filtr <input id="clusterBotFilterEnabled" type="checkbox"></label>
<div class="settings-top-row">
<label>Zelena do
<span class="seconds-field"><input id="presenceActiveSeconds" type="number" min="10" max="3600" step="5"><span>s</span></span>
@@ -192,6 +193,14 @@
<span class="seconds-field"><input id="presenceOfflineSeconds" type="number" min="20" max="7200" step="5"><span>s</span></span>
</label>
</div>
<div class="settings-top-row">
<label>Pocet navstev
<input id="clusterBotCount" type="number" min="3" max="500" step="1">
</label>
<label>Casove okno
<span class="seconds-field"><input id="clusterBotWindowSeconds" type="number" min="10" max="3600" step="10"><span>s</span></span>
</label>
</div>
<small id="presenceStatus" class="full-row">Oranzova je cas mezi zelenou a sedou.</small>
<button id="savePresenceSettings" class="full-row" type="button">Ulozit signalizaci</button>
</fieldset>
+36 -12
View File
@@ -74,6 +74,9 @@ $("#settingsButton").addEventListener("click", () => $("#settingsPanel").classLi
$("#closeSettingsButton").addEventListener("click", () => $("#settingsPanel").classList.add("hidden"));
$("#presenceActiveSeconds").addEventListener("input", markPresenceSettingsDirty);
$("#presenceOfflineSeconds").addEventListener("input", markPresenceSettingsDirty);
$("#clusterBotFilterEnabled").addEventListener("change", markPresenceSettingsDirty);
$("#clusterBotCount").addEventListener("input", markPresenceSettingsDirty);
$("#clusterBotWindowSeconds").addEventListener("input", markPresenceSettingsDirty);
$("#savePresenceSettings").addEventListener("click", savePresenceSettings);
$("#adminSoundToggle").addEventListener("click", () => {
setAdminSoundMuted(!state.soundMuted);
@@ -766,36 +769,57 @@ function renderPresenceSettings(settings) {
const normalized = normalizePresenceSettings(settings);
$("#presenceActiveSeconds").value = normalized.activeSeconds;
$("#presenceOfflineSeconds").value = normalized.offlineSeconds;
setPresenceStatus(`Oranzova: ${normalized.activeSeconds}-${normalized.offlineSeconds} s necinosti.`);
$("#clusterBotFilterEnabled").checked = normalized.clusterBotFilterEnabled;
$("#clusterBotCount").value = normalized.clusterBotCount;
$("#clusterBotWindowSeconds").value = normalized.clusterBotWindowSeconds;
setPresenceStatus(presenceStatusText(normalized, "Oranzova"));
}
function markPresenceSettingsDirty() {
const normalized = normalizePresenceSettings({
activeSeconds: $("#presenceActiveSeconds").value,
offlineSeconds: $("#presenceOfflineSeconds").value
});
setPresenceStatus(`Neulozeno. Oranzova bude ${normalized.activeSeconds}-${normalized.offlineSeconds} s.`);
const normalized = currentPresenceSettingsFromForm();
setPresenceStatus(presenceStatusText(normalized, "Neulozeno. Oranzova"));
}
async function savePresenceSettings() {
if (!state.site) return;
const normalized = normalizePresenceSettings({
activeSeconds: $("#presenceActiveSeconds").value,
offlineSeconds: $("#presenceOfflineSeconds").value
});
const normalized = currentPresenceSettingsFromForm();
setPresenceStatus("Ukladam signalizaci...");
const settings = structuredClone(state.site.settings);
settings.adminPresence = normalized;
await saveSite({ settings, isOnline: state.site.isOnline });
renderPresenceSettings(state.site.settings.adminPresence || normalized);
state.visitorRenderKey = "";
await Promise.all([loadConversations(), loadVisitors()]);
setPresenceStatus(`Ulozeno. Oranzova: ${normalized.activeSeconds}-${normalized.offlineSeconds} s.`);
setPresenceStatus(presenceStatusText(normalized, "Ulozeno. Oranzova"));
}
function normalizePresenceSettings(value = {}) {
const activeSeconds = clampNumber(value.activeSeconds, 10, 3600, 30);
const offlineSeconds = Math.max(clampNumber(value.offlineSeconds, 20, 7200, 120), activeSeconds + 10);
return { activeSeconds, offlineSeconds };
return {
activeSeconds,
offlineSeconds,
clusterBotFilterEnabled: value.clusterBotFilterEnabled !== false,
clusterBotCount: clampNumber(value.clusterBotCount, 3, 500, 20),
clusterBotWindowSeconds: clampNumber(value.clusterBotWindowSeconds, 10, 3600, 60)
};
}
function currentPresenceSettingsFromForm() {
return normalizePresenceSettings({
activeSeconds: $("#presenceActiveSeconds").value,
offlineSeconds: $("#presenceOfflineSeconds").value,
clusterBotFilterEnabled: $("#clusterBotFilterEnabled").checked,
clusterBotCount: $("#clusterBotCount").value,
clusterBotWindowSeconds: $("#clusterBotWindowSeconds").value
});
}
function presenceStatusText(settings, prefix) {
const cluster = settings.clusterBotFilterEnabled
? ` Hromadny filtr: ${settings.clusterBotCount} navstev / ${settings.clusterBotWindowSeconds} s.`
: " Hromadny filtr vypnuty.";
return `${prefix}: ${settings.activeSeconds}-${settings.offlineSeconds} s necinosti.${cluster}`;
}
function clampNumber(value, min, max, fallback) {