Add media storage limits, retention, and the near-limit flow

This commit is contained in:
Fisto
2026-09-16 08:49:42 +02:00
parent 6b961ddcef
commit 8f3f153c55
15 changed files with 394 additions and 12 deletions
+19 -1
View File
@@ -123,6 +123,7 @@ export interface PuppetMedia {
channelId: string;
guildId: string | null;
mode: "links" | "download";
confirm?: boolean;
}): Promise<{ id: string }>;
stopScrape(id: string): void;
listScrapes(): PuppetScrape[];
@@ -174,6 +175,8 @@ export class Puppet {
private readonly lastFetchAt = new Map<string, number>();
private readonly pending = new Map<string, { entryId: string; expiresAt: number }>();
private readonly polls = new Map<string, PollState>();
/** Near-limit scrape warnings waiting for the same user to re-run the command. */
private readonly scrapeConfirm = new Map<string, number>();
private voiceChannelId?: string | null;
private ownerVoiceChannelId?: string | null;
@@ -753,11 +756,26 @@ export class Puppet {
return this.respond(authorId, channelId, "scrape", "Download mode needs the admin tier.");
}
const key = `${channelId}:${authorId}`;
const confirmUntil = this.scrapeConfirm.get(key);
const confirm = confirmUntil !== undefined && Date.now() < confirmUntil;
if (confirm) this.scrapeConfirm.delete(key);
try {
const scrape = await this.media.startScrape({ channelId, guildId: guildId ?? null, mode });
const scrape = await this.media.startScrape({ channelId, guildId: guildId ?? null, mode, confirm });
this.actionLog.append({ type: "media", action: "scrape", channelId, mode, scrapeId: scrape.id });
return this.respond(authorId, channelId, "scrape", `Media scrape started (${mode}): ${scrape.id}.`);
} catch (error) {
if ((error as { code?: string }).code === "MEDIA_LIMIT" && !confirm) {
this.scrapeConfirm.set(key, Date.now() + 120_000);
return this.respond(
authorId,
channelId,
"scrape",
`${(error as Error).message}. Run the command again within 2 minutes to accept.`
);
}
this.actionLog.append({ type: "error", scope: "media", channelId, message: String(error) });
return this.respond(
authorId,