feat(mom): backfill missed messages on startup using conversations.history API

- Add getLastTimestamp() to ChannelStore to read last ts from log.jsonl
- Add backfillChannel() to fetch up to 3 pages (3000 messages) per channel
- Add backfillAllChannels() called in start() before socket connection
- Include mom's own messages (as bot) and user messages, exclude other bots
- Process attachments from backfilled messages
- Add logging: logBackfillStart, logBackfillChannel, logBackfillComplete
- Warn if attachment missing name instead of failing

fixes #103
This commit is contained in:
Mario Zechner 2025-12-03 22:05:13 +01:00
parent 1517e64869
commit f02194296d
4 changed files with 164 additions and 3 deletions

View file

@ -1,4 +1,4 @@
import { existsSync, mkdirSync } from "fs";
import { existsSync, mkdirSync, readFileSync } from "fs";
import { appendFile, writeFile } from "fs/promises";
import { join } from "path";
import * as log from "./log.js";
@ -74,7 +74,7 @@ export class ChannelStore {
*/
processAttachments(
channelId: string,
files: Array<{ name: string; url_private_download?: string; url_private?: string }>,
files: Array<{ name?: string; url_private_download?: string; url_private?: string }>,
timestamp: string,
): Attachment[] {
const attachments: Attachment[] = [];
@ -82,6 +82,10 @@ export class ChannelStore {
for (const file of files) {
const url = file.url_private_download || file.url_private;
if (!url) continue;
if (!file.name) {
log.logWarning("Attachment missing name, skipping", url);
continue;
}
const filename = this.generateLocalFilename(file.name, timestamp);
const localPath = `${channelId}/attachments/${filename}`;
@ -139,6 +143,30 @@ export class ChannelStore {
});
}
/**
* Get the timestamp of the last logged message for a channel
* Returns null if no log exists
*/
getLastTimestamp(channelId: string): string | null {
const logPath = join(this.workingDir, channelId, "log.jsonl");
if (!existsSync(logPath)) {
return null;
}
try {
const content = readFileSync(logPath, "utf-8");
const lines = content.trim().split("\n");
if (lines.length === 0 || lines[0] === "") {
return null;
}
const lastLine = lines[lines.length - 1];
const message = JSON.parse(lastLine) as LoggedMessage;
return message.ts;
} catch {
return null;
}
}
/**
* Process the download queue in the background
*/