|
|
#!/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 };
|
|
|
|