UNPKG

hot-content-mcp

Version:

MCP服务器,支持获取百度热搜、B站热门视频等多平台热门内容数据

282 lines 9.5 kB
import axios from 'axios'; import { ConfigManager } from './config.js'; export class BaiduHotSearchService { configManager; baseUrl = 'https://cn.apihz.cn/api/xinwen/baidu.php'; lastFetchTime = 0; cacheData = null; CACHE_DURATION = 5 * 60 * 1000; // 5分钟缓存 constructor(configManager) { this.configManager = configManager || new ConfigManager(); } /** * 获取百度热搜榜数据 */ async getHotSearchData(useCache = true) { // 检查缓存 if (useCache && this.isCacheValid()) { console.log('📋 使用缓存数据'); return this.cacheData; } try { console.log('🌐 开始获取百度热搜榜数据...'); // 获取API配置 const apiConfig = this.configManager.getBaiduConfig(); // 构建请求URL const url = this.buildApiUrl(apiConfig); // 发送请求 const response = await this.makeApiRequest(url); // 验证响应 this.validateResponse(response.data); // 转换数据格式 const simplifiedData = this.transformData(response.data); // 更新缓存 this.updateCache(simplifiedData); console.log(`✅ 成功获取 ${simplifiedData.length} 条热搜数据`); return simplifiedData; } catch (error) { console.error('❌ 获取热搜数据失败:', error); throw this.handleError(error); } } /** * 构建API请求URL */ buildApiUrl(config) { const params = new URLSearchParams({ id: config.id, key: config.key }); return `${this.baseUrl}?${params.toString()}`; } /** * 发送API请求 */ async makeApiRequest(url) { const response = await axios.get(url, { timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } }); return response; } /** * 验证API响应 */ validateResponse(response) { if (!response) { throw new Error('API响应为空'); } if (response.code !== 200) { throw new Error(`API返回错误状态码: ${response.code}`); } if (!response.data || !Array.isArray(response.data)) { throw new Error('API响应数据格式错误:data字段无效'); } if (response.data.length === 0) { throw new Error('API返回的热搜数据为空'); } } /** * 转换数据格式为简化版本 */ transformData(response) { return response.data.map((item, index) => ({ rank: item.index + 1, // 排名从1开始 title: item.word || item.query || '未知标题', hotScore: item.hotScore || '0', trend: this.getTrendText(item.hotChange), url: item.url || item.rawUrl || '', description: item.desc || undefined })).sort((a, b) => a.rank - b.rank); // 确保按排名排序 } /** * 获取趋势文本描述 */ getTrendText(hotChange) { const trendMap = { 'up': '↗️ 上升', 'down': '↘️ 下降', 'same': '➡️ 持平', 'new': '🆕 新上榜', 'hot': '🔥 热门' }; return trendMap[hotChange] || '➡️ 无变化'; } /** * 检查缓存是否有效 */ isCacheValid() { return this.cacheData !== null && (Date.now() - this.lastFetchTime) < this.CACHE_DURATION; } /** * 更新缓存 */ updateCache(data) { this.cacheData = data; this.lastFetchTime = Date.now(); } /** * 错误处理 */ handleError(error) { if (axios.isAxiosError(error)) { if (error.response) { return new Error(`API请求失败: ${error.response.status} - ${error.response.statusText}`); } else if (error.request) { return new Error('网络请求超时或无响应'); } } if (error instanceof Error) { return error; } return new Error('未知错误'); } /** * 搜索特定关键词的热搜 */ async searchHotSearch(keyword) { const allData = await this.getHotSearchData(); return allData.filter(item => item.title.toLowerCase().includes(keyword.toLowerCase())); } /** * 获取排名前N的热搜 */ async getTopHotSearch(count = 10) { const allData = await this.getHotSearchData(); return allData.slice(0, Math.min(count, allData.length)); } /** * 清除缓存 */ clearCache() { this.cacheData = null; this.lastFetchTime = 0; console.log('🗑️ 缓存已清除'); } } export class BilibiliHotSearchService { configManager; baseUrl = 'https://cn.apihz.cn/api/bang/bilibili1.php'; lastFetchTime = 0; cacheData = null; CACHE_DURATION = 5 * 60 * 1000; constructor(configManager) { this.configManager = configManager || new ConfigManager(); } async getBilibiliHotData(useCache = true) { if (useCache && this.isCacheValid()) { console.log('📋 使用B站缓存数据'); return this.cacheData; } if (!this.configManager.hasBilibiliConfig()) { throw new Error('未配置API,请检查config.json配置文件'); } try { console.log('🌐 开始获取B站热门视频数据...'); const apiConfig = this.configManager.getApiConfig(); const url = this.buildApiUrl(apiConfig); const response = await this.makeApiRequest(url); this.validateResponse(response.data); const simplifiedData = this.transformData(response.data); this.updateCache(simplifiedData); console.log(`✅ 成功获取 ${simplifiedData.length} 条B站视频数据`); return simplifiedData; } catch (error) { console.error('❌ 获取B站数据失败:', error); throw this.handleError(error); } } buildApiUrl(config) { const params = new URLSearchParams({ id: config.id, key: config.key }); return `${this.baseUrl}?${params.toString()}`; } async makeApiRequest(url) { const response = await axios.get(url, { timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } }); return response; } validateResponse(response) { if (!response) { throw new Error('B站API响应为空'); } if (response.code !== 200) { throw new Error(`B站API返回错误状态码: ${response.code}`); } if (!response.data || !Array.isArray(response.data)) { throw new Error('B站API响应数据格式错误:data字段无效'); } if (response.data.length === 0) { throw new Error('B站API返回的视频数据为空'); } } transformData(response) { return response.data.map((item, index) => ({ rank: index + 1, title: item.title || '未知标题', author: item.name || '未知UP主', views: item.view || item.vv || 0, likes: item.like || 0, coins: item.coin || 0, url: item.url || '', bvid: item.bvid || '', description: item.desc || undefined, publishLocation: item.publocation || undefined, stats: { danmaku: item.danmaku || 0, reply: item.reply || 0, favorite: item.favorite || 0, share: item.share || 0 } })).sort((a, b) => a.rank - b.rank); } isCacheValid() { return this.cacheData !== null && (Date.now() - this.lastFetchTime) < this.CACHE_DURATION; } updateCache(data) { this.cacheData = data; this.lastFetchTime = Date.now(); } handleError(error) { if (axios.isAxiosError(error)) { if (error.response) { return new Error(`B站API请求失败: ${error.response.status} - ${error.response.statusText}`); } else if (error.request) { return new Error('B站API网络请求超时或无响应'); } } if (error instanceof Error) { return error; } return new Error('B站API未知错误'); } async getTopBilibiliVideos(count = 10) { const allData = await this.getBilibiliHotData(); return allData.slice(0, Math.min(count, allData.length)); } async searchBilibiliVideos(keyword) { const allData = await this.getBilibiliHotData(); return allData.filter(item => item.title.toLowerCase().includes(keyword.toLowerCase()) || item.author.toLowerCase().includes(keyword.toLowerCase())); } clearCache() { this.cacheData = null; this.lastFetchTime = 0; console.log('🗑️ B站缓存已清除'); } } //# sourceMappingURL=api.js.map