haixunMaster/lib/search/providers/threads-provider.ts

76 lines
2.3 KiB
TypeScript

import { keywordSearchViaThreadsApi } from "@/lib/threads-api";
import type { ThreadsApiCredentials } from "@/lib/threads-api/types";
import { cacheGet, cacheSet } from "../cache";
import { getSearchConfig } from "../config";
import { logSearchEvent } from "../logger";
import type { SearchProvider, SearchRequest, SearchResponse, SearchResult } from "../types";
export class ThreadsApiSearchProvider implements SearchProvider {
constructor(private credentials: ThreadsApiCredentials | null) {}
name() {
return "threads" as const;
}
enabled(): boolean {
return getSearchConfig().threads.enabled && !!this.credentials?.accessToken;
}
async search(req: SearchRequest): Promise<SearchResponse> {
if (!this.enabled() || !this.credentials) {
logSearchEvent({
kind: "provider",
provider: "threads",
status: "unavailable",
keyword: req.keyword,
});
return { provider: "threads", results: [], status: "unavailable" };
}
const cfg = getSearchConfig();
const cacheKey = `search:threads:${req.keyword}:${req.limit}`;
const cached = cacheGet<SearchResult[]>("threads", cacheKey);
if (cached) {
return { provider: "threads", results: cached, status: "success", skipReason: "cache_hit" };
}
try {
const posts = await keywordSearchViaThreadsApi(this.credentials, {
query: req.keyword,
limit: req.limit,
searchType: "RECENT",
});
const results: SearchResult[] = posts.map((p) => ({
title: p.text.slice(0, 120),
url: p.permalink ?? "",
snippet: p.text,
author: p.authorName ?? "",
publishedAt: p.postedAt,
source: "threads" as const,
threadId: p.externalId,
raw: { likeCount: p.likeCount, replyCount: p.replyCount },
}));
cacheSet("threads", cacheKey, results, cfg.threadsCacheTtlMs);
logSearchEvent({
kind: "provider",
provider: "threads",
status: "success",
keyword: req.keyword,
count: results.length,
});
return { provider: "threads", results, status: "success" };
} catch {
logSearchEvent({
kind: "provider",
provider: "threads",
status: "unavailable",
keyword: req.keyword,
});
return { provider: "threads", results: [], status: "unavailable" };
}
}
}