前阵子在一个技术群里,看见某位大佬说写了个程序每天定时给女朋友发电子邮件,突然想到,我也可以写这样一个程序每天给女朋友定时发纪念日提醒及天气提醒,于是查阅相关资料并且向那个大佬要了他写的程序源码,在空余时间,我自己也动手实践了一下。
- node 相关知识
- axios 库的使用
- nodemailer 库的使用
- Promise 知识
- window 开启定时执行脚本任务
├── config.js #项目配置
├── daily-email.js #代码程序文件
├── public #后期会把每天生成的数据放到一个josn文件或者excel表中查看
├── template.html #测试生成模板页面使用
├── timing-task.bat #定时执行程序bat
<style>
.container {
background-color: rgb(165, 115, 140);
background: url("http://cn.bing.com/th?id=OHR.GreatTits_ZH-CN0546267922_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp") center no-repeat;
background-size: 100%;
width: 960px;
height: 540px;
display: flex;
justify-content: space-between;
flex-direction: column;
align-items: center;
color: white;
}
.title {
font-size: 22px;
margin-top: 50px;
}
.description {
color: white;
}
.content {
background: rgba(255, 255, 255, 0.5);
margin: 0 auto;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 20px;
box-sizing: border-box;
}
.content>p {
text-align: left;
font-size: 12px;
color: white;
width: 100%;
margin: 5px auto;
padding: 0;
}
</style>
<div class="container">
<div class="title">陪你一起看世界:第1162期</div>
<a class="description" target="_blank" href="https://www.bing.com/search?q=%E5%A4%A7%E5%B1%B1%E9%9B%80&form=hpcapt&mkt=zh-cn" rel="noopener">冬天树枝上的大山雀,法国 (© Eric Ferry/Alamy)</a>
<div class="content">
<p style="display: flex;">
<span>😘今天是:<span style="border-bottom: 1px dashed rgb(204, 204, 204); --darkreader-inline-border-bottom:#3e4446;" t="5" times="" data-darkreader-inline-border-bottom="">2022/2/18</span>,星期五,是我们在一起的第: 1162天~🥰🎈🎈🎈,今天天气: 多云 最高温 18℃,最低温 10℃,今天的风向是:东风。❤❤❤
</span>
</p>
<p></p>
<p>I love three things in this world.
Sun,Moon and You.
Sun for morning,Moon for night,
and You forever.</p>
</div>
</div>
const fetchBingPictrue = async () => {
const BPicList = await axios.get("https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1")
let bingInfo = {}
if (!BPicList.images[0]) {
console.error("获取bing壁纸失败")
return bingInfo
}
bingInfo.picUrl = `http://cn.bing.com${BPicList.images[0].url}`
bingInfo.copyright = BPicList.images[0].copyright
bingInfo.copyrightlink = BPicList.images[0].copyrightlink
console.log('🚀【获取到的bing壁纸信息】', bingInfo);
return bingInfo
}
const fetchWeaterByCity = async () => {
let weather = await axios.get(
`http://wthrcdn.etouch.cn/weather_mini?city=广州`,
);
if (weather.data.forecast.length === 0) {
console.error("获取今日天气失败")
return {}
}
console.log('🚀【获取到天气信息】', weather.data.forecast[0]);
return weather.data.forecast[0]
}
const fetchSentence = async () => {
const sentence = await axios.get(`https://chp.shadiao.app/api.php`);
console.log('🚀【获取到每日一句】', sentence);
return sentence
}
/**
* @description: 设置email的内容
* @return {*}
* @param {*} bingInfo bing的信息,包含图片及文字描述和链接
* @param {*} weatherInfo 今天天气信息
* @param {*} sentence 每日一句土味情话
*/
const setEmailContent = (bingInfo, weatherInfo, sentence) => {
const today = new Date().toLocaleDateString(); //获取今天的日期
const weekday = new Date().toLocaleString("default", { weekday: "long" }) // 获取今天是星期几
const dayCount = parseInt((new Date() - new Date('2000-01-01') / 1000 / 60 / 60 / 24) // 获取是第几天
const content = `
<style>
.container {
background-color: rgb(165, 115, 140);
background: url("${bingInfo.picUrl}") center no-repeat;
background-size: 100%;
width: 960px;
height: 540px;
display: flex;
justify-content: space-between;
flex-direction: column;
align-items: center;
color: white;
}
.title {
font-size: 22px;
margin-top: 50px;
}
.description {
color: white;
}
.content {
background: rgba(255, 255, 255, 0.5);
margin: 0 auto;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 20px;
box-sizing: border-box;
}
.content>p {
text-align: left;
font-size: 12px;
color: white;
width: 100%;
margin: 5px auto;
padding: 0;
}
</style>
<div class="container">
<div class="title">陪你一起看世界:第${dayCount}期</div>
<a class="description" target="_blank" href="${bingInfo.copyrightlink}">${bingInfo.copyright}</a>
<div class="content">
<p style="display: flex;">
<span>😘今天是:${today},${weekday},是我们在一起的第: ${dayCount}天~🥰🎈🎈🎈,今天天气: ${weatherInfo.type} 最${weatherInfo.high},最${
weatherInfo.low},今天的风向是:${weatherInfo.fengxiang}。❤❤❤
</span>
</p>
<p></p>
<p>${sentence}</p>
</div>
</div>
`;
return content
}
参考文章:https://segmentfault.com/a/1190000012251328
/**
* @description: 通过Nodemailer发送电子邮件
* @return {*}
* @param {*} content 邮件内容
*/
const sendEmailByNodemailer = (content) => {
const transporter = nodemailer.createTransport({
service: '163', // 使用了内置传输发送邮件 查看支持列表:https://nodemailer.com/smtp/well-known/
port: 465, // SMTP 端口
secureConnection: true, // 使用了 SSL
auth: {
user: '1XXXXX@163.com', //邮箱账号
pass: 'xxxyyyxxxyyzxzxz', // 不是邮箱密码,是你设置的smtp授权码
},
});
let mailOptions = {
from: `"邮箱名称" <邮箱地址>`, // 发送者 邮件地址
to: `"邮件标题" <要发送人的邮箱>`, // 逗号隔开的接收人列表
subject: `想和你一起看世界:第${parseInt(
(new Date() - new Date('2000-01-01') / 1000 / 60 / 60 / 24,
)}期`, // 邮件标题
// 发送text或者html格式
// text: 'Hello world?', // plain text body
// 发送的html内容
html: content,
};
// send mail with defined transport object
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log("error", error);
}
console.log("Message sent: %s", info.messageId);
console.log(info);
});
}
const handleSendEmail = async () => {
try {
const bingInfo = await fetchBingPictrue()
const weatherInfo = await fetchWeaterByCity()
const sentence = await fetchSentence()
const emailContent = setEmailContent(bingInfo, weatherInfo, sentence)
sendEmailByNodemailer(emailContent)
} catch (error) {
// 这里可以catch 相关错误后,将错误信息发送到邮箱通知查看
console.error("发送信息失败")
}
}
// config.js
// 配置相关信息
exports.config = {
CITY: '深圳', //获取城市天气
TOGETHER_TIME: '2000-01-01', //在一起的时间
EMAIL_NAME: '陪你一起看世界系列', //邮箱名称
EMALI_SERVICE: '163', //使用163服务 https://nodemailer.com/smtp/well-known/
EMAIL_ACCOUNT: 'xxxx@163.com', //使用发送邮箱的账号
EMAIL_PASS: 'xxxyyyxxxyyzxzxz', // 不是邮箱密码,是你设置的smtp授权码
TO_EMAIL: 'xxxxxxxxxxx@qq.com', // 接送者邮箱
TO_TITLE: '你的专属', //标题
}
// timing-task.bat
// bat 批处理文件是一个文本文件,这个文件的每一行都是一条DOS命令(大部分时候就好象我们在DOS提示符下执行的命令行一样),你可以使用DOS下的Edit或者Windows的记事本(notepad)等任何文本文件编辑工具创建和修改批处理文件
node D:/node/daily-email.js //这里写项目执行文件的位置
- 第一种方式,使用云函数的方式,设置定时执行程序;
- 第二种方式,购买轻量级服务器的方式,利用服务器实现定时执行该程序;
- 第三种方式,将自己的电脑变成服务器(如果你的电脑长期不关机的话),或者当程序设置开机自动执行,每天开机时自动执行脚本发送邮件。
const fs = require("fs")
const path = require('path')
const dataFilePath = path.resolve(__dirname, './public/data.json')
/**
* @description: 写入数据到json文件中
* @return {*}
* @param {*} data 需要写入的obj数据
*/
const writeData = (data) => {
try {
const oldFileData = fs.readFileSync(dataFilePath, 'UTF-8').toString()
const oldData = JSON.parse(oldFileData)
oldData.push(data)
fs.writeFileSync(dataFilePath, JSON.stringify(oldData))
} catch (err) {
console.log('🚀【写入数据出现错误】', err);
}
}
- 原因:有些HTML的电子邮件客户端没有按照 W3C 规范一致的地呈现网页,所有导致渲染出来会不一致,如部分标签不支持,CSS 存在兼容问题,且对
<head>
标签中写style
支持度不是那么好,js 无法使用或者不支持嵌入视频和音频。 - 解决办法:使用内联样式写 CSS,了解常用邮箱 CSS 兼容性的相关知识,查阅文档。
- 解决办法,双方都使用 QQ 邮箱,且是 QQ 好友可以避免第二种问题,能够正常显示内容,目前第一种问题暂时没有想到好的解决办法。
- 利用碎片化的学习积累,将实现过程、碰到的问题以及相应的解决方案记录下来,相信日积月累的知识沉淀,可以帮助自己更好更快的掌握编程技术,在工作中能更好的实现业务,也能很好的扩展自己的知识广度。
- 相关源码:https://github.com/sean-lgt/timing-email-node
- 以上就是本文的全部内容,希望这篇文章对你有所帮助,欢迎点赞和收藏,如果发现有什么错误或者更好的解决方案及建议,欢迎随时联系。