Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

The reason was that I saw the boiling point of a daily reminder message sent to my girlfriend. I automatically sent the message at a fixed time every day. I felt very interesting

Environment to prepare

Operating system: Supports macOS, Linux, and Windows

Operating environment: Node LTS is recommended. The minimum version is 8.x.

An introduction to creating an Egg project and directory structure

Quick start

The directory structure

run

Local development

$ npm i
$ npm run dev
$ open http://localhost:7001/
Copy the code

The deployment of production

$ npm start
$ npm stop
Copy the code

The controller

class HomeController extends Controller { async send() { const { ctx, app } = this; ctx.body = app.config; const result = await ctx.service.sendmsg.sendOut(); Ctx.logger. info(' active trigger, send template message result: %j', result); ctx.body = result; ctx.set('Content-Type', 'application/json'); }}Copy the code

Service service layer

// const moment = require('moment'); Class sendMsg extends Service {// Async sendOut() {const {CTX, app} = this; const token = await this.getToken(); const data = await this.getTemplateData(); Ctx.logger. info(' token result: %j', token); // Const users = app.config.wechat. Users; const promise = users.map(id => { CTX. Logger. The info (' -- -- -- -- -- -- -- -- -- -- -- -- -- -- start sending a daily reminder -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- : % j ', id); data.touser = id; return this.toWechart(token, data); }); const results = await Promise.all(promise); CTX. Logger. The info (' -- -- -- -- -- -- -- -- -- -- -- -- -- -- end send a daily reminder - > result -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- : % j ', the results). return results; } // Async toWechart(token, data) {... } // getToken async getToken() {... } // Assemble template message data async getTemplateData() {... } // getWeather async getWeather(city = 'city ') {... } // get the number of days before the next payday getWageDay() {... } // Get the number of days until the next wedding anniversary getMarryDay() {... } // Get the number of days until your next birthday. } // getLoveDay() {... } // getLoveYear() {... } // getBirthYear() {... } // getMarryYear() {... } // Get a daily sentence async getOneSentence() {... } // getDatetime() {... }}Copy the code

Sending template Messages

async toWechart(token, Data) {/ / template message interface document const url = 'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=' + token; const result = await this.ctx.curl(url, { method: 'POST', data, dataType: 'json', headers: { 'Content-Type': 'application/json', }, }); return result; }Copy the code

Get the Access token

async getToken() { const { app } = this; const url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + app.config.weChat.appld + '&secret=' + app.config.weChat.secret; const result = await this.ctx.curl(url, { method: 'get', dataType: 'json', }); if (result.status === 200) { return result.data.access_token; }}Copy the code

Assemble the template message data

