huge changes
This commit is contained in:
148
services/patternStore.js
Normal file
148
services/patternStore.js
Normal file
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* In-memory counter store with TTL windows for pattern detection.
|
||||
* Windows: 'today' resets at midnight, 'week' resets Monday 00:00, 'month' resets 1st 00:00.
|
||||
*/
|
||||
|
||||
// store[window][namespace][key] = count
|
||||
const store = {
|
||||
today: new Map(),
|
||||
week: new Map(),
|
||||
month: new Map()
|
||||
};
|
||||
|
||||
function getNamespaceMap(window, namespace) {
|
||||
const windowMap = store[window];
|
||||
if (!windowMap) return null;
|
||||
if (!windowMap.has(namespace)) windowMap.set(namespace, new Map());
|
||||
return windowMap.get(namespace);
|
||||
}
|
||||
|
||||
function increment(namespace, key, window) {
|
||||
const map = getNamespaceMap(window, namespace);
|
||||
if (!map) return;
|
||||
map.set(key, (map.get(key) || 0) + 1);
|
||||
}
|
||||
|
||||
function get(namespace, key, window) {
|
||||
const map = getNamespaceMap(window, namespace);
|
||||
if (!map) return 0;
|
||||
return map.get(key) || 0;
|
||||
}
|
||||
|
||||
function reset(namespace, window) {
|
||||
const windowMap = store[window];
|
||||
if (!windowMap) return;
|
||||
windowMap.delete(namespace);
|
||||
}
|
||||
|
||||
function getAll(namespace, window) {
|
||||
const map = getNamespaceMap(window, namespace);
|
||||
if (!map) return new Map();
|
||||
return new Map(map);
|
||||
}
|
||||
|
||||
// --- Scheduled resets ---
|
||||
|
||||
function msUntilNextMidnight() {
|
||||
const now = new Date();
|
||||
const next = new Date(now);
|
||||
next.setHours(24, 0, 0, 0);
|
||||
return next.getTime() - now.getTime();
|
||||
}
|
||||
|
||||
function msUntilNextMonday() {
|
||||
const now = new Date();
|
||||
const day = now.getDay(); // 0=Sun
|
||||
const daysUntilMonday = day === 0 ? 1 : (8 - day);
|
||||
const next = new Date(now);
|
||||
next.setDate(now.getDate() + daysUntilMonday);
|
||||
next.setHours(0, 0, 0, 0);
|
||||
return next.getTime() - now.getTime();
|
||||
}
|
||||
|
||||
function msUntilNextMonth() {
|
||||
const now = new Date();
|
||||
const next = new Date(now.getFullYear(), now.getMonth() + 1, 1, 0, 0, 0, 0);
|
||||
return next.getTime() - now.getTime();
|
||||
}
|
||||
|
||||
// Callbacks to run on daily reset (e.g. clear firedToday in patternChecker)
|
||||
const dailyResetCallbacks = [];
|
||||
|
||||
function onDailyReset(fn) {
|
||||
dailyResetCallbacks.push(fn);
|
||||
}
|
||||
|
||||
function scheduleDailyReset() {
|
||||
setTimeout(() => {
|
||||
store.today = new Map();
|
||||
for (const fn of dailyResetCallbacks) {
|
||||
try { fn(); } catch (_) {}
|
||||
}
|
||||
scheduleDailyReset();
|
||||
}, msUntilNextMidnight());
|
||||
}
|
||||
|
||||
function scheduleWeeklyReset() {
|
||||
setTimeout(() => {
|
||||
store.week = new Map();
|
||||
scheduleWeeklyReset();
|
||||
}, msUntilNextMonday());
|
||||
}
|
||||
|
||||
function scheduleMonthlyReset() {
|
||||
setTimeout(() => {
|
||||
store.month = new Map();
|
||||
scheduleMonthlyReset();
|
||||
}, msUntilNextMonth());
|
||||
}
|
||||
|
||||
function scheduleResets() {
|
||||
scheduleDailyReset();
|
||||
scheduleWeeklyReset();
|
||||
scheduleMonthlyReset();
|
||||
}
|
||||
|
||||
// --- Cooldown store ---
|
||||
const cooldowns = new Map();
|
||||
|
||||
function setCooldown(key) {
|
||||
cooldowns.set(key, Date.now());
|
||||
}
|
||||
|
||||
function isOnCooldown(key, cooldownMinutes) {
|
||||
const last = cooldowns.get(key);
|
||||
if (!last) return false;
|
||||
return (Date.now() - last) < cooldownMinutes * 60 * 1000;
|
||||
}
|
||||
|
||||
// --- Staff last-seen tracker (fallback for missing presence intent) ---
|
||||
const staffLastSeen = new Map();
|
||||
|
||||
function updateStaffLastSeen(staffId) {
|
||||
staffLastSeen.set(staffId, Date.now());
|
||||
}
|
||||
|
||||
function getStaffLastSeen(staffId) {
|
||||
return staffLastSeen.get(staffId) || null;
|
||||
}
|
||||
|
||||
function isStaffRecentlyActive(staffId, withinMinutes = 60) {
|
||||
const last = staffLastSeen.get(staffId);
|
||||
if (!last) return false;
|
||||
return (Date.now() - last) < withinMinutes * 60 * 1000;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
increment,
|
||||
get,
|
||||
reset,
|
||||
getAll,
|
||||
scheduleResets,
|
||||
onDailyReset,
|
||||
setCooldown,
|
||||
isOnCooldown,
|
||||
updateStaffLastSeen,
|
||||
getStaffLastSeen,
|
||||
isStaffRecentlyActive
|
||||
};
|
||||
Reference in New Issue
Block a user