import express from 'express' import * as fs from "node:fs"; import nodemailer from "nodemailer"; import * as dotenv from "dotenv"; dotenv.config() const app = express() const PORT = parseInt(process.env.PORT) || 20123 const TIMEOUT = parseInt(process.env.TIMEOUT) || 60000 const SMTP_HOST = process.env.SMTP_HOST const SMTP_PORT = parseInt(process.env.SMTP_PORT) const SMTP_SECURE = process.env.SMTP_SECURE === 'true' const SMTP_SENDER = process.env.SMTP_SENDER const SMTP_PASS = process.env.SMTP_PASS let stat = null app.get('/', (req, res) => { res.send(JSON.stringify(stat)) }) const configJson = fs.readFileSync('./data/config.json', 'utf8'); const configs = JSON.parse(configJson); console.log('Read config successfully.'); const fetchData = (uid, key) => fetch(`https://api.torn.com/user/${uid}?selections=cooldowns,bars,travel&key=${key}`) const transporter = nodemailer.createTransport({ host: SMTP_HOST, port: SMTP_PORT, secure: SMTP_SECURE, // true for port 465, false for other ports auth: { user: SMTP_SENDER, pass: SMTP_PASS, }, }); if (configs.length > 0) { for (const config of configs) { if (!config['apikey'] || typeof config['apikey'] !== 'string') { throw new Error('配置文件apikey无效') } if (!config['email'] || typeof config['email'] !== 'string') { throw new Error('配置文件email无效') } if (!config['uid'] || typeof config['uid'] !== 'string' || (config['uid'] | 0) === 0) { throw new Error('配置文件uid无效') } const apikey = config['apikey'] const email = config['email'] const uid = config['uid'] // transporter.sendMail({ // from: `"Torn通知" <${SMTP_SENDER}>`, // sender address // to: email, // list of receivers // subject: "Torn通知测试", // Subject line // text: "这是一封测试邮件 - Wuhu Mail Notify Project", // plain text body // // html: "Hello world?", // html body // }).catch(console.error); const timeout = async () => { try { const apiData = await (await fetchData(uid, apikey)).json(); const date = new Date(); // ms const timestamp = date.getTime() //ms const serverTime = apiData?.server_time * 1000; // ms const timeDiff = parseInt((timestamp - serverTime) / 1000 + ''); // sec const drugCd = apiData?.cooldowns?.drug const energyFull = apiData?.energy?.fulltime const nerveFull = apiData?.nerve?.fulltime const travelTimeLeft = apiData?.travel?.time_left // cd if (drugCd && typeof drugCd === 'number' && drugCd - timeDiff < 60 && drugCd - timeDiff > 0) { console.log(`[${uid}]发送XAN通知`) transporter.sendMail({ from: `"Torn通知" <${SMTP_SENDER}>`, // sender address to: email, // list of receivers subject: "到饭点了", // Subject line text: "药CD即将结束 - Wuhu Mail Notify Project", // plain text body // html: "Hello world?", // html body }).catch(console.error); } else { console.log(`[${uid}]毒CD未到通知时间 ( 还需 ${drugCd - timeDiff}s ),跳过`) } // 体力满 if (energyFull && typeof energyFull === 'number' && energyFull - timeDiff < 60 && energyFull - timeDiff > 0) { console.log(`[${uid}]发送体力通知`) transporter.sendMail({ from: `"Torn通知" <${SMTP_SENDER}>`, to: email, subject: "体力已满", text: "角色体力已满 - Wuhu Mail Notify Project", }).catch(console.error); } else { console.log(`[${uid}]体力未满 ( 还需 ${energyFull - timeDiff}s ),跳过`) } // 精神满 if (nerveFull && typeof nerveFull === 'number' && nerveFull - timeDiff < 60 && nerveFull - timeDiff > 0) { console.log(`[${uid}]发送精神通知`) transporter.sendMail({ from: `"Torn通知" <${SMTP_SENDER}>`, to: email, subject: "精神已满", text: "角色精神已满 - Wuhu Mail Notify Project", }).catch(console.error); } else { console.log(`[${uid}]精神未满 ( 还需 ${nerveFull - timeDiff}s ),跳过`) } // 飞机落地 if (travelTimeLeft && typeof travelTimeLeft === 'number' && travelTimeLeft - timeDiff < 60 && travelTimeLeft - timeDiff > 0) { console.log(`[${uid}]发送飞行通知`) transporter.sendMail({ from: `"Torn通知" <${SMTP_SENDER}>`, to: email, subject: "飞行结束", text: "角色即将落地 - Wuhu Mail Notify Project", }).catch(console.error); } else { console.log(`[${uid}]飞机未到通知时间 ( 还需 ${travelTimeLeft - timeDiff}s ),跳过`) } } catch (e) { console.error(e) stat = { stack: e.stack, msg: e.message, ts: Date.now(), } } setTimeout(timeout, TIMEOUT) } timeout().catch(console.error) } } else { throw new Error('配置文件列表为空') } app.listen(PORT, () => { console.log(`App listening on port ${PORT}`) })