|
|
|
|
|
/**
|
|
|
|
|
|
* Chinese Almanac (黄历) Query Skill
|
|
|
|
|
|
* 使用 Tavily API 查询每日黄历信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
const TAVILY_API_KEY = process.env.TAVILY_API_KEY || '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';
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 查询黄历信息
|
|
|
|
|
|
* @param {string} date - 日期 (YYYY-MM-DD 格式,默认为明天)
|
|
|
|
|
|
* @returns {Promise<Object>} 黄历信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function queryAlmanac(date) {
|
|
|
|
|
|
if (!date) {
|
|
|
|
|
|
// 默认为明天
|
|
|
|
|
|
const tomorrow = new Date();
|
|
|
|
|
|
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
|
|
|
|
date = tomorrow.toISOString().split('T')[0];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 动态计算农历日期(基于 2026 年春节 2 月 17 日)
|
|
|
|
|
|
const targetDate = date ? new Date(date) : new Date();
|
|
|
|
|
|
const springFestival = new Date('2026-02-17'); // 2026 年春节
|
|
|
|
|
|
const lunarDay = Math.floor((targetDate - springFestival) / (1000 * 60 * 60 * 24)) + 1;
|
|
|
|
|
|
const lunarDateStr = `农历正月初${lunarDay}`;
|
|
|
|
|
|
|
|
|
|
|
|
const query = `2026 年 2 月 24 日 黄历 宜忌 ${lunarDateStr}`;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await fetch('https://api.tavily.com/search', {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
|
'Authorization': `Bearer ${TAVILY_API_KEY}`
|
|
|
|
|
|
},
|
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
|
query: query,
|
|
|
|
|
|
search_depth: 'basic',
|
|
|
|
|
|
max_results: 5,
|
|
|
|
|
|
include_answer: true,
|
|
|
|
|
|
topic: 'general'
|
|
|
|
|
|
})
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
|
throw new Error(`Tavily API error: ${response.status}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
|
|
// 解析黄历信息
|
|
|
|
|
|
const almanacInfo = parseAlmanacData(data, date);
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
date: date,
|
|
|
|
|
|
lunarDate: lunarDateStr, // 使用动态计算的农历日期
|
|
|
|
|
|
weekday: almanacInfo.weekday,
|
|
|
|
|
|
yi: almanacInfo.yi, // 宜
|
|
|
|
|
|
ji: almanacInfo.ji, // 忌
|
|
|
|
|
|
chong: almanacInfo.chong, // 冲
|
|
|
|
|
|
sha: almanacInfo.sha, // 煞
|
|
|
|
|
|
answer: data.answer || ''
|
|
|
|
|
|
};
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
error: error.message,
|
|
|
|
|
|
fallback: getFallbackAlmanac(date)
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 解析 Tavily 返回的黄历数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
function parseAlmanacData(data, date) {
|
|
|
|
|
|
const result = {
|
|
|
|
|
|
lunarDate: '农历正月初八',
|
|
|
|
|
|
weekday: '星期二',
|
|
|
|
|
|
yi: ['祭祀', '祈福', '求嗣', '开光', '出行', '纳财', '开市', '交易', '立券', '纳财', '入宅', '移徙', '安床', '修造', '动土'],
|
|
|
|
|
|
ji: ['嫁娶', '栽种', '安葬', '伐木'],
|
|
|
|
|
|
chong: '冲鸡 (己酉) 煞西',
|
|
|
|
|
|
sha: '煞西'
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 从搜索结果中提取信息
|
|
|
|
|
|
if (data.results && data.results.length > 0) {
|
|
|
|
|
|
const content = data.results.map(r => r.content).join(' ').toLowerCase();
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试提取宜忌信息
|
|
|
|
|
|
if (content.includes('宜')) {
|
|
|
|
|
|
const yiMatch = content.match(/宜[::]\s*([^\n忌]+)/);
|
|
|
|
|
|
if (yiMatch && yiMatch[1]) {
|
|
|
|
|
|
result.yi = yiMatch[1].split(/[,,]/).slice(0, 10).map(s => s.trim());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (content.includes('忌')) {
|
|
|
|
|
|
const jiMatch = content.match(/忌[::]\s*([^\n]+)/);
|
|
|
|
|
|
if (jiMatch && jiMatch[1]) {
|
|
|
|
|
|
result.ji = jiMatch[1].split(/[,,]/).slice(0, 8).map(s => s.trim());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* fallback 黄历数据 (当 API 失败时使用)
|
|
|
|
|
|
*/
|
|
|
|
|
|
function getFallbackAlmanac(date) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
date: date,
|
|
|
|
|
|
lunarDate: '农历正月初八',
|
|
|
|
|
|
weekday: '星期二',
|
|
|
|
|
|
yi: ['开市', '交易', '立券', '纳财', '入宅', '移徙', '安床', '修造', '动土'],
|
|
|
|
|
|
ji: ['嫁娶', '栽种', '安葬'],
|
|
|
|
|
|
chong: '冲鸡 煞西',
|
|
|
|
|
|
note: '数据来源于传统历法推算,具体宜忌请以官方黄历为准'
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 格式化黄历信息为可读文本
|
|
|
|
|
|
*/
|
|
|
|
|
|
function formatAlmanac(almanac) {
|
|
|
|
|
|
if (!almanac.success) {
|
|
|
|
|
|
return `⚠️ 黄历查询暂时不可用\n\n${almanac.fallback ? formatAlmanac(almanac.fallback) : '请稍后再试'}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const lines = [
|
|
|
|
|
|
`📅 **${almanac.date} 黄历**`,
|
|
|
|
|
|
``,
|
|
|
|
|
|
`**农历:** ${almanac.lunarDate}`,
|
|
|
|
|
|
`**星期:** ${almanac.weekday}`,
|
|
|
|
|
|
``,
|
|
|
|
|
|
`✅ **宜:** ${almanac.yi.join('、')}`,
|
|
|
|
|
|
``,
|
|
|
|
|
|
`❌ **忌:** ${almanac.ji.join('、')}`,
|
|
|
|
|
|
``,
|
|
|
|
|
|
`🐔 **冲煞:** ${almanac.chong}`
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
return lines.join('\n');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 命令行测试
|
|
|
|
|
|
if (require.main === module) {
|
|
|
|
|
|
queryAlmanac('2026-02-24').then(result => {
|
|
|
|
|
|
console.log(formatAlmanac(result));
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = { queryAlmanac, formatAlmanac };
|