【教你白嫖github actions】每日给女盆友定时发邮件

4,573 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

地址戳这里github地址,欢迎star

演示图

WechatIMG3409.png

WechatIMG1047.png

背景

一直考虑做一个每天定时给对象发信息的小工具,但是一直没舍得买服务器。某天,正当我热火朝天地摸鱼时,发现了github上的一个功能github actions,相当于一个自带服务器的持续构建功能,并且既可以发布定时任务,每个月又有白嫖的分钟数,岂不是正和我意?起飞~

思路

整体基于node开发,并且引入三个npm库:

  • nodemailer发送邮件
  • node-fetch请求接口数据
  • dayjs计算日期

主要逻辑代码如下:

// index.js
const fetch = require('node-fetch');
const dayjs = require('dayjs');
const utc = require('dayjs/plugin/utc');
const timezone = require('dayjs/plugin/timezone');

const sendEmail = require('./sendEmail');
const emailHtml = require('./emailHtml');

// 给dayjs添加时区选项
dayjs.extend(utc);
dayjs.extend(timezone);

const {
  fromDisplayText,
  fromDisplaySubText,
  user,
  to,
  weatherKey,
  location,
  type,
  tianXingKey,
  startDay,
} = require('./config');

async function init() {
  try {
    // 获取天气信息
    const weatherRes = await fetch(
      `https://devapi.qweather.com/v7/weather/3d?key=${weatherKey}&location=${location}`
    );
    const weatherData = await weatherRes.json();

    // 获取天气生活指数
    const lifeRes = await fetch(
      `https://devapi.qweather.com/v7/indices/1d?key=${weatherKey}&location=${location}&type=${type}`
    );
    const lifeData = await lifeRes.json();

    // 获取one一个文案及图片
    const oneRes = await fetch(
      `http://api.tianapi.com/txapi/one/index?key=${tianXingKey}`
    );
    const oneData = await oneRes.json();
    const { word, imgurl } = oneData.newslist[0];

    // 计算日期
    const lovingDays = dayjs(dayjs().tz('Asia/Shanghai')).diff(
      startDay,
      'days'
    );

    // 用邮件模版生成字符串
    const htmlStr = emailHtml(weatherData, lifeData, word, imgurl, lovingDays);

    // 发送邮件;
    sendEmail({
      from: fromDisplayText,
      to,
      subject: fromDisplaySubText,
      html: htmlStr,
    });
  } catch (e) {
    // 发送邮件给自己提示
    sendEmail({
      from: '报错啦',
      to: user,
      subject: '定时邮件-报错提醒',
      html: '请查看github actions',
    });
  }
}

init();

主要就是请求接口数据,然后用接口数据生成需要的html,最后发送邮件。

1、生成需要的邮件

function fn(weatherData, lifeData, word, imgurl, lovingDays) {
  const { daily: weatherDataDaily } = weatherData;
  const { daily } = lifeData;

  return `<!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="IE=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    </head>
    <body>
      <div>
        <!-- 天数 -->
        <div>
          <p>今天是在一起的第${lovingDays}天!</p>
        </div>
        <!-- 图片 -->
        <div>
          <img
            style="width: 100%; max-width: 768px"
            src="${imgurl}"
            alt="图片"
          />
        </div>
        <!-- 每日一句 -->
        <div>
          <p style="font-size: 14px; text-indent: 2em; font-style: italic;">
            ${word}
          </p>
        </div>
        <!-- 天气 -->
        <div>
          <p>
            <b>今日气温:</b>
            <span>${weatherDataDaily[0].tempMin}°C - ${weatherDataDaily[0].tempMax}°C</span>
          </p>
          <ul>
            <li style="margin-bottom: 10px">
              ${daily[1].name}(${daily[1].category}):
              ${daily[1].text}
            </li>
            <li style="margin-bottom: 10px">
              ${daily[2].name}(${daily[2].category}):
              ${daily[2].text}
            </li>
            <li style="margin-bottom: 10px">
              ${daily[0].name}(${daily[0].category}):
              ${daily[0].text}
            </li>
          </ul>
        </div>
      </div>
    </body>
  </html>
  `;
}

module.exports = fn;

2、发送邮件

const nodemailer = require('nodemailer');
const { user, pass } = require('./config');

const sendMail = async (data) => {
  let transporter = nodemailer.createTransport({
    host: 'smtp.qq.com',
    port: '465',
    secureConnection: true,
    auth: {
      user,
      pass,
    }
  });

  data.from = `"${data.from}" ${user}`;

  await transporter.sendMail(data);
};

module.exports = sendMail;

3、指定定时任务

需要在根目录创建一个.github文件夹,然后在.github文件夹中再新建一个workflows文件夹,然后在里面任意创建一个yml格式的文件,文件内容如下:

name: ccy-helper

on:
  schedule:
    - cron: "00 23 * * *" # 该时间为UTC时间,比北京时间晚8个小时,每天早上7点自动执行(需要注意该时间不准时,会有不同程度的分钟数的延迟)

jobs:
  send:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v1
      - name: Run Project
        run: npm install && npm run server # 安装依赖并执行server

如何使用

  1. 完整代码拉下来

  2. 修改其中的config.js配置文件

    // 配置信息
    module.exports = {
      fromDisplayText: '阳哥', // 收件箱展示的来件人名字
      fromDisplaySubText: '每日提醒', // 收件箱展示的次级标题
      user: 'xxx', // 发送者邮箱
      pass: 'xxx', // 发送者邮箱MTP协议密码(很简单,进邮箱设置下就有了)
      to: 'xxx', // 发送到谁,填邮箱
      weatherKey: '33369e365fe84eb68876f52a2ae51cca', // 和风天气key
      location: 'xxx',  // 和风天气-地区的id,如:101270119
      type: '1,3,9', // 和风天气-生活指数type
      tianXingKey: 'eb75297818f2413f24e1f1f76662bccd', // 天行数据的key
      startDay: '2015-09-29', // 在一起的日期
    }
    

    和风天气key天行数据的key可以直接用我的,也可以自己申请个账号,每天都有免费的额度。

    和风天气查询地区的id可以看该文档:dev.qweather.com/docs/api/ge…

    例如查询北京的:geoapi.qweather.com/v2/city/loo…

    查其他地区的话,替换后面的location就好

  3. 将配置好的代码提交到自己的仓库就好了


Tips:本地测试的时候直接执行npm run server就可以看邮件效果,记得让对象打开微信里的QQ邮箱提醒功能。基于此思路你也可以开发一些其他的小工具,要是你有好的想法,欢迎一起交流~