import { mkdirSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import type { InboxMessage, ChannelSnapshot } from "./types.ts"; import { readMessages } from "./transport.ts"; const MAX_NOTIFICATION_LEN = 4000; export function getNewMessagesForChannel( dataDir: string, inboxName: string, channel: string, lastTs: number, maxMessages?: number, ): ChannelSnapshot | null { const messages = readMessages(dataDir, inboxName, channel, lastTs); if (messages.length === 0) return null; let capped = messages; if (maxMessages !== undefined && messages.length > maxMessages) { capped = messages.slice(0, maxMessages); if (capped.length === 0) { return { channel, messages: [] }; } const boundaryTs = capped[capped.length - 1].ts; for (let i = maxMessages; i < messages.length && messages[i].ts === boundaryTs; i++) { capped.push(messages[i]); } } return { channel, messages: capped }; } export function getNewMessagesForSubscriptions( dataDir: string, inboxName: string, subscriptions: string[], cursor: Record, maxReplayMessages?: number, ): ChannelSnapshot[] { const snapshots: ChannelSnapshot[] = []; let remaining = maxReplayMessages; for (const channel of subscriptions) { const lastTs = cursor[channel]?.lastTs ?? 0; const limit = remaining !== undefined ? remaining : undefined; const snapshot = getNewMessagesForChannel(dataDir, inboxName, channel, lastTs, limit); if (snapshot) { snapshots.push(snapshot); if (remaining !== undefined) { remaining -= snapshot.messages.length; if (remaining <= 0) break; } } } return snapshots; } export function formatMessageTime(ts: number): string { const diffMs = Date.now() - ts; const diffSec = Math.floor(diffMs / 1000); const diffMin = Math.floor(diffSec / 60); const diffHour = Math.floor(diffMin / 60); const diffDay = Math.floor(diffHour / 24); if (diffSec < 60) return "just now"; if (diffMin < 60) return `${diffMin} min ago`; if (diffHour < 24) return `${diffHour}h ago`; return `${diffDay}d ago`; } export function formatInboxNotification(snapshots: ChannelSnapshot[], tmpBaseDir?: string): string | null { if (snapshots.length === 0) return null; const lines: string[] = ["## Inbox"]; for (let i = 0; i < snapshots.length; i++) { const snapshot = snapshots[i]; const count = snapshot.messages.length; lines.push(`### ${snapshot.channel} (${count} new message${count === 1 ? "" : "s"})`); for (const msg of snapshot.messages) { lines.push(`**${msg.from}** - ${formatMessageTime(msg.ts)}`); lines.push(""); lines.push(msg.content); } if (i < snapshots.length - 1) { lines.push(""); lines.push("---"); } } lines.push(""); lines.push("---"); lines.push(""); lines.push("Use inbox_post to respond."); const full = lines.join("\n"); if (full.length > MAX_NOTIFICATION_LEN && tmpBaseDir) { const path = join(tmpBaseDir, `inbox-${Date.now()}.md`); mkdirSync(tmpBaseDir, { recursive: true }); writeFileSync(path, full, "utf-8"); return `## Inbox\n\nFull notification written to: ${path}\n\nUse inbox_post to respond.`; } return full; }