使用青龙创建定时任务-晚霞预测等

前言

平时喜欢瞎拍照,但是相机太重懒得平时拿,所以就想到每次提前查询晚霞概率,如果第二天晚霞概率较高,那么就带上相机。每次去查询很麻烦就想着有没有类似的自动任务平台,刚好发现绿联新版本上内置了青龙,正好满足了我的需求。

青龙定时任务管理平台是一个功能强大的自动化任务调度系统,它允许用户通过简单的配置和命令来管理各种定时任务。 该平台的核心功能包括: 任务调度:支持创建、编辑、删除定时任务,并能够设置任务的执行时间、频率和优先级。支持 javascript,python,shell 脚本

1. 安装青龙面板

我使用的是绿联最新版的系统,内置了青龙面板,所以只需要在系统桌面打开青龙面板即可

内置的版本较低,而且镜像不是官方镜像所以更新不及时,所以我选择了手动安装,安装方法如下:

WARNING

1.如果遇到镜像安装或者下载失败的,请自己查询 docker 镜像代理

2.青龙面板需要占用 5700 端口,所以需要先检查 5700 端口是否被占用,如果被占用,请更换端口

以下以项目为例。

打开docker, 点击项目,点击创建,创建一个项目

预览
创建项目
Docker Compose(项目)
services:
app:
  image: whyour/qinglong:latest
  volumes:
    - /volume1/docker/ql/config:/ql/data
  ports:
    - "5777:5700" 
  environment:
    QlBaseUrl: '/'
  restart: always
  network_mode: bridge

按照上述配置,点击立即部署,等待部署完成,即可访问青龙面板

访问地址为局域网访问 nasip:5777

2. 申请企业微信应用

注册企业微信

企业微信注册 注册成功,并等待审核通过。

添加员工

添加员工,添加员工后需要记录好员工 账号(员工账号)。

管理后台

创建企业微信应用,创建成功后需要记录好应用 id(应用id) 和 secret(应用secret), 用于后续配置。依照企业微信引导配置企业微信应用的 可信域名, 用于后续配置。 依照企业微信引导配置企业微信应用的 企业可信IP, 用于后续配置。

记录好企业 ID

企业信息, 记录下企业Id(企业id)

3. 配置青龙面板

  • 通知方式 选择企业微信
预览
通知方式
  • weWorkAppkey按照以下填写 企业id,应用secret,员工账号,应用id

  • 如果是只有内网IP(内网 ip 一般对应的公网ip是动态的会导致上一步配置的可信 ip失效),需要设置 weWorkOrigin。如果更有固定公网 ip 可以跳过这一步。

    # 在代理的服务器上设置好代理,以 nginx 为例 假设代理服务器的ip为120.17.125.26
          listen 5566;
          server_name _;
          access_log /var/log/nginx/access.5566.log main;
          error_log /var/log/nginx/error.5566.log;
          location / {
              proxy_pass https://qyapi.weixin.qq.com/;
              proxy_set_header Host qyapi.weixin.qq.com;
              proxy_set_header X-Real-IP $remote_addr;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
              proxy_set_header X-Forwarded-Proto $scheme;
          }
      }

    然后在青龙面板中配置 weWorkOrigin 为 http://120.17.125.26:5566 有自定义域名设置域名也可以。服务器安全规则上,需要放开 5566 (可以自定义端口)端口。

  • 保存配置,如果发送成功,则配置成功。

4. 编写自定义脚本

进入脚本管理, 创建一个 ql_sunset.js 脚本 方便后期管理可以新建一个 node 目录,区分不同语言

// 新建 ql_sunset.js
async function fetchAsync (url) {
    const res = await fetch(url)
    if(res.ok) return await res.json()
    return { }
}

async function getSunsetbot() {
    const tmpId = Math.floor((Math.random()*10000000)+1).toString();
    const types = ['rise_2', 'set_2']
    const arr = types.map(type => fetchAsync(`https://sunsetbot.top/?query_id=${tmpId}&intend=select_city&query_city=%E6%B7%B1%E5%9C%B3&event_date=None&event=${type}&times=None`))
    const resArr = await Promise.all(arr)
    const msgs = resArr.map(item => {
        const { place_holder, tb_event_time, tb_quality, tb_aod } = item
        const tb_event_time1 = tb_event_time.replace('\<br\>', ' ')
        return `${tb_event_time1}\n鲜艳度:${tb_quality}气溶胶:${tb_aod}\n【${place_holder}`.replaceAll('\<br\>', '')
    })
    
    QLAPI.systemNotify({title: '明日火烧云', content: msgs.join('\n\n') }).then((x) => {
        console.log('systemNotify', x);
    });
}		

