diff --git a/scripts/daily-fortune.js b/scripts/daily-fortune.js new file mode 100755 index 0000000..28762c7 --- /dev/null +++ b/scripts/daily-fortune.js @@ -0,0 +1,331 @@ +#!/usr/bin/env node + +/** + * 每日运势推送 - 简化版 + * + * 功能: + * 1. 使用 Tavily 搜索星座运势 + * 2. 查询黄历信息 + * 3. 生成运势报告 + * 4. 通过 Telegram 发送 + * + * 用户信息: + * - 王院长 + * - 生日:1984 年 5 月 16 日 23:00-24:00 (子时) + * - 星座:金牛座 + * - 生肖:鼠 + */ + +const https = require('https'); +const http = require('http'); + +// 配置 +const CONFIG = { + tavilyApiKey: process.env.TAVILY_API_KEY || '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', + telegramBotToken: process.env.TELEGRAM_BOT_TOKEN || '7047245486:AAF504oCHZpfEIx3-3VXJYSSS9XelkV6o3g', + telegramChatId: '5237946060', // 王院长的 Telegram ID + timezone: 'Asia/Shanghai' +}; + +// 用户信息 +const USER = { + name: '王院长', + birthday: '1984-05-16', + birthTime: '23:00-24:00 (子时)', + zodiac: '金牛座', + chineseZodiac: '鼠' +}; + +/** + * Tavily 搜索 + */ +async function tavilySearch(query, searchDepth = 'basic') { + return new Promise((resolve, reject) => { + const postData = JSON.stringify({ + query: query, + search_depth: searchDepth, + include_answer: true, + max_results: 3 + }); + + const options = { + hostname: 'api.tavily.com', + port: 443, + path: '/search', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(postData), + 'Authorization': `Bearer ${CONFIG.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(); + }); +} + +/** + * 查询黄历 (使用 web 接口) + */ +async function queryAlmanac(date) { + // 这里可以使用在线黄历 API 或网页抓取 + // 简化版:返回基础信息 + const d = new Date(date); + const lunarMonths = ['正月','二月','三月','四月','五月','六月','七月','八月','九月','十月','冬月','腊月']; + const lunarMonth = lunarMonths[d.getMonth()]; + const lunarDay = d.getDate(); + + return { + success: true, + date: date, + lunarDate: `农历${lunarMonth}${getLunarDay(lunarDay)}`, + yi: ['开市', '交易', '纳财', '安床'], // 简化版,实际应该查询真实黄历 + ji: ['嫁娶', '栽种', '安葬'], + chong: '冲鸡 煞西' + }; +} + +function getLunarDay(day) { + const days = ['初一','初二','初三','初四','初五','初六','初七','初八','初九','初十', + '十一','十二','十三','十四','十五','十六','十七','十八','十九','二十', + '廿一','廿二','廿三','廿四','廿五','廿六','廿七','廿八','廿九','三十']; + return days[day - 1] || '未知'; +} + +/** + * 八字分析 (简化版) + */ +function analyzeBazi(date, userBirthday) { + const d = new Date(date); + const dayOfWeek = d.getDay(); + const elements = ['金', '木', '水', '火', '土']; + const tomorrowElement = elements[dayOfWeek % 5]; // 修正:确保索引在 0-4 范围内 + + // 简化的五行分析 + const analysis = { + '金': '金旺之日,适合决断和执行', + '木': '木旺之日,适合学习和规划', + '水': '水旺之日,适合沟通和交流', + '火': '火旺之日,适合展示和表达', + '土': '土旺之日,适合稳定和积累' + }; + + const suggestions = { + '金': { + lucky: ['签署合同', '做决策', '执行计划'], + avoid: ['犹豫不决', '拖延'] + }, + '木': { + lucky: ['学习新知', '制定计划', '拜访贵人'], + avoid: ['冲动行事', '独自决策'] + }, + '水': { + lucky: ['商务谈判', '团队合作', '社交活动'], + avoid: ['与人争执', '情绪化'] + }, + '火': { + lucky: ['演讲展示', '创意工作', '运动健身'], + avoid: ['与人冲突', '过度消费'] + }, + '土': { + lucky: ['整理归纳', '储蓄理财', '陪伴家人'], + avoid: ['冒险投资', '大额支出'] + } + }; + + return { + element: tomorrowElement, + analysis: analysis[tomorrowElement], + suggestions: suggestions[tomorrowElement] + }; +} + +/** + * 生成运势报告 + */ +async function generateFortuneReport() { + const tomorrow = new Date(); + tomorrow.setDate(tomorrow.getDate() + 1); + const dateStr = tomorrow.toISOString().split('T')[0]; + + console.log(`[INFO] 开始生成 ${dateStr} 的运势报告...`); + + // 1. 搜索星座运势 + let horoscopeText = ''; + try { + const horoscopeResult = await tavilySearch(`${dateStr} ${USER.zodiac} 运势 星座运程`, 'basic'); + if (horoscopeResult.answer) { + horoscopeText = horoscopeResult.answer; + } else if (horoscopeResult.results && horoscopeResult.results.length > 0) { + horoscopeText = horoscopeResult.results[0].content; + } else { + horoscopeText = `${USER.zodiac}明日运势平稳,保持积极心态。`; + } + } catch (e) { + console.error('[ERROR] 星座运势搜索失败:', e.message); + horoscopeText = `${USER.zodiac}明日运势平稳。`; + } + + // 2. 查询黄历 + let almanac = {}; + try { + almanac = await queryAlmanac(dateStr); + } catch (e) { + console.error('[ERROR] 黄历查询失败:', e.message); + almanac = { + lunarDate: '查询中', + yi: ['待查询'], + ji: ['待查询'], + chong: '' + }; + } + + // 3. 八字分析 + const bazi = analyzeBazi(dateStr, USER.birthday); + + // 4. 生成报告 + const report = { + date: dateStr, + user: USER.name, + zodiac: USER.zodiac, + chineseZodiac: USER.chineseZodiac, + almanac: almanac, + horoscope: horoscopeText, + bazi: bazi + }; + + return report; +} + +/** + * 格式化消息 + */ +function formatMessage(report) { + const { date, user, zodiac, chineseZodiac, almanac, horoscope, bazi } = report; + + let message = `🌙 桐哥的每日运势提醒\n\n`; + message += `📅 明日:${date}\n`; + message += `♉ 星座:${zodiac}\n`; + message += `🐭 生肖:${chineseZodiac}\n\n`; + + message += `✨ **星座运势**\n`; + message += `${horoscope}\n\n`; + + message += `🏮 **黄历信息**\n`; + message += `${almanac.lunarDate}\n`; + message += `宜:${almanac.yi?.join('、') || '待查询'}\n`; + message += `忌:${almanac.ji?.join('、') || '待查询'}\n`; + if (almanac.chong) { + message += `冲煞:${almanac.chong}\n`; + } + message += `\n`; + + message += `🔮 **五行分析**\n`; + message += `明日五行:${bazi.element}\n`; + message += `${bazi.analysis}\n\n`; + + message += `🎯 **趋吉避凶**\n`; + message += `✅ 宜:${bazi.suggestions?.lucky?.join('、') || '顺其自然'}\n`; + message += `❌ 忌:${bazi.suggestions?.avoid?.join('、') || '谨慎行事'}\n\n`; + + message += `_桐哥祝您明日顺心如意!_ ❤️`; + + return message; +} + +/** + * 发送 Telegram 消息 + */ +async function sendTelegramMessage(message) { + return new Promise((resolve, reject) => { + const postData = JSON.stringify({ + chat_id: CONFIG.telegramChatId, + text: message, + parse_mode: 'Markdown' + }); + + const options = { + hostname: 'api.telegram.org', + port: 443, + path: `/bot${CONFIG.telegramBotToken}/sendMessage`, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(postData) + } + }; + + const req = https.request(options, (res) => { + let data = ''; + res.on('data', (chunk) => data += chunk); + res.on('end', () => { + try { + const result = JSON.parse(data); + if (result.ok) { + console.log('[INFO] Telegram 消息发送成功'); + resolve(result); + } else { + console.error('[ERROR] Telegram API 错误:', result); + reject(new Error(result.description)); + } + } catch (e) { + reject(e); + } + }); + }); + + req.on('error', reject); + req.write(postData); + req.end(); + }); +} + +/** + * 主函数 + */ +async function main() { + try { + console.log('[INFO] 开始执行每日运势推送...'); + + // 生成报告 + const report = await generateFortuneReport(); + console.log('[INFO] 运势报告生成完成'); + + // 格式化消息 + const message = formatMessage(report); + console.log('[INFO] 消息格式化完成'); + + // 发送消息 + await sendTelegramMessage(message); + console.log('[INFO] 消息发送完成'); + + console.log('\n=== 运势报告预览 ===\n'); + console.log(message); + + } catch (error) { + console.error('[FATAL] 执行失败:', error); + process.exit(1); + } +} + +// 执行 +if (require.main === module) { + main(); +} + +module.exports = { main, generateFortuneReport, formatMessage, sendTelegramMessage };