haixunMaster/lib/services/style-analysis.ts

79 lines
3.4 KiB
TypeScript
Raw Permalink Normal View History

2026-06-21 12:50:31 +00:00
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 正在分析 D1D8通常 13 分鐘)…`;
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;
}