53 lines
1.4 KiB
TypeScript
53 lines
1.4 KiB
TypeScript
import type { ActivityItem } from './types.js';
|
|
import type { ActivityLogRepository } from '../database/repositories.js';
|
|
|
|
const MAX_ITEMS = 200;
|
|
|
|
/**
|
|
* Bounded log of bot actions surfaced on the dashboard.
|
|
* Hydrates from SQLite on construction and persists every push,
|
|
* so the feed survives restarts.
|
|
*/
|
|
export class ActivityLog {
|
|
private items: ActivityItem[] = [];
|
|
|
|
constructor(private repo: ActivityLogRepository) {
|
|
const rows = repo.getRecent(MAX_ITEMS);
|
|
this.items = rows
|
|
.map((r) => ({
|
|
timestamp: r.timestamp,
|
|
type: r.type as ActivityItem['type'],
|
|
message: r.message,
|
|
recipient: r.recipient ?? undefined,
|
|
imageUrl: r.imageUrl ?? undefined,
|
|
postTitle: r.postTitle ?? undefined,
|
|
postUrl: r.postUrl ?? undefined,
|
|
}))
|
|
.reverse();
|
|
}
|
|
|
|
push(
|
|
type: ActivityItem['type'],
|
|
message: string,
|
|
recipient?: string,
|
|
imageUrl?: string,
|
|
origin?: { postTitle: string; postUrl: string },
|
|
): void {
|
|
const item: ActivityItem = { timestamp: Date.now(), type, message, recipient, imageUrl, ...origin };
|
|
this.items.push(item);
|
|
if (this.items.length > MAX_ITEMS) {
|
|
this.items = this.items.slice(-MAX_ITEMS);
|
|
}
|
|
this.repo.insert(item);
|
|
this.repo.prune(MAX_ITEMS);
|
|
}
|
|
|
|
recent(limit: number): ActivityItem[] {
|
|
return this.items.slice(-limit).reverse();
|
|
}
|
|
|
|
totalProcessed(): number {
|
|
return this.repo.countSentSkipped();
|
|
}
|
|
}
|