|
|
/** |
|
|
* 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 };
|
|
|
|