getSunsetbot()

5. 配置定时任务

进入定时任务, 创建一个定时任务

任务名称: 晚霞预测 命令/脚本: task ./node/ql_sunset.js 定时类型: 常规定时 执行周期: 0 0 22 ? * *

执行周期使用cron表达式 不了解可以使用cron生成工具,上面的公式代表每天22点整执行定时任务。

6. 测试

定时任务 列表中找到晚霞预测任务,点击操作栏的运行。查看日志,是否有输出。如果没有错误则检查手机上是否收到了通知。

预览
任务日志
预览
手机消息

7. 其他脚本

生日提醒
天气助手
import { LunarDay, SolarDay } from '/root/.local/share/pnpm/global/5/node_modules/tyme4ts/dist/lib/index.mjs';

// 提前多少天提醒
const limit = 12
// name: 名字 date: 生日  type: 输入类型(1 公历, 2 农历) notice: 过什么生日(1 公历, 2 农历)
const birthdays = [
  {
      name: 'xxx',
      date: '1991-09-23',
      type: 2,
      notice: 2
  }
]


export function getBirthdayInfo({ date, type, notice }) {
  const [year, month, day] = date.split('-').map(Number);
  const currentYear = new Date().getFullYear();
  const currentMonth = new Date().getMonth() + 1;
  const currentDay = new Date().getDate();

  let birthSolar;
  let birthLunar;

  if (type === 1) {
      // 公历输入
      birthSolar = SolarDay.fromYmd(year, month, day);
      birthLunar = birthSolar.getLunarDay();
  } else {
      // 农历输入
      birthLunar = LunarDay.fromYmd(year, month, day);
      birthSolar = birthLunar.getSolarDay();
  }
  // const thisYearLunar = LunarDay.fromYmd(currentYear, month, day);
  // const thisYearSolar = thisYearLunar.getSolarDay();
  const YS = birthSolar.getYear()
  const MS = birthSolar.getMonth()
  const DS = birthSolar.getDay()
  const YL = birthLunar.getYear()
  const ML = birthLunar.getMonth()
  const DL = birthLunar.getDay()
  let currentBirthDay;

  // 过公历生日
  if (notice === 1) {
      currentBirthDay = SolarDay.fromYmd(currentYear, MS, DS)
  }

  // 过农历生日
  if (notice === 2) {
      currentBirthDay = LunarDay.fromYmd(currentYear, ML, DL).getSolarDay()
  }

  return {
      age: currentYear - YS,
      solarBirthday: birthSolar.toString(),
      lunarBirthday: birthLunar.toString(),
      countDown: currentBirthDay.subtract(SolarDay.fromYmd(currentYear, currentMonth, currentDay)),
      zodiac: LunarDay.fromYmd(YL, ML, DL).getYearSixtyCycle().getEarthBranch().getZodiac().toString(),
      solarBirthdayCurrent: currentBirthDay.toString()
  };
}

function checkBirthday() {
  birthdays.forEach((item) => {
      const res = getBirthdayInfo(item)
      console.log(item.name, res)
      if (res.countDown === 0 || res.countDown === limit) {
          const msg1 = res.countDown === 0 ? `今天是${item.name}${res.age}岁生日,记得要送上生日祝福哦!` : `距离${item.name}${res.age}岁生日还有${res.countDown}天, 记得要提前准备礼物哦` 
          const msg2 = `\n\n公历生日:${res.solarBirthday}\n农历生日:${res.lunarBirthday}\n生肖属相:${res.zodiac}`
          QLAPI.systemNotify({ title: '生日提醒', content: msg1 + msg2 }).then((x) => {
              console.log(`生日提醒 ${item.name}`, x);
          });
      }
  })
}

checkBirthday()
预览
生日效果

8. 常见问题

8.1 提示esm 模块找不到

青龙面板对于 esm 路径的处理有问题,目前是已知 BUG, 预计将在2.18.4版本修复。 临时方案 导入时候使用绝对路径,青龙默认依赖通过 pnpm 安装,安装目录为 root/.local/share/pnpm/global/5/node_modules/, 以 jose 为例。

import { SignJWT, importPKCS8 } from '/root/.local/share/pnpm/global/5/node_modules/jose/dist/webapi/index.js';