import "server-only"; import { analyzeStyle8D, evaluateBenchmarkEngagement, serialize8DPersona } from "@/lib/ai/analyze-style-8d"; import { parseProviderApiKeys } from "@/lib/ai/keys"; import { prisma } from "@/lib/db"; import { setTaskStatus } from "@/lib/jobs/progress"; import { updateJobProgress } from "@/lib/jobs/progress-server"; import type { JobProgressDetail } from "@/lib/jobs/types"; import { ensureActiveSession, getProfilePosts } from "@/lib/threads-browser"; import { consumeCrawlerPageQuota } from "@/lib/threads-browser/safety"; import { getOrCreateSettings } from "@/lib/user-settings"; import { assertJobNotCancelled } from "@/lib/jobs/cancel"; export async function runStyle8DAnalysis(accountId: string, benchmarkUsername: string, jobId: string) { const username = benchmarkUsername.replace(/^@/, "").trim(); const detail: JobProgressDetail = { summary: "確認 Threads 連線…", phase: "session", tasks: [ { id: "session", label: "確認 Extension 連線", status: "running", startedAt: Date.now() }, { id: "samples", label: "抓取近期貼文", status: "pending" }, { id: "style", label: "AI 8D 分析", status: "pending" }, { id: "store", label: "儲存帳號策略", status: "pending" }, ], }; await updateJobProgress(jobId, detail); await assertJobNotCancelled(jobId); const session = await ensureActiveSession(); consumeCrawlerPageQuota(session.id); setTaskStatus(detail, "session", { status: "done" }); setTaskStatus(detail, "samples", { status: "running", startedAt: Date.now() }); detail.phase = "samples"; detail.summary = `正在抓取 @${username} 的近期貼文…`; await updateJobProgress(jobId, detail); const posts = await getProfilePosts(session.storageState, username, 12); await assertJobNotCancelled(jobId); if (posts.length < 4) throw new Error("可讀取的近期貼文不足 4 篇,無法做可靠的 8D 分析"); setTaskStatus(detail, "samples", { status: "done", found: posts.length }); setTaskStatus(detail, "style", { status: "running", startedAt: Date.now() }); detail.phase = "style"; detail.summary = `已取得 ${posts.length} 篇,AI 正在分析 D1–D8(通常 1~3 分鐘)…`; await updateJobProgress(jobId, detail); const settings = await getOrCreateSettings(); const analysis = await analyzeStyle8D({ username, posts, aiProvider: settings.researchAiProvider ?? settings.aiProvider, aiModel: settings.researchAiModel ?? settings.aiModel, apiKeys: parseProviderApiKeys(settings.providerApiKeys), }); await assertJobNotCancelled(jobId); setTaskStatus(detail, "style", { status: "done" }); setTaskStatus(detail, "store", { status: "running", startedAt: Date.now() }); detail.phase = "store"; detail.summary = "8D 分析完成,正在寫入帳號策略…"; await updateJobProgress(jobId, detail); const profile = { username, analyzedAt: new Date().toISOString(), postCount: posts.length, engagement: evaluateBenchmarkEngagement(posts), analysis, personaDraft: serialize8DPersona(analysis.personaDraft), }; await assertJobNotCancelled(jobId); await prisma.account.update({ where: { id: accountId }, data: { styleProfile: JSON.stringify(profile), styleBenchmark: username }, }); setTaskStatus(detail, "store", { status: "done" }); detail.summary = `完成 · @${username} · ${posts.length} 篇樣本 · 8D 策略已套用`; await updateJobProgress(jobId, detail); return profile; }