83 lines
3.1 KiB
TypeScript
83 lines
3.1 KiB
TypeScript
/** 置入模式:超過此天數的貼文不適合留言推薦(求助通常已解決) */
|
||
export const PLACEMENT_MAX_POST_AGE_DAYS = 30;
|
||
|
||
/** 置入黃金窗口:7 天內留言區仍活躍、需求多半未關閉 */
|
||
export const PLACEMENT_IDEAL_MAX_AGE_DAYS = 7;
|
||
|
||
/** 網搜 after: 篩選用,略寬於黃金窗口以補足結果 */
|
||
export const PLACEMENT_WEB_SEARCH_MAX_AGE_DAYS = 14;
|
||
|
||
/** 置入模式:Brave 網搜單次海巡最多查詢次數 */
|
||
export const PLACEMENT_WEB_SEARCH_MAX_QUERIES = 8;
|
||
|
||
/** 置入模式:Brave 網搜找到足夠貼文後即停止 */
|
||
export const PLACEMENT_WEB_SEARCH_TARGET_POSTS = 20;
|
||
|
||
/** 置入模式:Threads API/瀏覽器已找到足夠貼文時,跳過 Brave 網搜 */
|
||
export const PLACEMENT_WEB_SEARCH_SKIP_IF_POSTS = 15;
|
||
|
||
/** 一般模式:Brave 網搜單次海巡最多查詢次數 */
|
||
export const DEFAULT_WEB_SEARCH_MAX_QUERIES = 12;
|
||
|
||
export function postAgeDays(postedAt: Date, now = Date.now()): number {
|
||
return (now - postedAt.getTime()) / (1000 * 60 * 60 * 24);
|
||
}
|
||
|
||
export function postAgeHours(postedAt: Date, now = Date.now()): number {
|
||
return (now - postedAt.getTime()) / (1000 * 60 * 60);
|
||
}
|
||
|
||
export function isPostFreshEnough(
|
||
postedAt: Date | null | undefined,
|
||
maxAgeDays: number = PLACEMENT_MAX_POST_AGE_DAYS
|
||
): boolean {
|
||
if (!postedAt) return true;
|
||
return postAgeDays(postedAt) <= maxAgeDays;
|
||
}
|
||
|
||
/** 0–100:置入排序用,越近期分數越高 */
|
||
export function computePlacementRecencyScore(
|
||
postedAt: Date | null | undefined,
|
||
now = Date.now()
|
||
): number {
|
||
if (!postedAt) return 35;
|
||
const hours = postAgeHours(postedAt, now);
|
||
if (hours <= 6) return 100;
|
||
if (hours <= 24) return 95;
|
||
if (hours <= 48) return 88;
|
||
if (hours <= 72) return 80;
|
||
const days = postAgeDays(postedAt, now);
|
||
if (days <= PLACEMENT_IDEAL_MAX_AGE_DAYS) return 70;
|
||
if (days <= 14) return 50;
|
||
if (days <= PLACEMENT_MAX_POST_AGE_DAYS) return 30;
|
||
return 0;
|
||
}
|
||
|
||
export function formatGoogleAfterDate(maxAgeDays: number, now = Date.now()): string {
|
||
const date = new Date(now - maxAgeDays * 24 * 60 * 60 * 1000);
|
||
const y = date.getUTCFullYear();
|
||
const m = String(date.getUTCMonth() + 1).padStart(2, "0");
|
||
const d = String(date.getUTCDate()).padStart(2, "0");
|
||
return `${y}-${m}-${d}`;
|
||
}
|
||
|
||
export function formatPostAgeLabel(postedAt?: Date | null): string {
|
||
if (!postedAt) return "發文日期未知";
|
||
const days = Math.floor(postAgeDays(postedAt));
|
||
if (days <= 0) return "今天";
|
||
if (days === 1) return "昨天";
|
||
if (days < 7) return `${days} 天前`;
|
||
if (days < 30) return `約 ${Math.floor(days / 7)} 週前`;
|
||
return `${days} 天前`;
|
||
}
|
||
|
||
export function formatPlacementTimingHint(postedAt?: Date | null): string {
|
||
if (!postedAt) return "發文時間未知(置入窗口不明,需保守評估)";
|
||
const hours = postAgeHours(postedAt);
|
||
if (hours <= 24) return "剛發不久,置入窗口最佳";
|
||
if (hours <= 72) return "近 3 天內,置入窗口良好";
|
||
const days = Math.floor(postAgeDays(postedAt));
|
||
if (days <= PLACEMENT_IDEAL_MAX_AGE_DAYS) return "一週內,仍可置入";
|
||
if (days <= PLACEMENT_MAX_POST_AGE_DAYS) return "已超過一週,置入窗口收窄";
|
||
return "過舊,不建議置入";
|
||
} |