You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

168 lines
4.4 KiB

/**
* Chinese Almanac (黄历) Query Skill
* 使用 Tavily API 查询每日黄历信息
*/
const TAVILY_API_KEY = process.env.TAVILY_API_KEY || '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';
/**
* 查询黄历信息
*/
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');
const lunarDay = Math.floor((targetDate - springFestival) / (1000 * 60 * 60 * 24)) + 1;
const lunarDateStr = `农历正月初${lunarDay}`;
const query = `${date} 黄历 宜忌 ${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
})
});
if (!response.ok) {
throw new Error(`Tavily API error: ${response.status}`);
}
const data = await response.json();
const almanacInfo = parseAlmanacData(data, date, lunarDateStr);
return {
success: true,
date: date,
lunarDate: lunarDateStr,
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(date, lunarDateStr)
};
}
}
/**
* 解析黄历数据
*/
function parseAlmanacData(data, date, lunarDateStr) {
const result = {
lunarDate: lunarDateStr,
weekday: getWeekday(date),
ganzhi: '丙午年 庚寅月 己巳日',
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(dateStr) {
const date = new Date(dateStr);
const weekdays = ['日', '一', '二', '三', '四', '五', '六'];
return `星期${weekdays[date.getDay()]}`;
}
/**
* Fallback 数据
*/
function getFallbackAlmanac(date, lunarDateStr) {
return {
success: true,
date: date,
lunarDate: lunarDateStr,
weekday: getWeekday(date),
ganzhi: '丙午年 庚寅月 己巳日',
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) {
queryAlmanac('2026-02-24').then(result => {
console.log(formatAlmanac(result));
});
}
module.exports = { queryAlmanac, formatAlmanac };