chat history, better turn queue updates

This commit is contained in:
legop3
2026-01-15 02:55:46 -05:00
parent 23fe03ca54
commit 37927d82af
20 changed files with 585 additions and 36 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -11,7 +11,7 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
<title>Multi Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-CvfJ6hKE.js"></script>
<script type="module" crossorigin src="/assets/index-UA5S-yPp.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BY_dmM98.css">
</head>
<body>
+3
View File
@@ -3,6 +3,9 @@ const { httpServer } = require('./http');
const io = new SocketIOServer(httpServer, {
cors: { origin: '*' },
transports: ['websocket', 'polling'],
pingInterval: 5000,
pingTimeout: 7000,
});
// Allow more service listeners without warnings.
-2
View File
@@ -9,8 +9,6 @@ httpServer.on('upgrade', (req, socket, head) => {
roverWSS.handleUpgrade(req, socket, head, (ws) => {
roverWSS.emit('connection', ws, req);
});
} else {
socket.destroy();
}
});
+12
View File
@@ -12,6 +12,9 @@ const RATE_LIMIT_WINDOW_MS = 8000;
const RATE_LIMIT_MAX = 5;
const rateBuckets = new Map(); // socketId -> [timestamps]
const MAX_HISTORY = 100;
const history = [];
const PROFANITY_LIST = ['bitch', 'cunt', 'nigger', 'nigga', 'asshole', 'dick'];
const DUPLICATE_WINDOW_MS = 15000;
const lastMessageBySocket = new Map(); // socketId -> { text, ts }
@@ -83,7 +86,15 @@ function buildMessage(socket, text, meta = {}) {
};
}
function pushHistory(message) {
history.push(message);
if (history.length > MAX_HISTORY) {
history.shift();
}
}
function broadcastMessage(message) {
pushHistory(message);
publishEvent({ source: 'chat', type: 'chat:message', payload: message });
}
@@ -208,6 +219,7 @@ function sendExternalMessage({
}
io.on('connection', (socket) => {
socket.emit('chat:init', history);
socket.on('chat:send', (payload = {}, cb = () => {}) => handleIncoming(payload, socket, cb));
});
+4
View File
@@ -657,6 +657,10 @@ io.on('connection', (socket) => {
socket.on('subscribeAll', handleSubscribeAll);
socket.on('session:subscribeAll', handleSubscribeAll);
socket.on('disconnecting', () => {
logger.info('Socket disconnecting', socket.id);
removeSocket(socket);
});
socket.on('disconnect', () => {
logger.info('Socket disconnected', socket.id);
removeSocket(socket);
+30 -1
View File
@@ -9,6 +9,7 @@ const activeDrivers = new Map();
const TURN_DURATION_MS = 60 * 1000;
const IDLE_TIMEOUT_MS = 7 * 1000;
const MAX_IDLE_SKIPS = 3;
const STALE_REAPER_MS = 5000;
const turnEvents = new EventEmitter();
const turnDeadlines = new Map(); // roverId -> timestamp when current driver expires
const idleDeadlines = new Map(); // roverId -> timestamp when idle skip will happen
@@ -268,16 +269,44 @@ function removeDriverCompletely(roverId, socketId) {
function recordActivity(roverId, socketId) {
const queue = driverQueues.get(roverId);
if (!queue || queue.current !== socketId) return;
if (idleDisarmed.get(roverId)) return;
idleDisarmed.set(roverId, true);
const hadDeadline = idleDeadlines.has(roverId);
clearTimeout(idleTimers.get(roverId));
idleDeadlines.delete(roverId);
turnEvents.emit('queue', { roverId, reason: 'activity' });
if (hadDeadline) {
turnEvents.emit('queue', { roverId, reason: 'activity' });
}
}
modeEvents.on('change', (mode) => {
driverQueues.forEach((_, roverId) => syncState(roverId));
});
function reapStaleDrivers() {
const staleIds = new Set();
driverQueues.forEach((queue) => {
queue.queue.forEach((socketId) => {
if (!io.sockets.sockets.has(socketId)) {
staleIds.add(socketId);
}
});
if (queue.current && !io.sockets.sockets.has(queue.current)) {
staleIds.add(queue.current);
}
});
if (staleIds.size === 0) return;
driverQueues.forEach((queue, roverId) => {
staleIds.forEach((socketId) => {
if (queue.current === socketId || queue.queue.includes(socketId)) {
driverRemoved(roverId, socketId);
}
});
});
}
setInterval(reapStaleDrivers, STALE_REAPER_MS);
module.exports = {
driverAdded,
driverRemoved,