feat: 完成简单卡片
This commit is contained in:
parent
686b3efb34
commit
3fbcef7994
@ -246,9 +246,5 @@ interface User {
|
||||
* open_id
|
||||
*/
|
||||
open_id: string;
|
||||
/**
|
||||
* 提醒列表
|
||||
*/
|
||||
remindList: string[];
|
||||
}
|
||||
```
|
133
routes/bot/actionMsg.js
Normal file
133
routes/bot/actionMsg.js
Normal file
@ -0,0 +1,133 @@
|
||||
const { genSetRemindCard, genRemindCard } = require("../../utils/genCard");
|
||||
const { getNextRemindTime } = require("../../utils/nextRemindTime");
|
||||
const {
|
||||
upsertUser,
|
||||
upsertRemindByMessageId,
|
||||
getRemindRecordByMessageId,
|
||||
getRemind,
|
||||
updateNextRemindTime,
|
||||
updateRemindRecord,
|
||||
closeRemind,
|
||||
} = require("../../utils/pb");
|
||||
const { updateCard } = require("../../utils/sendMsg");
|
||||
const { trans2LocalTime, getNowStr, getDelayedTimeStr } = require("../../utils/time");
|
||||
|
||||
/**
|
||||
* 是否为Action消息
|
||||
* @param {LarkUserAction} body
|
||||
*/
|
||||
module.exports.isActionMsg = (body) => {
|
||||
return body?.action;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取Action类型
|
||||
* @param {LarkUserAction} body
|
||||
* @returns {string} Action类型
|
||||
*/
|
||||
const getActionType = (body) => {
|
||||
return body?.action?.tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理时间选择事件
|
||||
* @param {LarkUserAction} body
|
||||
* @returns
|
||||
*/
|
||||
const manageTimePicker = async (body) => {
|
||||
const userInfo = await upsertUser(body);
|
||||
const { content } = body?.action?.value;
|
||||
if (!content) return;
|
||||
const rawTime = body?.action?.option;
|
||||
// 将'2023-09-03 10:35 +0700'转换为'2023-09-03 11:35'
|
||||
const time = trans2LocalTime(rawTime);
|
||||
// 判断选择的时间是否是将来的时间
|
||||
if (new Date(time) <= new Date()) {
|
||||
const card = genSetRemindCard(
|
||||
"pendingErr",
|
||||
content,
|
||||
getNowStr(),
|
||||
userInfo.userId
|
||||
);
|
||||
await updateCard(body.open_message_id, card);
|
||||
return;
|
||||
}
|
||||
const remindInfo = {
|
||||
owner: userInfo.id,
|
||||
messageId: body.open_message_id,
|
||||
subscriberType: "chat_id",
|
||||
subscriberId: body.open_chat_id,
|
||||
needReply: false,
|
||||
delayTime: 0,
|
||||
cardInfo: {
|
||||
title: "🍳小煎蛋提醒!",
|
||||
content: `📝 ${content}`,
|
||||
},
|
||||
frequency: "single",
|
||||
time,
|
||||
enabled: true,
|
||||
nextRemindTime: time,
|
||||
};
|
||||
// 数据库写入提醒,这里根据messageId更新或者创建是为了防止垃圾飞书不更新消息
|
||||
await upsertRemindByMessageId(remindInfo);
|
||||
// 更新卡片
|
||||
const card = genSetRemindCard("confirmed", content, time, userInfo.userId);
|
||||
await updateCard(body.open_message_id, card);
|
||||
return;
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理按钮点击事件
|
||||
* @param {LarkUserAction} body
|
||||
*/
|
||||
const manageBtnClick = async (body) => {
|
||||
const { result, type, text, remindId } = body?.action?.value;
|
||||
if (!type) return;
|
||||
// 当前点击的卡片
|
||||
const remindRecord = await getRemindRecordByMessageId(body.open_message_id);
|
||||
// 没有卡片,返回
|
||||
if (!remindRecord) return;
|
||||
// 当前卡片的提醒信息
|
||||
const remind = await getRemind(remindId)
|
||||
// 没有提醒信息,返回
|
||||
if (!remind) return;
|
||||
// 当前时间即为回复时间
|
||||
const interactTime = getNowStr()
|
||||
// 创建卡片
|
||||
const card = genRemindCard(remind, type, { type, text }, interactTime)
|
||||
// 更新飞书的卡片
|
||||
await updateCard(body.open_message_id, card)
|
||||
// 更新remindRecord
|
||||
const newRecord = {
|
||||
...remindRecord,
|
||||
status: type,
|
||||
interactTime,
|
||||
result: JSON.stringify({ type, text, result }),
|
||||
}
|
||||
await updateRemindRecord(remindRecord.id, newRecord)
|
||||
|
||||
// 如果是非延迟的单次提醒到此就结束了
|
||||
if (type !== 'delayed' && remind.frequency === 'single') {
|
||||
closeRemind(remind.id)
|
||||
return
|
||||
}
|
||||
|
||||
// 更新下一次的提醒时间
|
||||
const nextRemindTime = type === 'delayed' ? getDelayedTimeStr(remind.delayTime) : getNextRemindTime(remind)
|
||||
await updateNextRemindTime(remind.id, nextRemindTime)
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理Action消息
|
||||
* @param {LarkUserAction} body
|
||||
*/
|
||||
module.exports.manageActionMsg = async (body) => {
|
||||
const actionType = getActionType(body);
|
||||
if (actionType === 'picker_datetime') {
|
||||
manageTimePicker(body);
|
||||
}
|
||||
if (actionType === 'button') {
|
||||
manageBtnClick(body);
|
||||
}
|
||||
return;
|
||||
};
|
@ -1,4 +1,6 @@
|
||||
const { sendMsg } = require("./sendMsg")
|
||||
const { genSetRemindCard } = require("../../utils/genCard");
|
||||
const { sendMsg } = require("../../utils/sendMsg");
|
||||
const { getDelayedTimeStr } = require("../../utils/time");
|
||||
|
||||
/**
|
||||
* 获取事件文本类型
|
||||
@ -81,10 +83,33 @@ const filterGetInfoCommand = (body) => {
|
||||
return true
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getMsgType,
|
||||
getChatId,
|
||||
getMsgText,
|
||||
filterIllegalMsg,
|
||||
filterGetInfoCommand,
|
||||
}
|
||||
/**
|
||||
* 发送设置提醒卡片
|
||||
* @param {LarkMessageEvent} body
|
||||
*/
|
||||
const sendSetRemindCard = async (body) => {
|
||||
const text = getMsgText(body);
|
||||
const setRemindCard = genSetRemindCard("pending", text, getDelayedTimeStr(1));
|
||||
await sendMsg("chat_id", getChatId(body), "interactive", setRemindCard);
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理消息
|
||||
* @param {LarkMessageEvent} body
|
||||
*/
|
||||
module.exports.manageEventMsg = async (body) => {
|
||||
// 过滤非法消息
|
||||
if (filterIllegalMsg(body)) return "OK";
|
||||
// 过滤获取用户信息的指令
|
||||
if (filterGetInfoCommand(body)) return "OK";
|
||||
// 发送设置提醒卡片
|
||||
sendSetRemindCard(body);
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否为事件消息
|
||||
* @param {LarkMessageEvent} body
|
||||
*/
|
||||
module.exports.isEventMsg = (body) => {
|
||||
return body?.header?.event_type === "im.message.receive_v1";
|
||||
}
|
@ -1,49 +1,25 @@
|
||||
'use strict'
|
||||
const { genSetRemindCard } = require("../../utils/genCard")
|
||||
const { filterIllegalMsg, filterGetInfoCommand, getMsgText, getChatId } = require("../../utils/msgTools")
|
||||
const { upsertUser } = require("../../utils/pb")
|
||||
const { sendMsg } = require("../../utils/sendMsg")
|
||||
/**
|
||||
*
|
||||
* @param {LarkMessageEvent} body
|
||||
* @returns
|
||||
*/
|
||||
// const getSenderInfo = async (body) => {
|
||||
// if (body.event.sender.sender_type === 'user') {
|
||||
// // 是人发的给注册下
|
||||
// return await upsertUser(body.event.sender.sender_id)
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* 发送设置提醒卡片
|
||||
* @param {LarkMessageEvent} body
|
||||
*/
|
||||
const sendSetRemindCard = async (body) => {
|
||||
const text = getMsgText(body)
|
||||
const setRemindCard = genSetRemindCard(text)
|
||||
await sendMsg('chat_id', getChatId(body), 'interactive', setRemindCard)
|
||||
}
|
||||
const { isEventMsg, manageEventMsg } = require("./eventMsg");
|
||||
const { isActionMsg, manageActionMsg } = require("./actionMsg");
|
||||
|
||||
module.exports = async function (fastify, opts) {
|
||||
// 机器人验证及分发
|
||||
fastify.post('/', async function (request, reply) {
|
||||
console.log(JSON.stringify(request.body))
|
||||
fastify.post("/", async function (request, reply) {
|
||||
console.log(JSON.stringify(request.body));
|
||||
// 验证机器人
|
||||
if (request.body.type === 'url_verification') {
|
||||
console.log('url_verification')
|
||||
return { challenge: request.body.challenge }
|
||||
if (request.body.type === "url_verification") {
|
||||
console.log("url_verification");
|
||||
return { challenge: request.body.challenge };
|
||||
}
|
||||
// 过滤非法消息
|
||||
if (filterIllegalMsg(request.body)) return 'OK'
|
||||
// 过滤获取用户信息的指令
|
||||
if (filterGetInfoCommand(request.body)) return 'OK'
|
||||
// const userInfo = await getSenderInfo(request.body)
|
||||
// if (!userInfo) return 'OK'
|
||||
// console.log(userInfo)
|
||||
// 发送设置提醒卡片
|
||||
sendSetRemindCard(request.body)
|
||||
return 'OK'
|
||||
})
|
||||
}
|
||||
if (isEventMsg(request.body)) {
|
||||
// 处理事件消息
|
||||
manageEventMsg(request.body);
|
||||
}
|
||||
if (isActionMsg(request.body)) {
|
||||
// 处理Action消息
|
||||
manageActionMsg(request.body);
|
||||
}
|
||||
reply.send('OK')
|
||||
});
|
||||
};
|
||||
|
@ -7,8 +7,8 @@ exports.initSchedule = async () => {
|
||||
// 定时任务,每15分钟刷新一次token
|
||||
schedule.scheduleJob('*/15 * * * *', resetAccessToken);
|
||||
// 定时任务,每分钟检查一次是否有需要提醒的卡片
|
||||
// schedule.scheduleJob('* * * * *', sendCurrTimeReminds);
|
||||
schedule.scheduleJob('* * * * *', sendCurrTimeReminds);
|
||||
// 立即执行一次
|
||||
resetAccessToken()
|
||||
// sendCurrTimeReminds()
|
||||
sendCurrTimeReminds()
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
const { genRemindCard } = require("../utils/genCard")
|
||||
const { getDelayedRemindTime, getNextRemindTime } = require("../utils/nextRemindTime")
|
||||
const { getCurrRemind, updateNextRemindTime, getPendingRemindRecord, updateRemindRecord, createRemindRecord } = require("../utils/pb")
|
||||
const { getNextRemindTime } = require("../utils/nextRemindTime")
|
||||
const { getCurrRemind, updateNextRemindTime, getPendingRemindRecord, updateRemindRecord, createRemindRecord, closeRemind } = require("../utils/pb")
|
||||
const { sendMsg, updateCard } = require("../utils/sendMsg")
|
||||
const { getNowStr } = require("../utils/time")
|
||||
const { getNowStr, getDelayedTimeStr } = require("../utils/time")
|
||||
|
||||
/**
|
||||
* 更新上一个pending状态的卡片至delayed
|
||||
*/
|
||||
@ -32,14 +33,20 @@ const updateLastPendingCardToDelayed = async (remind) => {
|
||||
const manageRemind = async (remind) => {
|
||||
// 更新上一个pending状态的卡片至delayed
|
||||
await updateLastPendingCardToDelayed(remind)
|
||||
const cardType = remind.needReply ? 'pending' : 'confirmed'
|
||||
// 生成卡片
|
||||
const card = genRemindCard(remind, 'pending', null, null)
|
||||
const card = genRemindCard(remind, cardType, null, null)
|
||||
// 发送卡片
|
||||
const messageId = await sendMsg(remind.subscriberType, remind.subscriberId, 'interactive', card)
|
||||
// 创建remindRecord
|
||||
await createRemindRecord(remind.id, messageId)
|
||||
await createRemindRecord(remind.id, messageId, cardType)
|
||||
// 如果是不需要回复的单次提醒到此就结束了
|
||||
if (remind.frequency === 'single' && !remind.needReply) {
|
||||
closeRemind(remind.id)
|
||||
return
|
||||
}
|
||||
// 获取下一次提醒时间,不需要回复的直接时下一次提醒时间,需要回复的则是当前时间延后10min
|
||||
const nextRemindTime = remind.needReply ? getDelayedRemindTime(remind.delayTime) : getNextRemindTime(remind)
|
||||
const nextRemindTime = remind.needReply ? getDelayedTimeStr(remind.delayTime) : getNextRemindTime(remind)
|
||||
// 更新下一次提醒时间
|
||||
await updateNextRemindTime(remind.id, nextRemindTime)
|
||||
}
|
||||
|
74
typings.d.ts
vendored
74
typings.d.ts
vendored
@ -10,11 +10,11 @@ interface User {
|
||||
* 用户名
|
||||
* @example zhaoyingbo
|
||||
*/
|
||||
user_id: string;
|
||||
userId: string;
|
||||
/**
|
||||
* open_id
|
||||
*/
|
||||
open_id: string;
|
||||
openId: string;
|
||||
/**
|
||||
* 提醒列表
|
||||
*/
|
||||
@ -33,6 +33,10 @@ interface Remind {
|
||||
* 所有者信息,绑定用户表的id
|
||||
*/
|
||||
owner: string;
|
||||
/**
|
||||
* 消息Id
|
||||
*/
|
||||
messageId: string;
|
||||
/**
|
||||
* 接收者类型
|
||||
*/
|
||||
@ -128,19 +132,19 @@ interface Remind {
|
||||
| "workday"
|
||||
| "holiday";
|
||||
/**
|
||||
* 提醒时间,格式为HH:mm
|
||||
* 提醒时间,格式为HH:mm, single类型时仅作展示用,类型为yyyy-MM-dd HH:mm
|
||||
*/
|
||||
time: string;
|
||||
/**
|
||||
* 星期几,当frequency为weekly时有效
|
||||
* 星期几[1-7],当frequency为weekly时有效
|
||||
*/
|
||||
dayOfWeek: number;
|
||||
/**
|
||||
* 每月的几号,当frequency为monthly时有效
|
||||
* 每月的几号[1-31],当frequency为monthly时有效
|
||||
*/
|
||||
dayOfMonth: number;
|
||||
/**
|
||||
* 每年的哪天提醒,当frequency为yearly时有效,格式为MM-dd
|
||||
* 每年的哪天提醒,当frequency为 yearly 时有效,格式为MM-dd
|
||||
*/
|
||||
dayOfYear: string;
|
||||
/**
|
||||
@ -358,4 +362,62 @@ interface LarkMessageEvent {
|
||||
* 事件详情
|
||||
*/
|
||||
event: Event;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户Action信息
|
||||
*/
|
||||
interface LarkUserAction {
|
||||
/**
|
||||
* open_id
|
||||
*/
|
||||
open_id: string;
|
||||
/**
|
||||
* 用户名
|
||||
* @example zhaoyingbo
|
||||
*/
|
||||
user_id: string;
|
||||
/**
|
||||
* 当前消息的ID
|
||||
* @example om_038fc0eceed6224a1abc1cdaa4266405
|
||||
*/
|
||||
open_message_id: string;
|
||||
/**
|
||||
* 对话流ID
|
||||
* @example oc_433b1cb7a9dbb7ebe70a4e1a59cb8bb1
|
||||
*/
|
||||
open_chat_id: string;
|
||||
/**
|
||||
* 应用ID
|
||||
* @example 2ee61fe50f4f1657
|
||||
*/
|
||||
tenant_key: string;
|
||||
/**
|
||||
* token
|
||||
* @example tV9djUKSjzVnekV7xTg2Od06NFTcsBnj
|
||||
*/
|
||||
token: string;
|
||||
/**
|
||||
* 事件结果
|
||||
*/
|
||||
action: {
|
||||
/**
|
||||
* 传的参数
|
||||
*/
|
||||
value: any;
|
||||
/**
|
||||
* 标签名
|
||||
* @example picker_datetime
|
||||
*/
|
||||
tag: string;
|
||||
/**
|
||||
* 选择的事件
|
||||
* @example 2023-09-03 10:35 +0800
|
||||
*/
|
||||
option: string;
|
||||
/**
|
||||
* 时区
|
||||
*/
|
||||
timezone: string;
|
||||
};
|
||||
}
|
166
utils/genCard.js
166
utils/genCard.js
@ -1,5 +1,3 @@
|
||||
const { getNowStr } = require("./time");
|
||||
|
||||
/**
|
||||
* 生成提醒时间文本
|
||||
*/
|
||||
@ -46,14 +44,14 @@ const genRemindTimeText = (
|
||||
const genResult = (interactInfo, cardInfo, cardType) => {
|
||||
// 优先卡片返回的信息
|
||||
if (interactInfo) {
|
||||
const { result, text } = interactInfo;
|
||||
const { type, text } = interactInfo;
|
||||
if (text) return text;
|
||||
return {
|
||||
pending: "未交互",
|
||||
confirmed: "已确认",
|
||||
delayed: "已延期",
|
||||
canceled: "已取消",
|
||||
}[result];
|
||||
}[type];
|
||||
}
|
||||
if (!cardInfo) {
|
||||
return {
|
||||
@ -75,7 +73,7 @@ const genResult = (interactInfo, cardInfo, cardType) => {
|
||||
/**
|
||||
* 生成交互按钮
|
||||
*/
|
||||
const genActions = (cardInfo, cardType, needReply) => {
|
||||
const genActions = (id, cardInfo, cardType, needReply) => {
|
||||
// 如果非交互提醒,不显示按钮
|
||||
if (!needReply) return null;
|
||||
// 如果是交互卡片的完成交互状态,不显示按钮
|
||||
@ -95,8 +93,9 @@ const genActions = (cardInfo, cardType, needReply) => {
|
||||
canceled: "danger",
|
||||
}[type],
|
||||
value: {
|
||||
result: type,
|
||||
type,
|
||||
text,
|
||||
remindId: id,
|
||||
},
|
||||
});
|
||||
// 需要回复的卡片,显示确认、延期、取消按钮
|
||||
@ -169,6 +168,7 @@ module.exports.genRemindCard = (
|
||||
interactTime
|
||||
) => {
|
||||
const {
|
||||
id,
|
||||
cardInfo,
|
||||
templateInfo,
|
||||
needReply,
|
||||
@ -255,7 +255,7 @@ module.exports.genRemindCard = (
|
||||
}
|
||||
: null;
|
||||
// 待交互卡片且需要回复的显示按钮
|
||||
const actionsDom = genActions(cardInfo, cardType, needReply);
|
||||
const actionsDom = genActions(id, cardInfo, cardType, needReply);
|
||||
// 非交互卡片以及交互完卡片,需要显示提醒信息
|
||||
const remindInfoDom = genRemindInfo(
|
||||
needReply,
|
||||
@ -280,43 +280,143 @@ module.exports.genRemindCard = (
|
||||
elements.push(remindInfoDom);
|
||||
}
|
||||
return JSON.stringify({
|
||||
config: {
|
||||
enable_forward: true,
|
||||
update_multi: true,
|
||||
},
|
||||
elements,
|
||||
header,
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.genSetRemindCard = (text) => {
|
||||
return JSON.stringify({
|
||||
elements: [
|
||||
/**
|
||||
* 生成设置提醒卡片
|
||||
* @param {string} cardType 卡片类型
|
||||
* @param {*} content 卡片内容
|
||||
*/
|
||||
module.exports.genSetRemindCard = (cardType, content, time, userId) => {
|
||||
const action = {
|
||||
tag: "action",
|
||||
actions: [
|
||||
{
|
||||
tag: "div",
|
||||
text: {
|
||||
content: `📝 ${text}`,
|
||||
tag: "lark_md",
|
||||
tag: "picker_datetime",
|
||||
placeholder: {
|
||||
tag: "plain_text",
|
||||
content: "请选择提醒时间",
|
||||
},
|
||||
},
|
||||
{
|
||||
tag: "action",
|
||||
actions: [
|
||||
{
|
||||
tag: "picker_datetime",
|
||||
placeholder: {
|
||||
tag: "plain_text",
|
||||
content: "请选择提醒时间",
|
||||
},
|
||||
value: {
|
||||
content: text,
|
||||
},
|
||||
initial_datetime: getNowStr(),
|
||||
},
|
||||
],
|
||||
layout: "bisected",
|
||||
value: {
|
||||
content: `${content}`,
|
||||
},
|
||||
initial_datetime: time,
|
||||
},
|
||||
],
|
||||
layout: "bisected",
|
||||
};
|
||||
const tip = [
|
||||
{
|
||||
tag: "hr",
|
||||
},
|
||||
{
|
||||
tag: "note",
|
||||
elements: [
|
||||
{
|
||||
tag: "img",
|
||||
img_key: "img_v2_19db22c1-0030-434b-9b54-2a53b99c5f3l",
|
||||
alt: {
|
||||
tag: "plain_text",
|
||||
content: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
tag: "plain_text",
|
||||
content: "谁选的时间提醒挂在谁名下呦",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const remindInfo = [
|
||||
{
|
||||
tag: "column_set",
|
||||
flex_mode: "none",
|
||||
background_style: "grey",
|
||||
columns: [
|
||||
{
|
||||
tag: "column",
|
||||
width: "weighted",
|
||||
weight: 1,
|
||||
vertical_align: "top",
|
||||
elements: [
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**创建人**\n${userId}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
tag: "column",
|
||||
width: "weighted",
|
||||
weight: 1,
|
||||
vertical_align: "top",
|
||||
elements: [
|
||||
{
|
||||
tag: "markdown",
|
||||
content: `**提醒时间**\n${time}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const errTip = {
|
||||
tag: "note",
|
||||
elements: [
|
||||
{
|
||||
tag: "plain_text",
|
||||
content: "🚫请选择将来的时间哦!",
|
||||
},
|
||||
],
|
||||
};
|
||||
const elements = [
|
||||
{
|
||||
tag: "div",
|
||||
text: {
|
||||
content: `📝 ${content}`,
|
||||
tag: "lark_md",
|
||||
},
|
||||
},
|
||||
];
|
||||
if (cardType === "pendingErr") {
|
||||
elements.push(action);
|
||||
elements.push(errTip);
|
||||
elements.push(...tip);
|
||||
}
|
||||
if (cardType === "pending") {
|
||||
elements.push(action);
|
||||
elements.push(...tip);
|
||||
}
|
||||
if (cardType === "confirmed") {
|
||||
elements.push(...remindInfo);
|
||||
}
|
||||
return JSON.stringify({
|
||||
config: {
|
||||
enable_forward: true,
|
||||
update_multi: true,
|
||||
},
|
||||
elements,
|
||||
header: {
|
||||
template: "turquoise",
|
||||
template: {
|
||||
pending: "turquoise",
|
||||
pendingErr: "turquoise",
|
||||
confirmed: "green",
|
||||
}[cardType],
|
||||
title: {
|
||||
content: "✨小煎蛋提醒创建工具",
|
||||
content: `✨小煎蛋提醒创建${
|
||||
{
|
||||
pending: "工具",
|
||||
pendingErr: "工具",
|
||||
confirmed: "成功",
|
||||
}[cardType]
|
||||
}`,
|
||||
tag: "plain_text",
|
||||
},
|
||||
},
|
||||
|
@ -1,3 +1,6 @@
|
||||
const { getNowDateStr } = require("./time");
|
||||
const moment = require('moment')
|
||||
|
||||
const chineseHoliday = {
|
||||
'2023-09-29': true,
|
||||
'2023-09-30': true,
|
||||
@ -18,40 +21,17 @@ const chineseHoliday = {
|
||||
* @returns {boolean} 是否是工作日
|
||||
*/
|
||||
const judgeIsWorkDay = (time) => {
|
||||
const dateStr = getNowDateStr(time)
|
||||
// 是补班
|
||||
if (chineseHoliday[dateStr] === false) return true;
|
||||
// 是假期
|
||||
if (chineseHoliday[dateStr]) return false;
|
||||
// 是否是日常工作日
|
||||
const now = time ? new Date(time) : new Date();
|
||||
const nowDay = now.getDay();
|
||||
const nowDate = now.getDate();
|
||||
const nowMonth = now.getMonth();
|
||||
const nowYear = now.getFullYear();
|
||||
// 是否是调休
|
||||
const isHoliday = chineseHoliday[`${nowYear}-${nowMonth + 1}-${nowDate}`]
|
||||
if (isHoliday) return false;
|
||||
// 是否是补班
|
||||
const isMakeUpWorkday = chineseHoliday[`${nowYear}-${nowMonth + 1}-${nowDate + 1}`] === false
|
||||
if (isMakeUpWorkday) return true;
|
||||
// 是否是日常工作日
|
||||
return nowDay !== 0 && nowDay !== 6
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一个工作日或者非工作日日期
|
||||
* @param {boolean} isWorkday 是否是工作日
|
||||
* @returns {string} 下一个工作日日期,格式yyyy-MM-dd
|
||||
*/
|
||||
const getNextday = (isWorkday = true) => {
|
||||
const now = new Date();
|
||||
let dayCount = 1;
|
||||
let nextDay = new Date(now.getTime() + dayCount * 24 * 60 * 60 * 1000)
|
||||
while ((isWorkday && !judgeIsWorkDay(nextDay)) || (!isWorkday && judgeIsWorkDay(nextDay))) {
|
||||
dayCount++;
|
||||
nextDay = new Date(now.getTime() + dayCount * 24 * 60 * 60 * 1000)
|
||||
}
|
||||
const nextWorkdayYear = nextDay.getFullYear();
|
||||
const nextWorkdayMonth = nextDay.getMonth() + 1;
|
||||
const nextWorkdayDate = nextDay.getDate();
|
||||
return `${nextWorkdayYear}-${nextWorkdayMonth}-${nextWorkdayDate}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下次提醒时间
|
||||
* @param {Remind} remind 提醒信息
|
||||
@ -59,92 +39,88 @@ const getNextday = (isWorkday = true) => {
|
||||
*/
|
||||
module.exports.getNextRemindTime = (remind) => {
|
||||
const { frequency, time, dayOfWeek, dayOfMonth, dayOfYear } = remind;
|
||||
const now = new Date();
|
||||
const nowDay = now.getDay();
|
||||
const nowDate = now.getDate();
|
||||
const nowMonth = now.getMonth();
|
||||
const nowYear = now.getFullYear();
|
||||
const nowHour = now.getHours();
|
||||
const nowMinute = now.getMinutes();
|
||||
|
||||
// 单次提醒没有下次,正常会取delayTime
|
||||
if (remind.frequency === 'single') return remind.nextRemindTime
|
||||
// 拆分时间
|
||||
const [hour, minute] = time.split(':');
|
||||
// 当前时间
|
||||
const nowMoment = moment();
|
||||
// 每天循环
|
||||
if (frequency === 'daily') {
|
||||
const remindMoment = moment().hour(hour).minute(minute);
|
||||
// 判断当前时间是否已经过了今天的提醒时间
|
||||
if (nowHour > time.split(':')[0] || (nowHour === time.split(':')[0] && nowMinute >= time.split(':')[1])) {
|
||||
if (!remindMoment.isAfter(nowMoment)) {
|
||||
// 如果已经过了,那么下次提醒时间为明天的提醒时间
|
||||
return `${nowYear}-${nowMonth + 1}-${nowDate + 1} ${time}`
|
||||
} else {
|
||||
// 否则下次提醒时间为今天的提醒时间
|
||||
return `${nowYear}-${nowMonth + 1}-${nowDate} ${time}`
|
||||
remindMoment.add(1, 'day');
|
||||
}
|
||||
return remindMoment.format('YYYY-MM-DD HH:mm');
|
||||
}
|
||||
|
||||
// 每周循环
|
||||
if (frequency === 'weekly') {
|
||||
const remindMoment = moment().hour(hour).minute(minute).isoWeekday(dayOfWeek);
|
||||
// 判断当前时间是否已经过了本周的提醒时间
|
||||
if (nowDay > dayOfWeek || (nowDay === dayOfWeek && (nowHour > time.split(':')[0] || (nowHour === time.split(':')[0] && nowMinute >= time.split(':')[1])))) {
|
||||
if (!remindMoment.isAfter(nowMoment)) {
|
||||
// 如果已经过了,那么下次提醒时间为下周的提醒时间
|
||||
return `${nowYear}-${nowMonth + 1}-${nowDate + 7 - nowDay + dayOfWeek} ${time}`
|
||||
} else {
|
||||
// 否则下次提醒时间为本周的提醒时间
|
||||
return `${nowYear}-${nowMonth + 1}-${nowDate + dayOfWeek - nowDay} ${time}`
|
||||
remindMoment.add(1, 'week');
|
||||
}
|
||||
return remindMoment.format('YYYY-MM-DD HH:mm');
|
||||
}
|
||||
|
||||
// 每月循环
|
||||
if (frequency === 'monthly') {
|
||||
// 获取最近应该提醒的日期,如果一个月没有31天,那么就是最后一天
|
||||
const dayOfMonthNum = moment().daysInMonth();
|
||||
const remindMoment = moment().hour(hour).minute(minute).date(dayOfMonthNum < dayOfMonth ? dayOfMonthNum : dayOfMonth);
|
||||
// 判断当前时间是否已经过了本月的提醒时间
|
||||
if (nowDate > dayOfMonth || (nowDate === dayOfMonth && (nowHour > time.split(':')[0] || (nowHour === time.split(':')[0] && nowMinute >= time.split(':')[1])))) {
|
||||
// 如果已经过了,那么下次提醒时间为下月的提醒时间
|
||||
return `${nowYear}-${nowMonth + 2}-${dayOfMonth} ${time}`
|
||||
} else {
|
||||
// 否则下次提醒时间为本月的提醒时间
|
||||
return `${nowYear}-${nowMonth + 1}-${dayOfMonth} ${time}`
|
||||
if (!remindMoment.isAfter(nowMoment)) {
|
||||
// 如果已经过了,那么下次提醒时间为下月的提醒时间,需要重新判断一下是否有31号
|
||||
const nextDayOfMonthNum = moment().add(1, 'month').daysInMonth();
|
||||
remindMoment.add(1, 'month').date(nextDayOfMonthNum < dayOfMonth ? nextDayOfMonthNum : dayOfMonth);
|
||||
}
|
||||
return remindMoment.format('YYYY-MM-DD HH:mm');
|
||||
}
|
||||
|
||||
// 每年循环
|
||||
if (frequency === 'yearly') {
|
||||
// 月份,monont是从0开始
|
||||
const month = dayOfYear.split('-')[0] - 1;
|
||||
// 月份的日期
|
||||
const dayOfMonth = dayOfYear.split('-')[1];
|
||||
// 获取当月的时间,如果当月没有29号,那么就是最后一天
|
||||
const dayOfMonthNum = moment().month(month).daysInMonth();
|
||||
const remindMoment = moment().hour(hour).minute(minute).month(month).date(dayOfMonthNum < dayOfMonth ? dayOfMonthNum : dayOfMonth);
|
||||
// 判断当前时间是否已经过了今年的提醒时间
|
||||
if (nowMonth > parseInt(dayOfYear.split('-')[0]) || (nowMonth === parseInt(dayOfYear.split('-')[0]) && (nowDate > parseInt(dayOfYear.split('-')[1]) || (nowDate === parseInt(dayOfYear.split('-')[1]) && (nowHour > time.split(':')[0] || (nowHour === time.split(':')[0] && nowMinute >= time.split(':')[1])))))) {
|
||||
// 如果已经过了,那么下次提醒时间为明年的提醒时间
|
||||
return `${nowYear + 1}-${dayOfYear} ${time}`
|
||||
} else {
|
||||
// 否则下次提醒时间为今年的提醒时间
|
||||
return `${nowYear}-${dayOfYear} ${time}`
|
||||
if (!remindMoment.isAfter(nowMoment)) {
|
||||
// 如果已经过了,那么下次提醒时间为明年的提醒时间,需要重新判断一下是否有29号
|
||||
const nextDayOfMonthNum = moment().add(1, 'year').month(month).daysInMonth();
|
||||
remindMoment.add(1, 'year').date(nextDayOfMonthNum < dayOfMonth ? nextDayOfMonthNum : dayOfMonth);
|
||||
}
|
||||
return remindMoment.format('YYYY-MM-DD HH:mm');
|
||||
}
|
||||
|
||||
// 工作日循环
|
||||
if (frequency === 'workday') {
|
||||
// 获取今天是不是工作日
|
||||
const isWorkday = judgeIsWorkDay()
|
||||
// 如果是工作日,且当前时间没过提醒时间,那么下次提醒时间为今天的提醒时间
|
||||
if (isWorkday && (nowHour < time.split(':')[0] || (nowHour === time.split(':')[0] && nowMinute < time.split(':')[1]))) {
|
||||
return `${nowYear}-${nowMonth + 1}-${nowDate} ${time}`
|
||||
const remindMoment = moment().hour(hour).minute(minute);
|
||||
// 今天是否是工作日
|
||||
const isWorkday = judgeIsWorkDay();
|
||||
// 如果今天非工作日或者如果是工作日,且当前时间过了提醒时间,那么下次提醒时间为下次工作日的提醒时间
|
||||
if (!isWorkday || (isWorkday && !remindMoment.isAfter(nowMoment))) {
|
||||
remindMoment.add(1, 'day');
|
||||
while (!judgeIsWorkDay(remindMoment)) {
|
||||
remindMoment.add(1, 'day');
|
||||
}
|
||||
}
|
||||
// 非工作日或者已经过了提醒时间,那么下次提醒时间为下一个工作日的提醒时间
|
||||
return `${getNextday()} ${time}`
|
||||
return remindMoment.format('YYYY-MM-DD HH:mm');
|
||||
}
|
||||
|
||||
// 非工作日循环
|
||||
if (frequency === 'holiday') {
|
||||
// 获取今天是不是工作日
|
||||
const isWorkday = judgeIsWorkDay()
|
||||
// 如果是非工作日,且当前时间没过提醒时间,那么下次提醒时间为今天的提醒时间
|
||||
if (!isWorkday && (nowHour < time.split(':')[0] || (nowHour === time.split(':')[0] && nowMinute < time.split(':')[1]))) {
|
||||
return `${nowYear}-${nowMonth + 1}-${nowDate} ${time}`
|
||||
const remindMoment = moment().hour(hour).minute(minute);
|
||||
// 今天是否是工作日
|
||||
const isWorkday = judgeIsWorkDay();
|
||||
// 如果今天是工作日或者如果是非工作日,且当前时间过了提醒时间,那么下次提醒时间为下次非工作日的提醒时间
|
||||
if (isWorkday || (!isWorkday && !remindMoment.isAfter(nowMoment))) {
|
||||
remindMoment.add(1, 'day');
|
||||
while (judgeIsWorkDay(remindMoment)) {
|
||||
remindMoment.add(1, 'day');
|
||||
}
|
||||
}
|
||||
// 工作日或者已经过了提醒时间,那么下次提醒时间为下一个非工作日的提醒时间
|
||||
return `${getNextday(false)} ${time}`
|
||||
return remindMoment.format('YYYY-MM-DD HH:mm');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取delayTime分钟delay之后的提醒时间
|
||||
* @param {number} delayTime 延迟时间, 单位为分钟, 1 <= delayTime
|
||||
* @returns {string} delay的提醒时间, 格式为yyyy-MM-dd HH:mm
|
||||
*/
|
||||
module.exports.getDelayedRemindTime = (delayTime) => {
|
||||
const nextRemindTime = new Date(new Date().getTime() + delayTime * 60 * 1000)
|
||||
const nextRemindYear = nextRemindTime.getFullYear();
|
||||
const nextRemindMonth = nextRemindTime.getMonth() + 1;
|
||||
const nextRemindDate = nextRemindTime.getDate();
|
||||
const nextRemindHour = nextRemindTime.getHours();
|
||||
const nextRemindMinute = nextRemindTime.getMinutes();
|
||||
return `${nextRemindYear}-${nextRemindMonth}-${nextRemindDate} ${nextRemindHour}:${nextRemindMinute}`
|
||||
}
|
113
utils/pb.js
113
utils/pb.js
@ -22,6 +22,45 @@ module.exports.getTenantAccessToken = async () => {
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新提醒
|
||||
*/
|
||||
module.exports.createRemind = async (remind) => {
|
||||
await pb.collection('remind').create(remind)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提醒信息
|
||||
*/
|
||||
module.exports.getRemind = async (id) => {
|
||||
try {
|
||||
return await pb.collection('remind').getOne(id)
|
||||
} catch (err) {
|
||||
// 没有这个提醒就返回空
|
||||
if (err.message === "The requested resource wasn't found.") {
|
||||
return null
|
||||
} else throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据messageId更新或者创建提醒
|
||||
* @param {Remind} remind
|
||||
*/
|
||||
module.exports.upsertRemindByMessageId = async (remind) => {
|
||||
try {
|
||||
const record = await pb.collection('remind').getFirstListItem(
|
||||
`messageId = "${remind.messageId}" && enabled = true`
|
||||
)
|
||||
await pb.collection('remind').update(record.id, remind)
|
||||
} catch (err) {
|
||||
// 没有这个提醒就创建
|
||||
if (err.message === "The requested resource wasn't found.") {
|
||||
await pb.collection('remind').create(remind)
|
||||
} else throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前分钟应该提醒的所有提醒
|
||||
*/
|
||||
@ -34,37 +73,88 @@ module.exports.getCurrRemind = async () => {
|
||||
return items
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对应messageId对应的提醒
|
||||
*/
|
||||
module.exports.getRemindByMessageId = async (messageId) => {
|
||||
try {
|
||||
const record = await pb.collection('remind').getFirstListItem(
|
||||
`messageId = "${messageId}" && enabled = true`
|
||||
)
|
||||
return record
|
||||
} catch (err) {
|
||||
// 没有这个提醒不返回
|
||||
if (err.message === "The requested resource wasn't found.") {
|
||||
return null
|
||||
} else throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定Remind的下次提醒时间
|
||||
* @param {Remind} remindId 提醒信息Id
|
||||
* @param {string} remindId 提醒信息Id
|
||||
* @param {string} nextRemindTime 下次提醒时间
|
||||
*/
|
||||
module.exports.updateNextRemindTime = async (remindId, nextRemindTime) => {
|
||||
await pb.collection('remind').update(remindId, { nextRemindTime })
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭指定的Remind
|
||||
* @param {string} remindId 提醒信息Id
|
||||
*/
|
||||
module.exports.closeRemind = async (remindId) => {
|
||||
await pb.collection('remind').update(remindId, { enabled: false })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定remind的pending状态的remindRecord
|
||||
* @param {string} remindId remind的id
|
||||
* @returns {RemindRecord | null} remindRecord
|
||||
*/
|
||||
module.exports.getPendingRemindRecord = async (remindId) => {
|
||||
const record = await pb.collection('remindRecord').getFirstListItem(
|
||||
`remindId = "${remindId}" && status = "pending"`
|
||||
)
|
||||
return record
|
||||
try {
|
||||
const record = await pb.collection('remindRecord').getFirstListItem(
|
||||
`remindId = "${remindId}" && status = "pending"`
|
||||
)
|
||||
return record
|
||||
} catch (err) {
|
||||
// 没有这个record不返回
|
||||
if (err.message === "The requested resource wasn't found.") {
|
||||
return null
|
||||
} else throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定messageId状态的remindRecord
|
||||
* @param {string} remindId remind的id
|
||||
* @returns {RemindRecord | null} remindRecord
|
||||
*/
|
||||
module.exports.getRemindRecordByMessageId = async (messageId) => {
|
||||
try {
|
||||
const record = await pb.collection('remindRecord').getFirstListItem(
|
||||
`messageId = "${messageId}"`
|
||||
)
|
||||
return record
|
||||
} catch (err) {
|
||||
// 没有这个record不返回
|
||||
if (err.message === "The requested resource wasn't found.") {
|
||||
return null
|
||||
} else throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建remindRecord
|
||||
*/
|
||||
module.exports.createRemindRecord = async (remindId, messageId) => {
|
||||
module.exports.createRemindRecord = async (remindId, messageId, status) => {
|
||||
const remindRecord = {
|
||||
remindId,
|
||||
messageId,
|
||||
status: 'pending',
|
||||
status: status,
|
||||
remindTime: getNowStr(),
|
||||
interactTime: '',
|
||||
interactTime: status === 'pending' ? '' : getNowStr(),
|
||||
result: '',
|
||||
}
|
||||
await pb.collection('remindRecord').create(remindRecord)
|
||||
@ -92,11 +182,10 @@ const getUser = async (userId) => {
|
||||
* 创建用户
|
||||
* @param {string} userId 用户id
|
||||
* @param {string} openId 用户openId
|
||||
* @param {[]} remindList 用户提醒列表
|
||||
* @returns {User} 用户信息
|
||||
*/
|
||||
const createUser = async (userId, openId, remindList) => {
|
||||
return await pb.collection("user").create({ userId, openId, remindList })
|
||||
const createUser = async (userId, openId) => {
|
||||
return await pb.collection("user").create({ userId, openId })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,7 +217,7 @@ module.exports.upsertUser = async (userInfo) => {
|
||||
} catch (err) {
|
||||
// 没有这个用户上传个新的
|
||||
if (err.message === "The requested resource wasn't found.") {
|
||||
const user = await createUser(userInfo.user_id, userInfo.open_id, []);
|
||||
const user = await createUser(userInfo.user_id, userInfo.open_id);
|
||||
return user;
|
||||
} else throw err;
|
||||
}
|
||||
|
@ -8,3 +8,39 @@ const moment = require('moment')
|
||||
module.exports.getNowStr = () => {
|
||||
return moment().format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前日期的字符串
|
||||
* @param {string} time 时间
|
||||
* @returns {string} 当前日期的字符串 YYYY-MM-DD
|
||||
*/
|
||||
module.exports.getNowDateStr = (time) => {
|
||||
const targetTime = time ? new Date(time).getTime() : new Date().getTime()
|
||||
return moment(targetTime).format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定分钟后的时间字符串
|
||||
* @param {number} minutes 分钟数
|
||||
* @returns {string} 指定分钟后的时间字符串 YYYY-MM-DD HH:mm
|
||||
*/
|
||||
module.exports.getDelayedTimeStr = (minutes) => {
|
||||
return moment().add(minutes, 'minutes').format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
|
||||
/**
|
||||
* 转化为当前时区的时间
|
||||
* @returns {string} 当前时间的字符串 YYYY-MM-DD HH:mm
|
||||
* @example 2020-12-12 12:12
|
||||
*/
|
||||
module.exports.trans2LocalTime = (time) => {
|
||||
return moment(new Date(time).getTime()).format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
|
||||
const test = () => {
|
||||
const dayOfMonth = 31;
|
||||
const dayOfMonthNum = moment().daysInMonth();
|
||||
return moment().date(dayOfMonthNum < dayOfMonth ? dayOfMonthNum : dayOfMonth).format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
|
||||
// console.log(test())
|
Loading…
x
Reference in New Issue
Block a user