Files
cobble/src/functions/backend/db/cron-jobs/grab-cron-jobs.ts
T
tben 5beb5f3de4 Let a conversation's menu show the routines that run in it
The header menu had Tasks but nothing for the schedules whose results
land in that conversation. It now has Schedules, right under Tasks,
opened in a modal that lists only what runs there: a bot's own chat holds
the routines that run nowhere, a workspace only that bot's routines in
it (`routineScopeSql`, tested against SQLite).

The list loads when the modal opens, through
`api/admin/bots/list-routines`, which checks the user chats in that
conversation first, so the page props stay small. `grabCronJobs` gained
an optional `workspace_id`: left out it still means everywhere.

The routines page's row, form and edit modal moved to
`src/components/general/`, and its subtitle to `src/utils/`, since both
the page and the modal use them.
2026-10-23 07:23:15 +01:00

70 lines
2.2 KiB
TypeScript

import BunSQLite from "@moduletrace/bun-sqlite";
import type { CronJobWithNames } from "@/src/types";
import botFaceSql from "@/src/utils/bot-face-sql";
import routineScopeSql from "@/src/utils/routine-scope/routine-scope-sql";
type Params = {
user_id: number | string;
/**
* Only this bot's.
*/
bot_id?: number | string;
/**
* With `bot_id`: only the routines of that conversation, here
* (`routineScopeSql`). `null` is the bot's own chat, so it has to be
* given rather than left out to mean "everywhere".
*/
workspace_id?: number | string | null;
/**
* Only this one.
*/
cron_job_id?: number | string;
};
/**
* The user's schedules with their bot (its face as the user sees it) and
* workspace names, the next to run first and paused ones last. Given a
* `bot_id` and a `workspace_id` (null for its own chat) it is one
* conversation's: the routines that run right there.
*/
export default async function grabCronJobs({
user_id,
bot_id,
workspace_id,
cron_job_id,
}: Params): Promise<CronJobWithNames[]> {
const face = botFaceSql({ user_id, prefix: "bot_" });
const where = [`c.user_id = ?`];
const values: (number | string)[] = [user_id];
if (bot_id) {
if (workspace_id === undefined) {
where.push(`c.bot_id = ?`);
values.push(bot_id);
} else {
const scope = routineScopeSql({ bot_id, workspace_id });
where.push(scope.sql);
values.push(...scope.values);
}
}
if (cron_job_id) {
where.push(`c.id = ?`);
values.push(cron_job_id);
}
const res = await BunSQLite.sql<CronJobWithNames>({
sql: `SELECT c.*, b.name AS bot_name, b.kind AS bot_kind, b.hue AS bot_hue, b.color AS bot_color, b.stone AS bot_stone, ${face.sql},
b.image_media_id AS bot_image_media_id, w.name AS workspace_name
FROM cron_jobs c
JOIN bots b ON b.id = c.bot_id
LEFT JOIN workspaces w ON w.id = c.workspace_id
WHERE ${where.join(" AND ")}
ORDER BY c.enabled DESC, c.next_run_at IS NULL, c.next_run_at ASC, c.id ASC`,
values: [...face.values, ...values],
});
return res.payload || [];
}