Add bots files handling logic
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
import deleteMedia from "@/src/functions/backend/db/media/delete-media";
|
||||
import purgeStorage, { grabStorageMediaIds } from "@/src/functions/backend/db/bot-files/purge-storage";
|
||||
import AppError from "@/src/utils/app-error";
|
||||
import grabUserBot from "./grab-user-bot";
|
||||
import assertCrewManager from "@/src/functions/backend/db/access/assert-crew-manager";
|
||||
@@ -27,6 +28,13 @@ export default async function deleteBot({ user_id, bot_id }: Params) {
|
||||
throw new AppError(`The coordinator can't be deleted.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read before the delete: the files' rows go with the bot.
|
||||
*/
|
||||
const file_media_ids = await grabStorageMediaIds({ scope: "bot", owner_id: bot.id! }).catch(
|
||||
() => [],
|
||||
);
|
||||
|
||||
const res = await BunSQLite.sql({ sql: `DELETE FROM bots WHERE id = ?`, values: [bot.id] });
|
||||
|
||||
if (!res.success) {
|
||||
@@ -37,5 +45,11 @@ export default async function deleteBot({ user_id, bot_id }: Params) {
|
||||
await deleteMedia({ id: bot.image_media_id, user_id: Number(user_id) }).catch(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Its folder goes with it: the bytes, their `media` rows and the search
|
||||
* vectors.
|
||||
*/
|
||||
await purgeStorage({ scope: "bot", owner_id: bot.id!, media_ids: file_media_ids });
|
||||
|
||||
return bot;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
import grabMessagesMedia from "./grab-messages-media";
|
||||
import grabMessagesReactions from "./grab-messages-reactions";
|
||||
import grabMessagesReplies from "./grab-messages-replies";
|
||||
import mediaToAttachment from "./media-to-attachment";
|
||||
import mediaToAttachment, { grabAttachmentFileIds } from "./media-to-attachment";
|
||||
|
||||
type Params = {
|
||||
session_id: number | string;
|
||||
@@ -61,6 +61,14 @@ export default async function grabBotMessages({
|
||||
: Promise.resolve(new Map()),
|
||||
]);
|
||||
|
||||
/**
|
||||
* Files that live in a bot's folder carry their `bot_files` id, so a
|
||||
* chip opens the file rather than only its bytes.
|
||||
*/
|
||||
const file_ids = await grabAttachmentFileIds({
|
||||
media_ids: [...media.values()].flat().map((file) => Number(file.id)),
|
||||
});
|
||||
|
||||
return messages.map((message) => {
|
||||
const id = Number(message.id);
|
||||
const files = media.get(id);
|
||||
@@ -69,7 +77,14 @@ export default async function grabBotMessages({
|
||||
return {
|
||||
...message,
|
||||
...(files
|
||||
? { attachments: files.map((file) => mediaToAttachment({ media: file })) }
|
||||
? {
|
||||
attachments: files.map((file) =>
|
||||
mediaToAttachment({
|
||||
media: file,
|
||||
file_id: file_ids.get(Number(file.id)),
|
||||
}),
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
...(reply_to ? { reply_to } : {}),
|
||||
...(user_id ? { reactions: reactions.get(id) || [] } : {}),
|
||||
|
||||
@@ -13,6 +13,11 @@ type Params = {
|
||||
workspace_id?: number | null;
|
||||
text: string;
|
||||
needs_you?: boolean;
|
||||
/**
|
||||
* Files the bot saved and sent with it (`media` ids): a PDF it made, a
|
||||
* generated image, a document it wrote.
|
||||
*/
|
||||
media_ids?: number[];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -25,6 +30,7 @@ export default async function insertBotMessage({
|
||||
workspace_id,
|
||||
text,
|
||||
needs_you,
|
||||
media_ids = [],
|
||||
}: Params): Promise<BUN_SQLITE_COBBLE_BOT_MESSAGES> {
|
||||
const now = Date.now();
|
||||
const needs_you_flag = needs_you ? 1 : 0;
|
||||
@@ -52,6 +58,13 @@ export default async function insertBotMessage({
|
||||
throw new Error(`Failed to save message: ${res.msg}`);
|
||||
}
|
||||
|
||||
for (const media_id of media_ids) {
|
||||
await BunSQLite.sql({
|
||||
sql: `INSERT INTO bot_message_media (created_at, updated_at, message_id, media_id) VALUES (?, ?, ?, ?)`,
|
||||
values: [now, now, id, media_id],
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
await BunSQLite.sql({
|
||||
sql: `UPDATE bot_sessions SET last_activity_at = ?, unread = 1 WHERE id = ?`,
|
||||
values: [now, session_id],
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
import type { BUN_SQLITE_COBBLE_MEDIA } from "@/db/types/db";
|
||||
import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
import type { BUN_SQLITE_COBBLE_BOT_FILES, BUN_SQLITE_COBBLE_MEDIA } from "@/db/types/db";
|
||||
import grabMediaURL from "@/src/functions/backend/media/grab-media-url";
|
||||
import type { BotMessageAttachment, MediaType } from "@/src/types";
|
||||
|
||||
type Params = {
|
||||
media: BUN_SQLITE_COBBLE_MEDIA;
|
||||
/**
|
||||
* The bot file it is, when the caller already knows (avoids a lookup
|
||||
* per attachment).
|
||||
*/
|
||||
file_id?: number | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* A `media` row as a chat attachment: its original name (`title`), type
|
||||
* and the private URLs it's served at.
|
||||
* and the private URLs it's served at. A file saved in a bot's folder
|
||||
* carries its `file_id`, so the chip can open the file's page.
|
||||
*/
|
||||
export default function mediaToAttachment({ media }: Params): BotMessageAttachment {
|
||||
export default function mediaToAttachment({ media, file_id }: Params): BotMessageAttachment {
|
||||
const { media_url, media_thumbnail_url } = grabMediaURL({ media: { ...media } });
|
||||
|
||||
return {
|
||||
@@ -19,5 +26,29 @@ export default function mediaToAttachment({ media }: Params): BotMessageAttachme
|
||||
media_type: (media.media_type || "document") as MediaType,
|
||||
url: media_url || "",
|
||||
thumbnail_url: media_thumbnail_url,
|
||||
file_id: file_id || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The `bot_files` rows behind a message's media, keyed by media id, so the
|
||||
* attachments can carry their file ids without a query each.
|
||||
*/
|
||||
export async function grabAttachmentFileIds({ media_ids }: { media_ids: number[] }) {
|
||||
const ids = media_ids.filter((id) => id > 0);
|
||||
|
||||
if (!ids.length) return new Map<number, number>();
|
||||
|
||||
const placeholders = ids.map(() => "?").join(", ");
|
||||
|
||||
const res = await BunSQLite.sql<BUN_SQLITE_COBBLE_BOT_FILES>({
|
||||
sql: `SELECT id, media_id FROM bot_files WHERE media_id IN (${placeholders})`,
|
||||
values: ids,
|
||||
});
|
||||
|
||||
return new Map(
|
||||
(res.payload || [])
|
||||
.filter((file) => file.media_id)
|
||||
.map((file) => [Number(file.media_id), Number(file.id)]),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user