show running process in process viewer and add limiting to the notification system
This commit is contained in:
@@ -33,6 +33,12 @@ type NotificationRateState = {
|
||||
lastWarned: number;
|
||||
};
|
||||
|
||||
type AntiSpamState = {
|
||||
hits: number;
|
||||
windowStart: number;
|
||||
blockedUntil: number;
|
||||
};
|
||||
|
||||
type PendingNotificationScreenshot = {
|
||||
notificationId: string;
|
||||
clientId: string;
|
||||
@@ -45,6 +51,9 @@ type NotificationConfigShape = {
|
||||
spamWindowMs?: number;
|
||||
spamWarnThreshold?: number;
|
||||
historyLimit?: number;
|
||||
antiSpamMaxHits?: number;
|
||||
antiSpamWindowMs?: number;
|
||||
antiSpamCooldownMs?: number;
|
||||
};
|
||||
|
||||
type CreateDeps = {
|
||||
@@ -88,6 +97,8 @@ function safeSendViewer(ws: ServerWebSocket<SocketData>, payload: unknown) {
|
||||
}
|
||||
|
||||
export function createNotificationPluginHandlers(deps: CreateDeps) {
|
||||
const antiSpamState = new Map<string, AntiSpamState>();
|
||||
|
||||
const pluginUIEventBuffer = new Map<string, Array<{ event: string; payload: any; ts: number }>>();
|
||||
const PLUGIN_UI_EVENT_BUFFER_MAX = 200;
|
||||
const PLUGIN_UI_EVENT_TTL_MS = 60_000;
|
||||
@@ -183,6 +194,43 @@ export function createNotificationPluginHandlers(deps: CreateDeps) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function checkAntiSpam(key: string, ts: number): boolean {
|
||||
const notificationConfig = deps.getNotificationConfig();
|
||||
const maxHits = Math.max(1, notificationConfig.antiSpamMaxHits || 15);
|
||||
const windowMs = Math.max(5000, notificationConfig.antiSpamWindowMs || 600000);
|
||||
const cooldownMs = Math.max(5000, notificationConfig.antiSpamCooldownMs || 600000);
|
||||
|
||||
const state = antiSpamState.get(key);
|
||||
|
||||
if (state) {
|
||||
if (ts < state.blockedUntil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ts - state.windowStart > windowMs) {
|
||||
state.windowStart = ts;
|
||||
state.hits = 1;
|
||||
state.blockedUntil = 0;
|
||||
antiSpamState.set(key, state);
|
||||
return true;
|
||||
}
|
||||
|
||||
state.hits += 1;
|
||||
if (state.hits > maxHits) {
|
||||
state.blockedUntil = ts + cooldownMs;
|
||||
logger.warn(`[notify] anti-spam: blocked keyword for ${key} until ${new Date(state.blockedUntil).toISOString()} (${state.hits} hits in ${windowMs}ms)`);
|
||||
antiSpamState.set(key, state);
|
||||
return false;
|
||||
}
|
||||
|
||||
antiSpamState.set(key, state);
|
||||
return true;
|
||||
}
|
||||
|
||||
antiSpamState.set(key, { hits: 1, windowStart: ts, blockedUntil: 0 });
|
||||
return true;
|
||||
}
|
||||
|
||||
function pruneNotificationRate() {
|
||||
const notificationConfig = deps.getNotificationConfig();
|
||||
const spamWindow = Math.max(5000, notificationConfig.spamWindowMs || 60000);
|
||||
@@ -193,6 +241,15 @@ export function createNotificationPluginHandlers(deps: CreateDeps) {
|
||||
deps.notificationRate.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
const antiSpamWindowMs = Math.max(5000, notificationConfig.antiSpamWindowMs || 600000);
|
||||
const antiSpamCooldownMs = Math.max(5000, notificationConfig.antiSpamCooldownMs || 600000);
|
||||
const antiSpamMaxAge = Math.max(antiSpamWindowMs, antiSpamCooldownMs) * 2;
|
||||
for (const [key, state] of antiSpamState) {
|
||||
if (now - state.windowStart > antiSpamMaxAge && now > state.blockedUntil) {
|
||||
antiSpamState.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(pruneNotificationRate, 60_000);
|
||||
@@ -264,6 +321,10 @@ export function createNotificationPluginHandlers(deps: CreateDeps) {
|
||||
if (!shouldAcceptNotification(rateKey, ts)) {
|
||||
return;
|
||||
}
|
||||
const antiSpamKey = `${clientId}:${keyword || title}`;
|
||||
if (!checkAntiSpam(antiSpamKey, ts)) {
|
||||
return;
|
||||
}
|
||||
const info = clientManager.getClient(clientId);
|
||||
const category: "active_window" | "clipboard" =
|
||||
payload.category === "clipboard" ? "clipboard" : "active_window";
|
||||
@@ -368,6 +429,11 @@ export function createNotificationPluginHandlers(deps: CreateDeps) {
|
||||
deps.notificationRate.delete(key);
|
||||
}
|
||||
}
|
||||
for (const key of Array.from(antiSpamState.keys())) {
|
||||
if (key === clientId || key.startsWith(`${clientId}:`)) {
|
||||
antiSpamState.delete(key);
|
||||
}
|
||||
}
|
||||
const item = { type: "notifications_cleared", clientId, ts: Date.now() };
|
||||
for (const session of sessionManager.getAllNotificationSessions().values()) {
|
||||
const sRole = session.userRole ?? session.viewer.data.userRole ?? "";
|
||||
|
||||
Reference in New Issue
Block a user