feat: add news source adapters and AI article analysis
- Implement adapters for NewsAPI, GNews, NewsData, Serper Web Search, and Brave Search. - Add unified fetchNews aggregator with URL deduplication. - Wire OpenAI-compatible Kimi client for bias classification, ranking, summarization, and topic extraction. - Add AI analysis prompts, response parsing, and fallback logic. - Build ranker combining AI score, source credibility, and recency. - Create admin Discover page and API for manual news discovery.
This commit is contained in:
42
src/lib/news/sources/websearch.ts
Normal file
42
src/lib/news/sources/websearch.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { FetchNewsOptions, RawArticle } from '../types';
|
||||
import { getActiveKey } from '@/lib/api-keys';
|
||||
|
||||
export async function fetchWebSearch(options: FetchNewsOptions): Promise<RawArticle[]> {
|
||||
const apiKey = await getActiveKey('WEBSEARCH');
|
||||
if (!apiKey) {
|
||||
console.warn('Web search key not configured');
|
||||
return [];
|
||||
}
|
||||
|
||||
const res = await fetch('https://google.serper.dev/search', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-API-KEY': apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
q: options.query,
|
||||
num: Math.min(options.maxResults || 10, 20),
|
||||
tbs: options.from ? `cdr:1,cd_min:${options.from.toISOString().split('T')[0]}` : undefined,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text();
|
||||
throw new Error(`Web search error ${res.status}: ${text}`);
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
return (data.news || data.organic || []).map((item: Record<string, unknown>) => ({
|
||||
title: String(item.title || ''),
|
||||
url: String(item.link || item.url || ''),
|
||||
sourceName: String(item.source || 'Web Search'),
|
||||
sourceApiCode: 'websearch',
|
||||
publishedAt: item.date ? new Date(String(item.date)) : undefined,
|
||||
description: item.snippet ? String(item.snippet) : undefined,
|
||||
content: undefined,
|
||||
imageUrl: item.imageUrl ? String(item.imageUrl) : undefined,
|
||||
raw: item,
|
||||
}));
|
||||
}
|
||||
Reference in New Issue
Block a user