commit 5ec428a75cb0a9433604e2a2370c9a2543453454 Author: wy Date: Wed Mar 12 13:37:21 2025 +0800 init diff --git a/README.md b/README.md new file mode 100644 index 0000000..4edc54a --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +## Run + +```bash +npm install +node index.js +``` + +## Setup + +data/config.json +```json +[ + { + "apikey": "", + "email": "your@email.address", + "uid": "your uid" + } +] +``` +.env file +``` +PORT=20123 +TIMEOUT=60000 +SMTP_HOST= +SMTP_PORT= +SMTP_SECURE= +SMTP_SENDER= +SMTP_PASS= +``` diff --git a/index.js b/index.js new file mode 100644 index 0000000..4b6e0e0 --- /dev/null +++ b/index.js @@ -0,0 +1,139 @@ +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 + +console.log(process.env) + +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) { + 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) { + 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) { + 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) { + 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}`) +}) diff --git a/package.json b/package.json new file mode 100644 index 0000000..df44853 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "wuhu-mail-notify", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "serve": "node index.js" + }, + "private": true, + "devDependencies": { + "dotenv": "^16.4.7", + "express": "^4.21.2", + "nodemailer": "^6.10.0" + } +}