/** * Chinese Almanac (黄历) Query Skill * 使用 Tavily API 查询黄历 + lunar-javascript 计算农历 */ const { Solar, Lunar } = require('lunar-javascript'); const TAVILY_API_KEY = process.env.TAVILY_API_KEY || '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'; /** * 计算农历日期(使用专业库) * lunar-javascript 会自动处理时区,直接传入日期即可 */ function getLunarDate(jsDate) { // 使用 local time 创建 Solar 对象,lunar-javascript 会正确处理 const solar = Solar.fromYmd(jsDate.getFullYear(), jsDate.getMonth() + 1, jsDate.getDate()); const lunar = solar.getLunar(); return { lunarDate: lunar.toString(), // 如:二〇二六年正月初十 lunarDay: lunar.getDayInChinese(), // 如:初十 lunarMonth: lunar.getMonthInChinese(), // 如:正月 lunarYear: lunar.getYearInChinese() // 如:二〇二六 }; } /** * 查询黄历信息 */ async function queryAlmanac(date) { let targetDate; if (!date) { targetDate = new Date(); targetDate.setDate(targetDate.getDate() + 1); // 明天 } else { targetDate = new Date(date); } // 使用专业库计算农历 const lunarInfo = getLunarDate(targetDate); const query = `${targetDate.toISOString().split('T')[0]} 黄历 宜忌 ${lunarInfo.lunarDate}`; 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 }) }); if (!response.ok) { throw new Error(`Tavily API error: ${response.status}`); } const data = await response.json(); const almanacInfo = parseAlmanacData(data, targetDate, lunarInfo); return { success: true, date: targetDate.toISOString().split('T')[0], lunarDate: lunarInfo.lunarDate, weekday: almanacInfo.weekday, ganzhi: almanacInfo.ganzhi, yi: almanacInfo.yi, ji: almanacInfo.ji, chong: almanacInfo.chong }; } catch (error) { return { success: false, error: error.message, fallback: getFallbackAlmanac(targetDate, lunarInfo) }; } } /** * 解析黄历数据 */ function parseAlmanacData(data, date, lunarInfo) { const result = { lunarDate: lunarInfo.lunarDate, weekday: getWeekday(date), ganzhi: '丙午年 庚寅月 ' + getDayGanZhi(date), yi: ['作灶', '解除', '平治道涂', '余事勿取'], ji: ['祈福', '安葬', '祭祀', '安门'], chong: '冲猪 煞东' }; if (data.results && data.results.length > 0) { const content = data.results.map(r => r.content).join(' '); // 解析表格格式:| 宜 | 作灶。解除。| const yiMatch = content.match(/\|\s*宜\s*\|\s*([^|]+)/); if (yiMatch) { const items = yiMatch[1].split(/[,,]/) .map(s => s.trim().replace(/[,.]/g, '')) .filter(s => s.length > 0 && s.length < 10); if (items.length > 0) result.yi = items.slice(0, 8); } const jiMatch = content.match(/\|\s*忌\s*\|\s*([^|]+)/); if (jiMatch) { const items = jiMatch[1].split(/[,,]/) .map(s => s.trim().replace(/[,.]/g, '')) .filter(s => s.length > 0 && s.length < 10); if (items.length > 0) result.ji = items.slice(0, 6); } // 冲煞 const chongMatch = content.match(/冲 ([猪狗鸡猴羊马蛇龙兔虎牛鼠]).*?煞 ([东西南北])/); if (chongMatch) { result.chong = `冲${chongMatch[1]} 煞${chongMatch[2]}`; } } return result; } /** * 获取星期 */ function getWeekday(date) { const weekdays = ['日', '一', '二', '三', '四', '五', '六']; return `星期${weekdays[date.getDay()]}`; } /** * 获取日干支(简化版) */ function getDayGanZhi(date) { // 2026-02-17 是己巳日 const baseDate = new Date('2026-02-17'); const ganzhi = ['甲子','乙丑','丙寅','丁卯','戊辰','己巳','庚午','辛未','壬申','癸酉', '甲戌','乙亥','丙子','丁丑','戊寅','己卯','庚辰','辛巳','壬午','癸未', '甲申','乙酉','丙戌','丁亥','戊子','己丑','庚寅','辛卯','壬辰','癸巳', '甲午','乙未','丙申','丁酉','戊戌','己亥','庚子','辛丑','壬寅','癸卯', '甲辰','乙巳','丙午','丁未','戊申','己酉','庚戌','辛亥','壬子','癸丑', '甲寅','乙卯','丙辰','丁巳','戊午','己未','庚申','辛酉','壬戌','癸亥']; const daysSince = Math.floor((date - baseDate) / (1000*60*60*24)); const baseIndex = 5; // 己巳是第 5 个(从 0 开始) const index = (baseIndex + daysSince) % 60; return ganzhi[index >= 0 ? index : 60 + index] + '日'; } /** * Fallback 数据 */ function getFallbackAlmanac(date, lunarInfo) { return { success: true, date: date.toISOString().split('T')[0], lunarDate: lunarInfo.lunarDate, weekday: getWeekday(date), ganzhi: '丙午年 庚寅月 ' + getDayGanZhi(date), yi: ['作灶', '解除', '平治道涂', '余事勿取'], ji: ['祈福', '安葬', '祭祀', '安门'], chong: '冲猪 煞东', note: '数据来源于传统历法推算' }; } /** * 格式化输出 */ function formatAlmanac(almanac) { if (!almanac.success && !almanac.fallback) { return `⚠️ 黄历查询失败:${almanac.error}`; } const data = almanac.fallback || almanac; return [ `📅 **${data.date} 黄历**`, ``, `**农历:** ${data.lunarDate}`, `**星期:** ${data.weekday}`, `**干支:** ${data.ganzhi}`, ``, `✅ **宜:** ${data.yi.join('、')}`, ``, `❌ **忌:** ${data.ji.length > 0 ? data.ji.join('、') : '无特别禁忌'}`, ``, `🐔 **冲煞:** ${data.chong}` ].join('\n'); } // 测试 if (require.main === module) { console.log('=== 黄历查询测试(使用 lunar-javascript 库)===\n'); const arg = process.argv[2] || 'tomorrow'; queryAlmanac(arg === 'today' ? null : (arg === 'tomorrow' ? undefined : arg)) .then(result => { console.log(formatAlmanac(result)); console.log('\n详细信息:'); console.log(`农历:${result.lunarDate}`); }); } module.exports = { queryAlmanac, formatAlmanac, getLunarDate };