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:
@@ -0,0 +1,38 @@
|
||||
import { Database } from "bun:sqlite";
|
||||
import { beforeAll, describe, expect, test } from "bun:test";
|
||||
import botFaceSql from "./bot-face-sql";
|
||||
|
||||
const db = new Database(":memory:");
|
||||
|
||||
beforeAll(() => {
|
||||
db.run(`CREATE TABLE bots (id INTEGER PRIMARY KEY)`);
|
||||
db.run(`CREATE TABLE bot_user_faces (bot_id INTEGER, user_id INTEGER, face TEXT, status_line TEXT)`);
|
||||
db.run(`INSERT INTO bots VALUES (1), (2)`);
|
||||
db.run(`INSERT INTO bot_user_faces VALUES (1, 7, 'needs-you', 'Waiting on you'), (2, 8, 'working', 'Working on a task')`);
|
||||
});
|
||||
|
||||
function faces({ user_id, prefix }: { user_id: number; prefix?: string }) {
|
||||
const columns = botFaceSql({ user_id, prefix });
|
||||
|
||||
return db.query(`SELECT b.id, ${columns.sql} FROM bots b ORDER BY b.id`).all(...columns.values);
|
||||
}
|
||||
|
||||
describe("botFaceSql", () => {
|
||||
test("each user sees only their own face of a bot", () => {
|
||||
expect(faces({ user_id: 7 })).toEqual([
|
||||
{ id: 1, face: "needs-you", status_line: "Waiting on you" },
|
||||
{ id: 2, face: "idle", status_line: "" },
|
||||
]);
|
||||
expect(faces({ user_id: 8 })).toEqual([
|
||||
{ id: 1, face: "idle", status_line: "" },
|
||||
{ id: 2, face: "working", status_line: "Working on a task" },
|
||||
]);
|
||||
});
|
||||
|
||||
test("a user with no faces sees every bot idle, under the prefix", () => {
|
||||
expect(faces({ user_id: 9, prefix: "bot_" })).toEqual([
|
||||
{ id: 1, bot_face: "idle", bot_status_line: "" },
|
||||
{ id: 2, bot_face: "idle", bot_status_line: "" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { SqlCondition } from "./crew-access/types";
|
||||
|
||||
type Params = {
|
||||
user_id: number | string;
|
||||
/**
|
||||
* The bots table alias.
|
||||
*/
|
||||
alias?: string;
|
||||
/**
|
||||
* Put before the column names, e.g. `bot_` for `bot_face`.
|
||||
*/
|
||||
prefix?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Select columns for a bot's face and status line as one user sees them
|
||||
* (`bot_user_faces`): idle and no status line when the bot has none for
|
||||
* them. Faces are per user, so a job one user's waiting on never shows
|
||||
* on anyone else's screen.
|
||||
*/
|
||||
export default function botFaceSql({ user_id, alias = "b", prefix = "" }: Params): SqlCondition {
|
||||
const column = (name: string) =>
|
||||
`(SELECT f.${name} FROM bot_user_faces f WHERE f.bot_id = ${alias}.id AND f.user_id = ?)`;
|
||||
|
||||
return {
|
||||
sql: `COALESCE(${column("face")}, 'idle') AS ${prefix}face, COALESCE(${column("status_line")}, '') AS ${prefix}status_line`,
|
||||
values: [Number(user_id), Number(user_id)],
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user