|
|
/** |
|
|
* Daily Horoscope & Fortune Analysis |
|
|
* |
|
|
* 查询每日星座运势,结合黄历和八字分析 |
|
|
* 为桐哥 Agent 提供每日运势推送功能 |
|
|
*/ |
|
|
|
|
|
const https = require('https'); |
|
|
|
|
|
// 王院长生辰信息 |
|
|
const USER_BIRTH_INFO = { |
|
|
name: '王院长', |
|
|
birthday: '1984-05-16', |
|
|
birthTime: '23:00-24:00', // 子时 |
|
|
lunarBirthday: '甲子年 四月十六', |
|
|
zodiac: '鼠', |
|
|
westernZodiac: '金牛座', |
|
|
bazi: { |
|
|
year: '甲子', |
|
|
month: '己巳', |
|
|
day: '丙午', |
|
|
hour: '戊子', |
|
|
dayMaster: '丙火' // 日主 |
|
|
} |
|
|
}; |
|
|
|
|
|
// 十二星座 |
|
|
const ZODIAC_SIGNS = [ |
|
|
'白羊座', '金牛座', '双子座', '巨蟹座', |
|
|
'狮子座', '处女座', '天秤座', '天蝎座', |
|
|
'射手座', '摩羯座', '水瓶座', '双鱼座' |
|
|
]; |
|
|
|
|
|
/** |
|
|
* 使用 Tavily 搜索运势信息 |
|
|
*/ |
|
|
async function searchHoroscope(date, sign) { |
|
|
const tomorrow = new Date(date); |
|
|
tomorrow.setDate(tomorrow.getDate() + 1); |
|
|
const dateStr = tomorrow.toISOString().split('T')[0]; |
|
|
|
|
|
const query = `${dateStr} ${sign} 运势 星座运程`; |
|
|
|
|
|
// 调用 Tavily API |
|
|
const tavilyApiKey = process.env.TAVILY_API_KEY || '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'; |
|
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
const postData = JSON.stringify({ |
|
|
query: query, |
|
|
search_depth: 'basic', |
|
|
include_answer: true, |
|
|
max_results: 3, |
|
|
topic: 'general' |
|
|
}); |
|
|
|
|
|
const options = { |
|
|
hostname: 'api.tavily.com', |
|
|
port: 443, |
|
|
path: '/search', |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json', |
|
|
'Content-Length': Buffer.byteLength(postData), |
|
|
'Authorization': `Bearer ${tavilyApiKey}` |
|
|
} |
|
|
}; |
|
|
|
|
|
const req = https.request(options, (res) => { |
|
|
let data = ''; |
|
|
res.on('data', (chunk) => data += chunk); |
|
|
res.on('end', () => { |
|
|
try { |
|
|
const result = JSON.parse(data); |
|
|
resolve(result); |
|
|
} catch (e) { |
|
|
reject(e); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
req.on('error', reject); |
|
|
req.write(postData); |
|
|
req.end(); |
|
|
}); |
|
|
} |
|
|
|
|
|
/** |
|
|
* 查询黄历 |
|
|
*/ |
|
|
async function queryAlmanac(date) { |
|
|
// 调用 chinese-almanac skill |
|
|
const almanacPath = '/root/.openclaw/workspace/skills/chinese-almanac/almanac.js'; |
|
|
|
|
|
try { |
|
|
const almanac = require(almanacPath); |
|
|
const result = await almanac.queryAlmanac(date); |
|
|
return result; |
|
|
} catch (e) { |
|
|
console.error('黄历查询失败:', e.message); |
|
|
// Fallback: 基础信息 |
|
|
return { |
|
|
success: false, |
|
|
date: date, |
|
|
lunarDate: '查询失败', |
|
|
yi: [], |
|
|
ji: [], |
|
|
chong: '' |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* 八字分析 - 根据日主和明日五行 |
|
|
*/ |
|
|
function analyzeBazi(targetDate, userBazi) { |
|
|
const date = new Date(targetDate); |
|
|
const dayMaster = userBazi.dayMaster; // 丙火 |
|
|
|
|
|
// 简化的五行分析(实际应该用更精确的干支计算) |
|
|
const dayOfWeek = date.getDay(); |
|
|
const elements = ['金', '木', '水', '火', '土']; |
|
|
const tomorrowElement = elements[dayOfWeek % 5]; |
|
|
|
|
|
// 五行生克关系 |
|
|
const shengKe = { |
|
|
'丙火': { |
|
|
'金': '火克金 - 财星,利于求财', |
|
|
'木': '木生火 - 印星,贵人相助', |
|
|
'水': '水克火 - 官杀,压力较大', |
|
|
'火': '火比和 - 兄弟,竞争激烈', |
|
|
'土': '火生土 - 食伤,才华展现' |
|
|
} |
|
|
}; |
|
|
|
|
|
const analysis = shengKe[dayMaster]?.[tomorrowElement] || '五行平衡'; |
|
|
|
|
|
// 根据分析生成建议 |
|
|
const suggestions = { |
|
|
'财星': { |
|
|
lucky: ['商务谈判', '投资决策', '签订合同'], |
|
|
avoid: ['大额支出', '冒险投资'] |
|
|
}, |
|
|
'印星': { |
|
|
lucky: ['学习新知', '拜访长辈', '规划未来'], |
|
|
avoid: ['独自决策', '匆忙行动'] |
|
|
}, |
|
|
'官杀': { |
|
|
lucky: ['处理公务', '解决问题', '锻炼身体'], |
|
|
avoid: ['与人争执', '高风险活动'] |
|
|
}, |
|
|
'比和': { |
|
|
lucky: ['团队合作', '朋友聚会', '交流沟通'], |
|
|
avoid: ['单打独斗', '固执己见'] |
|
|
}, |
|
|
'食伤': { |
|
|
lucky: ['创意工作', '表达展示', '休闲娱乐'], |
|
|
avoid: ['批评他人', '过度消费'] |
|
|
} |
|
|
}; |
|
|
|
|
|
const key = Object.keys(suggestions).find(k => analysis.includes(k)) || '比和'; |
|
|
|
|
|
return { |
|
|
dayMaster: dayMaster, |
|
|
tomorrowElement: tomorrowElement, |
|
|
analysis: analysis, |
|
|
suggestions: suggestions[key] |
|
|
}; |
|
|
} |
|
|
|
|
|
/** |
|
|
* 生成运势报告 |
|
|
*/ |
|
|
async function generateFortuneReport(targetDate) { |
|
|
const tomorrow = new Date(targetDate); |
|
|
tomorrow.setDate(tomorrow.getDate() + 1); |
|
|
const dateStr = tomorrow.toISOString().split('T')[0]; |
|
|
|
|
|
console.log(`[Horoscope] 查询 ${dateStr} 的运势...`); |
|
|
|
|
|
// 1. 查询黄历 |
|
|
const almanac = await queryAlmanac(dateStr); |
|
|
console.log('[Horoscope] 黄历查询完成'); |
|
|
|
|
|
// 2. 搜索金牛座运势(王院长是金牛座) |
|
|
let horoscopeText = ''; |
|
|
try { |
|
|
const horoscopeResult = await searchHoroscope(dateStr, '金牛座'); |
|
|
if (horoscopeResult.answer) { |
|
|
horoscopeText = horoscopeResult.answer; |
|
|
} else if (horoscopeResult.results && horoscopeResult.results.length > 0) { |
|
|
horoscopeText = horoscopeResult.results[0].content; |
|
|
} |
|
|
} catch (e) { |
|
|
console.error('星座运势搜索失败:', e.message); |
|
|
horoscopeText = '今日金牛座运势平稳,保持积极心态。'; |
|
|
} |
|
|
console.log('[Horoscope] 星座运势查询完成'); |
|
|
|
|
|
// 3. 八字分析 |
|
|
const baziAnalysis = analyzeBazi(dateStr, USER_BIRTH_INFO.bazi); |
|
|
console.log('[Horoscope] 八字分析完成'); |
|
|
|
|
|
// 4. 生成综合报告 |
|
|
const report = { |
|
|
date: dateStr, |
|
|
user: USER_BIRTH_INFO.name, |
|
|
zodiac: USER_BIRTH_INFO.westernZodiac, |
|
|
chineseZodiac: USER_BIRTH_INFO.zodiac, |
|
|
almanac: almanac, |
|
|
horoscope: { |
|
|
text: horoscopeText, |
|
|
source: 'Tavily AI Search' |
|
|
}, |
|
|
bazi: baziAnalysis, |
|
|
overallLuck: calculateOverallLuck(almanac, baziAnalysis) |
|
|
}; |
|
|
|
|
|
return report; |
|
|
} |
|
|
|
|
|
/** |
|
|
* 计算整体运势指数 |
|
|
*/ |
|
|
function calculateOverallLuck(almanac, bazi) { |
|
|
let score = 3; // 基础 3 星 |
|
|
|
|
|
// 黄历加分项 |
|
|
if (almanac.yi && almanac.yi.length > 3) score += 0.5; |
|
|
if (almanac.chong && !almanac.chong.includes('鼠')) score += 0.5; |
|
|
|
|
|
// 八字加分项 |
|
|
if (baziAnalysis.analysis.includes('贵人') || baziAnalysis.analysis.includes('财星')) { |
|
|
score += 0.5; |
|
|
} |
|
|
|
|
|
const stars = Math.min(5, Math.max(1, Math.round(score))); |
|
|
return '★'.repeat(stars) + '☆'.repeat(5 - stars); |
|
|
} |
|
|
|
|
|
/** |
|
|
* 格式化运势报告为消息 |
|
|
*/ |
|
|
function formatFortuneMessage(report) { |
|
|
const { date, user, zodiac, almanac, horoscope, bazi, overallLuck } = report; |
|
|
|
|
|
let message = `📅 **${user} · 明日运势**\n`; |
|
|
message += `${date} | ${almanac.lunarDate || '农历查询中'}\n\n`; |
|
|
|
|
|
message += `✨ **整体运势**: ${overallLuck}\n\n`; |
|
|
|
|
|
message += `🗓️ **黄历宜忌**\n`; |
|
|
message += `宜:${almanac.yi?.join('、') || '查询中'}\n`; |
|
|
message += `忌:${almanac.ji?.join('、') || '查询中'}\n`; |
|
|
if (almanac.chong) { |
|
|
message += `冲煞:${almanac.chong}\n`; |
|
|
} |
|
|
message += '\n'; |
|
|
|
|
|
message += `♉ **${zodiac}运势**\n`; |
|
|
message += `${horoscope.text}\n\n`; |
|
|
|
|
|
message += `🔮 **八字分析**\n`; |
|
|
message += `日主:${bazi.dayMaster}\n`; |
|
|
message += `明日五行:${bazi.tomorrowElement}\n`; |
|
|
message += `${bazi.analysis}\n\n`; |
|
|
|
|
|
message += `💡 **趋吉避凶建议**\n`; |
|
|
message += `✅ 宜:${bazi.suggestions?.lucky?.join('、') || '顺其自然'}\n`; |
|
|
message += `❌ 忌:${bazi.suggestions?.avoid?.join('、') || '谨慎行事'}\n\n`; |
|
|
|
|
|
message += `_桐哥祝您明日顺利!_ 🌟`; |
|
|
|
|
|
return message; |
|
|
} |
|
|
|
|
|
/** |
|
|
* 主函数 - 获取明日运势 |
|
|
*/ |
|
|
async function getDailyHoroscope(options = {}) { |
|
|
const targetDate = options.date || new Date().toISOString().split('T')[0]; |
|
|
|
|
|
try { |
|
|
const report = await generateFortuneReport(targetDate); |
|
|
const message = formatFortuneMessage(report); |
|
|
|
|
|
return { |
|
|
success: true, |
|
|
report: report, |
|
|
message: message |
|
|
}; |
|
|
} catch (e) { |
|
|
console.error('运势查询失败:', e); |
|
|
return { |
|
|
success: false, |
|
|
error: e.message, |
|
|
message: '⚠️ 运势查询暂时失败,请稍后再试。' |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
// 导出函数 |
|
|
module.exports = { |
|
|
getDailyHoroscope, |
|
|
generateFortuneReport, |
|
|
formatFortuneMessage, |
|
|
searchHoroscope, |
|
|
queryAlmanac, |
|
|
analyzeBazi, |
|
|
USER_BIRTH_INFO, |
|
|
ZODIAC_SIGNS |
|
|
}; |
|
|
|
|
|
// 测试运行 |
|
|
if (require.main === module) { |
|
|
getDailyHoroscope().then(result => { |
|
|
console.log('\n=== 运势报告 ===\n'); |
|
|
console.log(result.message); |
|
|
}); |
|
|
}
|
|
|
|