import { prisma, resolvePersona } from "@/lib/db"; import { getOrCreateSettings } from "@/lib/user-settings"; import { getActiveAccountProfile } from "@/lib/account-context"; import { analyzeViralPost } from "@/lib/ai/analyze-viral"; import { parseProviderApiKeys } from "@/lib/ai/keys"; import { replicateViralPost } from "@/lib/ai/replicate-viral"; import { ensureActiveSession } from "@/lib/threads-browser"; import { scrapePostMedia } from "@/lib/threads-browser/media"; import { parseMediaUrls, parseViralAnalysis } from "@/lib/types/viral"; export async function analyzeScanItemViral(scanItemId: string) { const item = await prisma.scanItem.findUnique({ where: { id: scanItemId }, include: { replies: { orderBy: { likeCount: "desc" } }, scan: { include: { topic: true } }, }, }); if (!item) throw new Error("找不到貼文"); const session = await ensureActiveSession(); let mediaUrls = parseMediaUrls(item.mediaUrls); let mediaType = item.mediaType; if (mediaUrls.length === 0 && item.permalink) { const media = await scrapePostMedia(session.storageState, item.permalink, session); mediaUrls = media.urls; mediaType = media.mediaType; await prisma.scanItem.update({ where: { id: item.id }, data: { mediaUrls: JSON.stringify(mediaUrls), mediaType: media.mediaType, }, }); } const settings = await getOrCreateSettings(); const account = await getActiveAccountProfile(); const apiKeys = parseProviderApiKeys(settings.providerApiKeys); const analysis = await analyzeViralPost({ postText: item.text, authorName: item.authorName, likeCount: item.likeCount, replyCount: item.replyCount, searchTag: item.searchTag, mediaUrls, mediaType, topicLabel: item.scan.topic.label, topicBrief: item.scan.topic.brief, persona: resolvePersona(settings, account), aiProvider: settings.aiProvider, aiModel: settings.aiModel, apiKeys, replies: item.replies.map((r) => ({ text: r.text, authorName: r.authorName, likeCount: r.likeCount, })), }); await prisma.scanItem.update({ where: { id: item.id }, data: { viralAnalysis: JSON.stringify(analysis) }, }); return { item: { ...item, mediaUrls: JSON.stringify(mediaUrls), mediaType }, analysis }; } export async function analyzeScanTopViral(scanId: string, limit = 5) { const items = await prisma.scanItem.findMany({ where: { scanId, OR: [{ qualityTier: null }, { qualityTier: { not: "EXCLUDE" } }], }, orderBy: [{ combinedScore: "desc" }, { score: "desc" }], take: limit, }); const results = []; for (const item of items) { const result = await analyzeScanItemViral(item.id); results.push(result); } return results; } export async function replicateScanItem(scanItemId: string) { const item = await prisma.scanItem.findUnique({ where: { id: scanItemId }, include: { scan: { include: { topic: true } } }, }); if (!item) throw new Error("找不到貼文"); const viralAnalysis = parseViralAnalysis(item.viralAnalysis); if (!viralAnalysis) throw new Error("爆款分析資料損毀,請重新分析後再試"); const settings = await getOrCreateSettings(); const account = await getActiveAccountProfile(); const apiKeys = parseProviderApiKeys(settings.providerApiKeys); const replica = await replicateViralPost({ originalText: item.text, authorName: item.authorName, viralAnalysis, topicLabel: item.scan.topic.label, topicBrief: item.scan.topic.brief, persona: resolvePersona(settings, account), aiProvider: settings.aiProvider, aiModel: settings.aiModel, apiKeys, }); const draft = await prisma.draft.create({ data: { accountId: item.scan.topic.accountId, topicId: item.scan.topicId, text: replica.text, angle: replica.angle, hook: replica.hook, rationale: `${replica.rationale}\n\n結構筆記:${replica.structureNotes}`, imageBrief: replica.imageBrief, draftType: "viral-replica", scanItemId: item.id, sources: item.permalink ? JSON.stringify([item.permalink]) : null, referenceNotes: viralAnalysis.keyTakeaways?.join("\n") ?? null, status: "PENDING", }, }); return { draft, replica }; }