hot-content-mcp
Version:
MCP服务器,支持获取百度热搜、B站热门视频等多平台热门内容数据
282 lines • 9.5 kB
JavaScript
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