61 lines
1.8 KiB
TypeScript
61 lines
1.8 KiB
TypeScript
import { computePlacementRecencyScore } from "@/lib/scan-recency";
|
|
|
|
export interface RawPost {
|
|
externalId?: string;
|
|
text: string;
|
|
permalink?: string;
|
|
authorName?: string;
|
|
postedAt?: Date;
|
|
likeCount?: number;
|
|
replyCount?: number;
|
|
repostCount?: number;
|
|
}
|
|
|
|
export interface RankedPost extends RawPost {
|
|
score: number;
|
|
}
|
|
|
|
function recencyBoost(postedAt?: Date): number {
|
|
if (!postedAt) return 0.5;
|
|
const hours = (Date.now() - postedAt.getTime()) / (1000 * 60 * 60);
|
|
if (hours <= 6) return 1.5;
|
|
if (hours <= 24) return 1.2;
|
|
if (hours <= 72) return 1.0;
|
|
return 0.7;
|
|
}
|
|
|
|
export function computeScore(post: RawPost): number {
|
|
const likes = post.likeCount ?? 0;
|
|
const replies = post.replyCount ?? 0;
|
|
const reposts = post.repostCount ?? 0;
|
|
const engagement = likes * 1.0 + replies * 2.0 + reposts * 1.5;
|
|
return engagement * recencyBoost(post.postedAt);
|
|
}
|
|
|
|
/** 置入海巡排序:互動為輔,時間窗口為主 */
|
|
export function computePlacementScore(post: RawPost): number {
|
|
const likes = post.likeCount ?? 0;
|
|
const replies = post.replyCount ?? 0;
|
|
const reposts = post.repostCount ?? 0;
|
|
const engagement = likes * 0.6 + replies * 1.2 + reposts * 0.8;
|
|
const recency = computePlacementRecencyScore(post.postedAt);
|
|
return recency * 2.5 + Math.log10(engagement + 1) * 12;
|
|
}
|
|
|
|
function normalizeText(text: string): string {
|
|
return text.trim().toLowerCase().replace(/\s+/g, " ");
|
|
}
|
|
|
|
export function rankAndDedupe(posts: RawPost[], limit = 20): RankedPost[] {
|
|
const seen = new Set<string>();
|
|
const ranked: RankedPost[] = [];
|
|
|
|
for (const post of posts) {
|
|
const key = post.externalId ?? `${post.authorName ?? ""}:${normalizeText(post.text).slice(0, 120)}`;
|
|
if (seen.has(key)) continue;
|
|
seen.add(key);
|
|
ranked.push({ ...post, score: computeScore(post) });
|
|
}
|
|
|
|
return ranked.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
} |