async getTemplateData() { const { app } = this; GetWageDay == 0 wageDay // Anniversary template getMarryDay == 0 marry // Birthday template getBirthday == 0 birthday // Normal template  daily const wageDay = this.getWageDay(); const marry = this.getMarryDay(); const birthday = this.getbirthday(); const data = { topcolor: '#FF0000', data: {}, }; If (! wageDay) { data.template_id = app.config.weChat.wageDay; data.data = { dateTime: { value: this.getDatetime(), color: '#cc33cc', }, }; } else if (! Marry) {// Data. template_id = app.config.wechat. data.data = { dateTime: { value: this.getDatetime(), color: '#cc33cc', }, anniversary: { value: this.getMarryYear(), color: '#ff3399', }, year: { value: this.getLoveYear(), color: '#ff3399', }, }; } else if (! Birthday to birthday) {/ / template data. Template_id = app. Config. WeChat. Birthday; data.data = { dateTime: { value: this.getDatetime(), color: '#cc33cc', }, individual: { value: this.getBirthYear(), color: '#ff3399', }, }; } else {// Normal template data.template_id = app.config.wechat. Daily; // getWeather const getWeather = await this.getweather (); // Get a daily const message = await this.getonesentence (); data.data = { dateTime: { value: this.getDatetime(), color: '#cc33cc', }, love: { value: this.getLoveDay(), color: '#ff3399', }, wage: { value: wageDay, color: '#66ff00', }, birthday: { value: birthday, color: '#ff0033', }, marry: { value: marry, color: '#ff0033', }, wea: { value: getWeather.wea, color: '#33ff33', }, tem: { value: getWeather.tem, color: '#0066ff', }, airLevel: { value: getWeather.air_level, color: '#ff0033', }, tem1: { value: getWeather.tem1, color: '#ff0000', }, tem2: { value: getWeather.tem2, color: '#33ff33', }, win: { value: getWeather.win, color: '#3399ff', }, message: { value: message, color: '#8C8C8C', }, }; } return data; }Copy the code

For the weather

Async getWeather(city = 'city ') {const {app} = this; const url = 'https://www.tianqiapi.com/api?unescape=1&version=v6&appid=' + app.config.weather.appid + '&appsecret=' + app.config.weather.appsecret + '&city=' + city; const result = await this.ctx.curl(url, { method: 'get', dataType: 'json', }); console.log(result.status); / / "wea" : "cloudy", / / "tem" : "27", real-time temperature / / "tem1" : "27", high temperature / / "tem2" : "17", low temperature / / "win" : "west wind", / / "air_level" : If (result && result.status === 200) {return result.data; } return {city, wea: 'unknown', tem: 'unknown', tem1: 'unknown', tem2: 'unknown', win: 'unknown', win_speed: 'unknown', air_level: 'unknown'}; }Copy the code

Get the number of days until your next payday

getWageDay() { const { app } = this; const wage = app.config.time.wageDay; Wage + (total number of days in the current month -day) // Day const day = moment().date(); // const nowDayTotal = moment().daysinmonth (); // // nextmonth // const nextDayTotal = moment().month(moment().month() + 1).daysinMonth (); let resultDay = 0; if (day <= wage) { resultDay = wage - day; } else { resultDay = wage + (nowDayTotal - day); } return resultDay; }Copy the code

Get the number of days until your next wedding anniversary

getMarryDay() { const { app } = this; const marry = app.config.time.marry; // Get the current timestamp const now = moment(moment().format('YYYY-MM-DD')).valueof (); // Get anniversary month-day const MMDD = moment(marry). Format (' -mm-dd '); Const y = moment().year(); Const nowTimeNumber = moment(y + MMDD).valueof (); const nowTimeNumber = moment(y + MMDD).valueof (); // Determine whether today's wedding anniversary has passed (now>nowTimeNumber), resultMarry date is next year's wedding anniversary // If not, Let resultMarry = nowTimeNumber; If (now > nowTimeNumber) {// Get next year's anniversary resultMarry = moment((y + 1) + MMDD). } return moment(moment(resultMarry).format()).diff(moment(now).format(), 'day'); }Copy the code

Gets the number of days until your next birthday

getbirthday() { const { app } = this; const birthday = app.config.time.birthday[moment().year()]; // Get the current timestamp const now = moment(moment().format('YYYY-MM-DD')).valueof (); Const MMDD = moment(birthday). Format (' -mm-dd '); Const y = moment().year(); Moment (y + MMDD).valueof (); const nowTimeNumber = moment(y + MMDD).valueof (); // Now >nowTimeNumber; // Now >nowTimeNumber; // Now >nowTimeNumber; Let resultBirthday = nowTimeNumber; If (now > nowTimeNumber) {// Obtain next year target date resultBirthday = moment(app.config.time.birthday[y + 1]).valueof (); } return moment(moment(resultBirthday).format()).diff(moment(now).format(), 'day'); }Copy the code

Get love days

  getLoveDay() {
    const { app } = this;
    const loveDay = app.config.time.love;
    return moment(moment().format('YYYY-MM-DD')).diff(loveDay, 'day');
  }
Copy the code

I’ve been dating for years

  getLoveYear() {
    const { app } = this;
    const loveDay = app.config.time.love;
    return moment().year() - moment(loveDay).year();
  }
Copy the code

Get the number of birthdays

  getBirthYear() {
    const { app } = this;
    const birthYear = app.config.time.birthYear;
    return moment().year() - birthYear;
  }
Copy the code

Get the number of wedding anniversaries

  getMarryYear() {
    const { app } = this;
    const marry = app.config.time.marry;
    return moment().year() - moment(marry).year();
  }
Copy the code

Get a sentence of the day

async getOneSentence() { const url = 'https://v1.hitokoto.cn/'; const result = await this.ctx.curl(url, { method: 'get', dataType: 'json', }); if (result && result.status === 200) { return result.data.hitokoto; } return 'Only I love you today! '; }Copy the code

Get time date

getDatetime() { console.log('moment().weekday()', moment().weekday()); Const week = {1: 'on Monday, 2:' on Tuesday, 3: ' 'on Wednesday, 4:' on Thursday, 5: 'on Friday, 6:' Saturday, 0: 'Sunday'}; Return moment(). Format ('YYYY ') + week[).weekday()]; }Copy the code

Timed tasks and active triggers

Timing task

For details about setting rules, see the documentation

├ ─ ├ ─ garbage ├ ─ garbageCopy the code
Class UpdateCache extends Subscription {static get schedule() {return {cron: '0 30 7 * * *', // run at 7:30am // interval: '1m', // 1 minute interval type: 'all', // specify that all workers need to run}; Async subscribe() {const {CTX} = this; const result = await ctx.service.sendmsg.sendOut(); Ctx.logger. info(' scheduled task execution notification result: %j', result); }}Copy the code

You can view execution records of scheduled tasks in logs

├ ── impCopy the code

Take the initiative to send

Request or the browser to http://localhost:7001/send

Configuration File Description

└ ─ ─ logs └ ─ ─ config. The default. JsCopy the code

The secret key

The registered address

/ / weather config interface configuration. The weather = {appid: '* * * * * * *, appsecret:' * * * * * * * '};Copy the code

Special point in time Settings

My birthday is on the lunar calendar

// Date date marry: '2021-11-27', // Date date marry: '2021-11-27', // Date date date marry: '2021-11-27', // Date date date date: {2021: '2021-04-17', 2022, '2022-04-06', 2023, '2023-04-25', 2024, '2024-04-14', 2025, '2025-04-03', 2026: '2026-04-22',}, / / every year birthday falls birthYear: '1995-03-06'};Copy the code

Wechat public account configuration

Since individuals can only apply for subscription accounts, and subscription accounts do not support sending template messages, the test wechat public account used here can be applied for if there is a wechat signal, without registration, and can be scanned for login

No public account, quick application interface test number

Directly experience and test all advanced interfaces of the public platform

To apply for the address

/ / test WeChat public config. WeChat = {appld: '* * * * * * * * * *', secret: '* * * * * * * * * *', / / users openid users: [ '**********************', '**********************', '**********************', '**********************' ], daily: '* * * * * * * * * * * *', / / normal template Mary: '* * * * * * * * * * * *', ', / / template wageDay anniversary: '* * * * * * * * * * * *', ', / / payday template birthday: '************',', // birthday template};Copy the code

Wechat message template

This needs to be set separately on the wechat public platform test account mentioned above

Here is the template I used

Normal template

{{datetime. DATA}} Today is the {{love.DATA}} day before we pay our wages and {{{datetime. DATA}} day before your birthday and {{birthday.DATA}} day before we marry Today's weather {{wea.DATA}} Current temperature {{tem.DATA}} degree Maximum temperature {{tem1.DATA}} degree Minimum temperature {{tem2.DATA}} degree Air quality {{airlevel. DATA}} Wind direction {{win.DATA}} Daily 1  {{message.DATA}}Copy the code

Payroll template

{{datetime.data}} wife, today to pay wages, is expected to be submitted on time before 9 PM, remember to check!Copy the code

Birthday template

{{datetime. DATA}} I heard that today is your {{individual.DATA}} birthday? Oh, my God, I almost forgot! Because the years have left no mark on your face. Although, the calendar tells me: you rose a year, but you are still that innocent lovely little monster, happy birthday!Copy the code

Wedding anniversary

{{datetime. DATA}} today is the anniversary of our wedding {{anniversary.DATA}}. Our little family will always be happy.Copy the code

Display effect effect