import { readFile, writeFile, rename } from 'node:fs/promises'; import { randomUUID } from 'node:crypto'; import { publishReply } from './publisher.ts'; /** A separate exclusive claim prevents concurrent or ambiguous reply retries. */ export async function replyToPublished(recordPath: string, getToken: () => Promise, publish: typeof publishReply = publishReply) { const record = JSON.parse(await readFile(recordPath, 'utf8')); if (record.status !== 'published' || !record.postId || !record.imagesDeletedAt || !record.replyText) return; const replyPath = `${recordPath}.reply.json`; try { const existing = JSON.parse(await readFile(replyPath, 'utf8')); if (existing.status !== 'published') console.warn(`Reply outcome unknown; inspect ${replyPath} before retrying`); return; } catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; } const token = await getToken(); const replyId = await publish(token, record.postId, record.replyText, record.replyUrl, async containerId => { await writeFile(replyPath, JSON.stringify({ status: 'publishing', postId: record.postId, containerId }, null, 2), { flag: 'wx', mode: 0o600 }); }); const temporary = `${replyPath}.${randomUUID()}.tmp`; await writeFile(temporary, JSON.stringify({ status: 'published', postId: record.postId, replyId }, null, 2), { mode: 0o600 }); await rename(temporary, replyPath); console.log(`Threads reply published: ${replyId}`); }