Skip to content

Commit 7b3765c

Browse files
authored
feat: send custom message to dingtalk rebot (#69)
* feat: table column optimize * feat: send custom message to dingtalk rebot * feat: markdown transferred meaning
1 parent 278422d commit 7b3765c

File tree

9 files changed

+152
-59
lines changed

9 files changed

+152
-59
lines changed

app/controller/articleSubscription.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,15 @@ class ArticleSubscriptionController extends Controller {
2424
// 新增
2525
async createSubscription() {
2626
const { app, ctx } = this;
27-
const { groupName, webHook, remark, topicIds, siteNames, sendType, sendCron, time, status } = ctx.request.body;
27+
const { groupName, webHook, remark, topicIds = [], siteNames, sendType, sendCron, time, status, messageTitle, message, isAtAll } = ctx.request.body;
2828
if (_.isNil(groupName)) throw new Error('缺少必要参数 groupName');
2929
if (_.isNil(webHook)) throw new Error('缺少必要参数 webHook');
30-
if (_.isNil(topicIds)) throw new Error('缺少必要参数 topicIds');
3130
if (_.isNil(siteNames)) throw new Error('缺少必要参数 siteNames');
3231
if (_.isNil(sendType)) throw new Error('缺少必要参数 sendType');
3332
if (_.isNil(sendCron)) throw new Error('缺少必要参数 sendCron');
3433
if (_.isNil(time)) throw new Error('缺少必要参数 time');
3534
// 数据库插入数据
36-
const data = await ctx.service.articleSubscription.createSubscription({ groupName, webHook, remark, topicIds: topicIds.join(','), siteNames, sendType, sendCron, time, status });
35+
const data = await ctx.service.articleSubscription.createSubscription({ groupName, webHook, remark, topicIds: topicIds?.join(','), siteNames, sendType, sendCron, time, status, messageTitle, message, isAtAll });
3736
const { id } = data
3837
if (_.isNil(id)) throw new Error('创建失败');
3938
// 向 agent 进程发消息
@@ -44,15 +43,14 @@ class ArticleSubscriptionController extends Controller {
4443
// 更新
4544
async updateSubscription() {
4645
const { ctx, app } = this;
47-
const { id, groupName, webHook, remark, topicIds, siteNames, sendType, sendCron, time, status } = ctx.request.body;
46+
const { id, groupName, webHook, remark, topicIds = [], siteNames, sendType, sendCron, time, status, messageTitle, message, isAtAll } = ctx.request.body;
4847
if (_.isNil(id)) throw new Error('缺少必要参数 id');
4948
if (_.isNil(groupName)) throw new Error('缺少必要参数 groupName');
5049
if (_.isNil(webHook)) throw new Error('缺少必要参数 webHook');
51-
if (_.isNil(topicIds)) throw new Error('缺少必要参数 topicIds');
5250
if (_.isNil(siteNames)) throw new Error('缺少必要参数 siteNames');
5351
if (_.isNil(sendType)) throw new Error('缺少必要参数 sendType');
5452
if (_.isNil(sendCron)) throw new Error('缺少必要参数 sendCron');
55-
await ctx.service.articleSubscription.updateSubscription(id, { groupName, webHook, remark, topicIds: topicIds.join(','), siteNames, sendType, sendCron, time, status, updated_at: new Date() })
53+
await ctx.service.articleSubscription.updateSubscription(id, { groupName, webHook, remark, topicIds: topicIds?.join(','), siteNames, sendType, sendCron, time, status, messageTitle, message, isAtAll, updated_at: new Date() })
5654
// 向 agent 进程发消息
5755
app.messenger.sendToAgent(status === 1 ? 'changeTimedTask' : 'cancelTimedTask', { id, sendCron })
5856
ctx.body = app.utils.response(true, id);

app/model/article_subscription.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ module.exports = app => {
1313
status: { type: INTEGER(2), defaultValue: 1 },
1414
is_delete: { type: INTEGER(2), defaultValue: 0 },
1515
created_at: DATE,
16-
updated_at: DATE
16+
updated_at: DATE,
17+
messageTitle: STRING(64),
18+
message: STRING(2000),
19+
isAtAll: { type: INTEGER(2), defaultValue: 0 }
1720
},{
1821
freezeTableName: true
1922
});

app/service/articleSubscription.js

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const Service = require('egg').Service
2-
const { getGithubTrendingFromServerless, getJueJinHot } = require('../utils/articleSubscription')
2+
const { getGithubTrendingFromServerless, getJueJinHot, customMessage } = require('../utils/articleSubscription')
33

44
class ArticleSubscriptionService extends Service {
55
// 获取列表
@@ -33,22 +33,26 @@ class ArticleSubscriptionService extends Service {
3333

3434
// 通过订阅 id 查询需要发送的文章订阅消息
3535
async sendArticleSubscription(id) {
36-
const topicAll = await this.app.model.ArticleTopic.findAll({ where: { is_delete: 0 }, raw: true })
3736
const articleSubscription = await this.app.model.ArticleSubscription.findOne({
3837
where: {
3938
id,
4039
is_delete: 0
4140
},
4241
raw: true
4342
})
44-
const { webHook, groupName } = articleSubscription
45-
const topicIds = articleSubscription.topicIds.split(',')
46-
const topicList = topicAll.filter(item => topicIds.includes(`${ item.id }`))
43+
const { webHook, groupName, siteNames, messageTitle, message, isAtAll } = articleSubscription
44+
if (siteNames === '自定义消息') {
45+
customMessage(id, groupName, siteNames, messageTitle, message, isAtAll, webHook, this.app)
46+
} else {
47+
const topicAll = await this.app.model.ArticleTopic.findAll({ where: { is_delete: 0 }, raw: true });
48+
const topicIds = articleSubscription.topicIds?.split(',') || [];
49+
const topicList = topicAll.filter(item => topicIds.includes(`${ item.id }`))
4750

48-
for (let item of topicList) {
49-
const { siteName, topicName, topicUrl } = item
50-
siteName === 'Github' && getGithubTrendingFromServerless(id, groupName, siteName, topicName, topicUrl, webHook, this.app)
51-
siteName === '掘金' && getJueJinHot(id, groupName, siteName, topicName, topicUrl, webHook, this.app)
51+
for (let item of topicList) {
52+
const { siteName, topicName, topicUrl } = item
53+
siteName === 'Github' && getGithubTrendingFromServerless(id, groupName, siteName, topicName, topicUrl, webHook, this.app)
54+
siteName === '掘金' && getJueJinHot(id, groupName, siteName, topicName, topicUrl, webHook, this.app)
55+
}
5256
}
5357
}
5458

app/utils/articleSubscription.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,16 @@ const getJueJinHot = async (id, groupName, siteName, topicName, topicUrl, webHoo
9999
}
100100
}
101101

102+
// 自定义消息
103+
const customMessage = async (id, groupName, siteName, messageTitle, message, isAtAll, webHook, app) => {
104+
try {
105+
sendArticleMsg(messageTitle, message?.replace(/\\n/g, '\n')?.replace(/\\r/g, '\r'), webHook, { isAtAll })
106+
logFunc(app, id, groupName, siteName, messageTitle, '成功')
107+
} catch (err) {
108+
logFunc(app, id, groupName, siteName, messageTitle, `失败`, `${ JSON.stringify(err) }`)
109+
}
110+
}
111+
102112
// 打印文章订阅任务结果
103113
const logFunc = (app, id, groupName, siteName, topicName, msg, errMsg = '') => {
104114
if (!articleResultWebhook) return
@@ -118,5 +128,6 @@ module.exports = {
118128
getGithubTrendingFromGithub,
119129
getGithubTrendingFromJueJin,
120130
getGithubTrendingFromServerless,
121-
getJueJinHot
131+
getJueJinHot,
132+
customMessage
122133
}

app/utils/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ const sendHostsUpdateMsg = async (webhook_url, basicInfo, ip, address, operation
5757
}
5858

5959
// 发送文章订阅消息
60-
const sendArticleMsg = async (title, text, webhook) => {
60+
const sendArticleMsg = async (title, text, webhook, at = {}) => {
6161
const feChatRobot = new ChatBot({ webhook })
6262
feChatRobot
63-
.markdown(title, text)
63+
.markdown(title, text, at)
6464
.catch(ex => console.error(ex))
6565
}
6666

app/web/pages/articleSubscription/components/ChooseSendTime/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class ChooseSendTime extends React.Component<IProps, IState> {
5050
return (
5151
<div className="send-time">
5252
<Select style={{ width: 120 }} value={sendType} onChange={(val) => { this.handleChange('sendType', val) }}>
53+
<Option value={SUBSCRIPTIONSENDTYPE.FRIDAY}>{SUBSCRIPTIONSENDTYPECN[SUBSCRIPTIONSENDTYPE.FRIDAY]}</Option>
5354
<Option value={SUBSCRIPTIONSENDTYPE.WORKDAY}>{SUBSCRIPTIONSENDTYPECN[SUBSCRIPTIONSENDTYPE.WORKDAY]}</Option>
5455
<Option value={SUBSCRIPTIONSENDTYPE.EVERYDAY}>{SUBSCRIPTIONSENDTYPECN[SUBSCRIPTIONSENDTYPE.EVERYDAY]}</Option>
5556
</Select>

app/web/pages/articleSubscription/components/SubscriptionModal/index.tsx

Lines changed: 101 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useState, useEffect } from 'react';
2-
import { Modal, Input, Select, message as Message, Form } from 'antd';
2+
import { Modal, Input, Select, message as Message, Form, Radio, Switch } from 'antd';
33
import ChooseSendTime from '../ChooseSendTime';
44
import { SUBSCRIPTIONSENDTYPE } from '../../consts';
55
import { isFunction } from 'lodash';
@@ -19,19 +19,24 @@ const SubscriptionModal = (props: any) => {
1919
useEffect(() => {
2020
visible && form.resetFields()
2121
if (visible && data) {
22-
const { groupName = '', webHook = '', remark = '', topicIds = [], siteNames = '', sendType = SUBSCRIPTIONSENDTYPE.WORKDAY, time = '' } = data
23-
setSiteNames(siteNames)
22+
const { groupName = '', webHook = '', remark = '', topicIds = [], siteNames = '', sendType = SUBSCRIPTIONSENDTYPE.WORKDAY, time = '', messageTitle = '', message = '', isAtAll = false } = data;
23+
const radio = siteNames === '自定义消息' && !!message ? 'custom-message' : 'article-subscription';
24+
setSiteNames(siteNames);
2425
form.setFieldsValue({
2526
groupName,
2627
webHook,
28+
radio,
29+
messageTitle,
30+
message,
31+
isAtAll,
2732
remark,
2833
topicIds,
2934
sendTime: {
3035
sendType,
3136
time
3237
}
33-
})
34-
form.validateFields()
38+
});
39+
form.validateFields();
3540
}
3641
}, [visible])
3742

@@ -75,6 +80,8 @@ const SubscriptionModal = (props: any) => {
7580
cron = `0 ${ minute } ${ hour } ? * MON-FRI`
7681
} else if (type === SUBSCRIPTIONSENDTYPE.EVERYDAY) {
7782
cron = `0 ${ minute } ${ hour } ? * *`
83+
} else if (type === SUBSCRIPTIONSENDTYPE.FRIDAY) {
84+
cron = `0 ${ minute } ${ hour } ? * FRI`
7885
}
7986
return cron
8087
}
@@ -83,6 +90,11 @@ const SubscriptionModal = (props: any) => {
8390
isFunction(onCancel) && onCancel()
8491
}
8592

93+
const handleRadioChange = (e) => {
94+
if (e?.target?.value === 'custom-message') {
95+
setSiteNames('自定义消息')
96+
}
97+
}
8698
const handleTopicChange = (value, option) => {
8799
setSiteNames(Array.from(new Set(option.map(item => item.children.split(' - ')[0]))).join('、'))
88100
}
@@ -103,7 +115,7 @@ const SubscriptionModal = (props: any) => {
103115
{ max: 64, message: '长度不超过64个字符' }
104116
]}
105117
>
106-
<Input placeholder="请输入钉钉群名称" />
118+
<Input placeholder="请输入钉钉群名称" maxLength={64} />
107119
</FormItem>
108120
<FormItem
109121
label="webHook"
@@ -116,40 +128,88 @@ const SubscriptionModal = (props: any) => {
116128
<Input placeholder="请输入webHook" />
117129
</FormItem>
118130
<FormItem
119-
label="订阅项"
120-
name="topicIds"
121-
rules={[
122-
{ required: true, message: '请选择订阅项,最多三个' },
123-
{
124-
validator: (rule, value = [], callback) => {
125-
if (value?.length > 3) {
126-
callback('最多选择三个订阅项')
127-
}
128-
callback()
129-
}
130-
}
131-
]}
131+
label="订阅类型"
132+
name="radio"
133+
required
134+
initialValue="article-subscription"
132135
>
133-
<Select
134-
showSearch
135-
mode="multiple"
136-
onChange={handleTopicChange}
137-
placeholder="请选择订阅项,最多三个"
138-
>
139-
{
140-
topicList.map(site => {
136+
<Radio.Group onChange={handleRadioChange}>
137+
<Radio value="article-subscription">文章订阅</Radio>
138+
<Radio value="custom-message">自定义消息</Radio>
139+
</Radio.Group>
140+
</FormItem>
141+
<FormItem noStyle dependencies={['radio']}>
142+
{({ getFieldValue }) => {
143+
const radio = getFieldValue('radio');
144+
if (radio === 'article-subscription') {
141145
return (
142-
<OptGroup key={site.name} label={site.name}>
143-
{
144-
site?.children?.map(item => {
145-
return <Option key={item.id} value={item.id}>{item.name}</Option>
146-
})
147-
}
148-
</OptGroup>
149-
)
150-
})
151-
}
152-
</Select>
146+
<FormItem
147+
label="订阅项"
148+
name="topicIds"
149+
rules={[
150+
{ required: true, message: '请选择订阅项,最多三个' },
151+
{
152+
validator: (rule, value = [], callback) => {
153+
if (value?.length > 3) {
154+
callback('最多选择三个订阅项')
155+
}
156+
callback()
157+
}
158+
}
159+
]}
160+
>
161+
<Select
162+
showSearch
163+
mode="multiple"
164+
onChange={handleTopicChange}
165+
placeholder="请选择订阅项,最多三个"
166+
>
167+
{
168+
topicList.map(site => {
169+
return (
170+
<OptGroup key={site.name} label={site.name}>
171+
{
172+
site?.children?.map(item => {
173+
return <Option key={item.id} value={item.id}>{item.name}</Option>
174+
})
175+
}
176+
</OptGroup>
177+
)
178+
})
179+
}
180+
</Select>
181+
</FormItem>
182+
);
183+
} else if (radio === 'custom-message') {
184+
return (
185+
<>
186+
<FormItem
187+
label="消息标题"
188+
name="messageTitle"
189+
rules={[
190+
{ required: true, message: '请输入消息标题' },
191+
{ max: 64, message: '长度不超过64个字符' }
192+
]}
193+
initialValue="定时提醒"
194+
>
195+
<Input placeholder="请输入消息标题" maxLength={64} />
196+
</FormItem>
197+
<FormItem
198+
label="消息内容"
199+
name="message"
200+
rules={[
201+
{ required: true, message: '请输入消息内容' },
202+
{ max: 2000, message: '长度不超过2000个字符' }
203+
]}
204+
>
205+
<TextArea placeholder="请输入消息内容" rows={5} maxLength={2000} />
206+
</FormItem>
207+
</>
208+
);
209+
} else {
210+
return null;
211+
}
212+
}}
153213
</FormItem>
154214
<FormItem
155215
label="推送时间"
@@ -158,6 +218,9 @@ const SubscriptionModal = (props: any) => {
158218
>
159219
<ChooseSendTime />
160220
</FormItem>
221+
<Form.Item label="@所有人" name="isAtAll" valuePropName="checked">
222+
<Switch />
223+
</Form.Item>
161224
<FormItem
162225
label="备注"
163226
name="remark"

app/web/pages/articleSubscription/consts/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ export enum SUBSCRIPTIONSTATUS {
66

77
// 订阅的推送时间方式 1 周一至周五 2 每天
88
export enum SUBSCRIPTIONSENDTYPE {
9+
FRIDAY = 0,
910
WORKDAY = 1,
1011
EVERYDAY = 2
1112
}
1213

1314
// 订阅的推送时间方式 1 周一至周五 2 每天
1415
export const SUBSCRIPTIONSENDTYPECN = {
16+
[SUBSCRIPTIONSENDTYPE.FRIDAY]: '每周五',
1517
[SUBSCRIPTIONSENDTYPE.WORKDAY]: '周一至周五',
1618
[SUBSCRIPTIONSENDTYPE.EVERYDAY]: '每天'
1719
}

app/web/pages/articleSubscription/index.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { Fragment, useEffect, useState } from 'react';
22
import { PlusCircleOutlined } from '@ant-design/icons';
33
import SubscriptionModal from './components/SubscriptionModal';
4-
import { Divider, Table, Button, Breadcrumb, Input, Modal, Switch, message as Message } from 'antd';
4+
import { Divider, Table, Button, Breadcrumb, Input, Modal, Switch, message as Message, message } from 'antd';
55
import { SUBSCRIPTIONSENDTYPECN, SUBSCRIPTIONSTATUS } from './consts';
66
import { API } from '@/api';
77
import helpIcon from '../../asset/images/help-icon.png';
@@ -50,10 +50,21 @@ const ArticleSubscriptionList = (props: any) => {
5050
dataIndex: 'groupName',
5151
key: 'groupName'
5252
},
53+
{
54+
title: '订阅类型',
55+
dataIndex: 'siteNames',
56+
key: 'siteNames',
57+
render: (siteNames, record) => {
58+
return siteNames === '自定义消息' && !!record?.message ? '自定义消息' : '文章订阅';
59+
}
60+
},
5361
{
5462
title: '订阅项',
5563
dataIndex: 'siteNames',
56-
key: 'siteNames'
64+
key: 'siteNames',
65+
render: (siteNames, record) => {
66+
return siteNames === '自定义消息' && !!record?.message ? record?.messageTitle : siteNames;
67+
}
5768
},
5869
{
5970
title: '备注',

0 commit comments

Comments
 (0)