Give every user their own bot face

A bot's face and status line live in bot_user_faces, one row per bot and
user, set by jobs for the user whose conversation they're in. Needs-you
and working only show to that user; every bot read overlays the viewer's
face (botFaceSql) and pushes go to the user a face change is for.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-19 13:58:26 +01:00
co-authored by Claude Opus 5.5
parent 47c58bc76b
commit ab25158d87
32 changed files with 448 additions and 181 deletions
+27 -15
View File
@@ -1,33 +1,45 @@
import BunSQLite from "@moduletrace/bun-sqlite";
import type { BUN_SQLITE_COBBLE_BOTS } from "@/db/types/db";
import type { BUN_SQLITE_COBBLE_BOT_USER_FACES, BUN_SQLITE_COBBLE_BOTS } from "@/db/types/db";
import pushBotUpdated from "@/src/bots-queue/push-bot-updated";
type Params = {
/**
* Unix ms: bots updated since then are pushed.
* Unix ms: bots and faces updated since then are pushed.
*/
since: number;
users: Set<string>;
};
/**
* Pushes `server:bot-updated` (face, status line, the user's own active
* task) for every bot that changed since `since` to the connected users
* who see it (`pushBotUpdated`). Pushes are idempotent, so overlapping
* windows only repeat them.
* Pushes `server:bot-updated` (the user's own face, status line and
* active task) for every face that changed since `since` to the connected
* user it's for, and for every bot that changed (e.g. its computer) to
* the connected users who see it (`pushBotUpdated`). Pushes are
* idempotent, so overlapping windows only repeat them.
*/
export default async function relayChangedBots({ since, users }: Params) {
if (!users.size) return;
const bots =
(
await BunSQLite.sql<BUN_SQLITE_COBBLE_BOTS>({
sql: `SELECT id, face, status_line, computer_id FROM bots WHERE updated_at > ?`,
values: [since],
})
).payload || [];
const [faces, bots] = await Promise.all([
BunSQLite.sql<BUN_SQLITE_COBBLE_BOT_USER_FACES>({
sql: `SELECT bot_id, user_id FROM bot_user_faces WHERE updated_at > ?`,
values: [since],
}),
BunSQLite.sql<BUN_SQLITE_COBBLE_BOTS>({
sql: `SELECT id, computer_id FROM bots WHERE updated_at > ?`,
values: [since],
}),
]);
for (const bot of bots) {
await pushBotUpdated({ bot, users });
for (const face of faces.payload || []) {
if (!face.bot_id || !face.user_id || !users.has(String(face.user_id))) continue;
await pushBotUpdated({ bot_id: face.bot_id, user_id: face.user_id });
}
for (const bot of bots.payload || []) {
if (!bot.id) continue;
await pushBotUpdated({ bot_id: bot.id, users, computer_id: bot.computer_id || null });
}
}