Skip to content

Commit d0d1195

Browse files
author
Hai Liang Wang
committed
#5 enable send w3 events with email
1 parent 7171645 commit d0d1195

File tree

9 files changed

+218
-81
lines changed

9 files changed

+218
-81
lines changed

docker-compose.yml

+12-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,16 @@ services:
99
- NODE_ENV=${NODE_ENV:-development}
1010
- FEISHU_GROUP_GITHUB_BOTS=${FEISHU_GROUP_GITHUB_BOTS:-placeholder}
1111
- FEISHU_GROUP_W3_BOTS=${FEISHU_GROUP_W3_BOTS:-placeholder}
12+
- MAIL_W3_EVENTS_CC=${MAIL_W3_EVENTS_CC:-placeholder}
13+
- MAIL_W3_EVENTS_BCC=${MAIL_W3_EVENTS_BCC:-placeholder}
14+
- SMTP_HOST=smtp.feishu.cn
15+
- SMTP_PORT=465
16+
- SMTP_SSL=true
17+
- SMTP_USER_FROM=${SMTP_USER_FROM:-placeholder}
18+
- SMTP_USER_NAME="CSKeFu W3"
19+
- SMTP_USER_PASS=${SMTP_USER_PASS:-placeholder}
1220
ports:
13-
- "${SYNC_PORT:-8201}:8201"
21+
- "${SYNC_PORT:-8201}:8201"
22+
23+
24+

sample.env

+12-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,15 @@ COMPOSE_PROJECT_NAME=cskefu.sync
66

77
NODE_ENV=development
88
FEISHU_GROUP_GITHUB_BOTS=
9-
FEISHU_GROUP_W3_BOTS=
9+
FEISHU_GROUP_W3_BOTS=
10+
11+
MAIL_W3_EVENTS_CC=
12+
MAIL_W3_EVENTS_BCC=
13+
14+
# SMTP Client
15+
SMTP_HOST=smtp.feishu.cn
16+
SMTP_PORT=465
17+
SMTP_SSL=true
18+
SMTP_USER_FROM=
19+
SMTP_USER_NAME="CSKeFu W3"
20+
SMTP_USER_PASS=

sync/app/controllers/w3.ctrl.js

+66-54
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
const debug = require("debug")("sync:ctrl:w3");
66
const utils = require("../utils/index");
77
const feishuService = require("../services/feishu.service");
8-
9-
8+
const mailService = require("../services/email.service");
109

1110
function Controller() {
1211

@@ -19,66 +18,79 @@ function Controller() {
1918
* @param {*} body
2019
*/
2120
Controller.prototype.handleW3broadcast = async function (headers, params, body) {
22-
let ret = { "msg": "done" };
23-
/**
24-
* sync:routes:w3 /broadcast ctx.params {} , body
21+
let ret = { "msg": "done" };
22+
/**
23+
* sync:routes:w3 /broadcast ctx.params {} , body
2524
{
26-
"link": "https://www.cskefu.com/2022/10/08/test-c/",
27-
"post_title": "Test C",
28-
"author_id": "99",
29-
"display_name": "Hai",
30-
"user_email": "h@cskefu.com",
31-
"user_profile": "https://www.cskefu.com/user/99/",
32-
"categories": [
33-
{
34-
"term_id": 118,
35-
"name": "业务观点",
36-
"slug": "business",
37-
"term_group": 0,
38-
"term_taxonomy_id": 118,
39-
"taxonomy": "category",
40-
"description": "业务、产品及服务的应用和解决方案:呼叫中心、联络中心、智能客服、客服机器人、CTI、云计算等。有价值的干货:应用场景、业务价
25+
"link": "https://www.cskefu.com/2022/10/08/test-c/",
26+
"post_title": "Test C",
27+
"author_id": "99",
28+
"display_name": "Hai",
29+
"user_email": "h@cskefu.com",
30+
"user_profile": "https://www.cskefu.com/user/99/",
31+
"categories": [
32+
{
33+
"term_id": 118,
34+
"name": "业务观点",
35+
"slug": "business",
36+
"term_group": 0,
37+
"term_taxonomy_id": 118,
38+
"taxonomy": "category",
39+
"description": "业务、产品及服务的应用和解决方案:呼叫中心、联络中心、智能客服、客服机器人、CTI、云计算等。有价值的干货:应用场景、业务价
4140
值、创新方案。",
42-
"parent": 0,
43-
"count": 12,
44-
"filter": "raw",
45-
"cat_ID": 118,
46-
"category_count": 12,
47-
"category_description": "业务、产品及服务的应用和解决方案:呼叫中心、联络中心、智能客服、客服机器人、CTI、云计算等。有价值的干货:应用场
41+
"parent": 0,
42+
"count": 12,
43+
"filter": "raw",
44+
"cat_ID": 118,
45+
"category_count": 12,
46+
"category_description": "业务、产品及服务的应用和解决方案:呼叫中心、联络中心、智能客服、客服机器人、CTI、云计算等。有价值的干货:应用场
4847
景、业务价值、创新方案。",
49-
"cat_name": "业务观点",
50-
"category_nicename": "business",
51-
"category_parent": 0
52-
},
53-
{
54-
"term_id": 145,
55-
"name": "产品专栏",
56-
"slug": "product",
57-
"term_group": 0,
58-
"term_taxonomy_id": 145,
59-
"taxonomy": "category",
60-
"description": "产品理念、产品设计、产品构想等;主要由春松客服产品经理或产品办公室发布。",
61-
"parent": 0,
62-
"count": 2,
63-
"filter": "raw",
64-
"cat_ID": 145,
65-
"category_count": 2,
66-
"category_description": "产品理念、产品设计、产品构想等;主要由春松客服产品经理或产品办公室发布。",
67-
"cat_name": "产品专栏",
68-
"category_nicename": "product",
69-
"category_parent": 0
70-
}
71-
]
48+
"cat_name": "业务观点",
49+
"category_nicename": "business",
50+
"category_parent": 0
51+
},
52+
{
53+
"term_id": 145,
54+
"name": "产品专栏",
55+
"slug": "product",
56+
"term_group": 0,
57+
"term_taxonomy_id": 145,
58+
"taxonomy": "category",
59+
"description": "产品理念、产品设计、产品构想等;主要由春松客服产品经理或产品办公室发布。",
60+
"parent": 0,
61+
"count": 2,
62+
"filter": "raw",
63+
"cat_ID": 145,
64+
"category_count": 2,
65+
"category_description": "产品理念、产品设计、产品构想等;主要由春松客服产品经理或产品办公室发布。",
66+
"cat_name": "产品专栏",
67+
"category_nicename": "product",
68+
"category_parent": 0,
69+
"content": "Formatted HTML, https://www.kevinleary.net/get-wordpress-post-content-by-post-id/"
70+
}
71+
]
7272
}
73-
*/
74-
utils.writeTmpOutputFileOnDevelopment(body);
73+
*/
74+
utils.writeTmpOutputFileOnDevelopment(body);
7575

76-
// 发送 W3 文章通知到飞书群
76+
// 发送 W3 文章通知到飞书群
77+
try {
7778
await feishuService.sendW3BroadcastNotification(body);
79+
} catch (e) {
80+
console.log("[Error] feishuService.sendW3BroadcastNotification")
81+
console.error(e);
82+
}
7883

84+
// 发送 W3 文章通知到邮件列表服务
85+
try {
86+
await mailService.sendW3BroadcastMaillists(body);
87+
} catch (e) {
88+
console.log("[Error] mailService.sendW3BroadcastMaillists")
89+
console.error(e);
90+
}
7991

80-
debug("[handleW3broadcast] ret", JSON.stringify(ret))
81-
return ret;
92+
debug("[handleW3broadcast] ret", JSON.stringify(ret))
93+
return ret;
8294
}
8395

8496

sync/app/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
"koa-static": "^5.0.0",
2828
"koa2-cors": "^2.0.6",
2929
"lodash": "^4.17.21",
30-
"moment-timezone": "^0.5.37"
30+
"moment-timezone": "^0.5.37",
31+
"nodemailer": "^6.8.0",
32+
"nodemailer-smtp-transport": "^2.7.4"
3133
},
3234
"devDependencies": {
3335
"ava": "^4.3.3",

sync/app/routers/w3.router.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
let body = ctx.request.body;
1717
debug("/broadcast headers", JSON.stringify(ctx.request.headers, null, 2))
1818
debug("/broadcast", "ctx.params", JSON.stringify(ctx.params), ", body\n", JSON.stringify(body, null, 2));
19-
19+
2020
ctx.body = await w3Ctrl.handleW3broadcast(ctx.request.headers, ctx.params, body);
2121

2222
await next();

sync/app/sample.env

+12
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,15 @@
44
NODE_ENV=development
55
FEISHU_GROUP_GITHUB_BOTS=xxx,yyy
66
FEISHU_GROUP_W3_BOTS=xxx,yyy
7+
8+
MAIL_W3_EVENTS_CC=
9+
MAIL_W3_EVENTS_BCC=
10+
11+
# SMTP Client
12+
SMTP_HOST=smtp.feishu.cn
13+
SMTP_PORT=465
14+
SMTP_SSL=true
15+
SMTP_USER_FROM=
16+
SMTP_USER_NAME="CSKeFu W3"
17+
SMTP_USER_PASS=
18+

sync/app/services/email.service.js

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* Send Emails
3+
*/
4+
5+
6+
const debug = require("debug")("sync:services:email");
7+
const nodemailer = require('nodemailer');
8+
const smtpTransport = require('nodemailer-smtp-transport');
9+
const utils = require("../utils/index");
10+
11+
const SMTP_HOST = process.env["SMTP_HOST"];
12+
const SMTP_PORT = process.env["SMTP_PORT"];
13+
const SMTP_SSL = process.env["SMTP_SSL"];
14+
const SMTP_USER_FROM = process.env["SMTP_USER_FROM"];
15+
const SMTP_USER_NAME = process.env["SMTP_USER_NAME"];
16+
const SMTP_USER_PASS = process.env["SMTP_USER_PASS"];
17+
const MAIL_W3_EVENTS_CC = process.env["MAIL_W3_EVENTS_CC"];
18+
const MAIL_W3_EVENTS_BCC = process.env["MAIL_W3_EVENTS_BCC"];
19+
const BRAND_SUFFIX = process.env["BRAND_SUFFIX"] || "春松客服 W3";
20+
21+
const transport = nodemailer.createTransport(smtpTransport({
22+
host: SMTP_HOST, // 服务
23+
port: SMTP_PORT, // smtp端口
24+
secure: SMTP_SSL,
25+
auth: {
26+
user: SMTP_USER_FROM, // 用户名
27+
pass: SMTP_USER_PASS // SMTP授权码
28+
}
29+
}));
30+
31+
/**
32+
* Send Mail
33+
* @param {*} options
34+
* @returns
35+
*/
36+
function send(options) {
37+
debug("[send] %j", options)
38+
39+
return new Promise((resolve, reject) => {
40+
transport.sendMail({
41+
from: `${SMTP_USER_NAME}<${SMTP_USER_FROM}>`, // 发件邮箱
42+
to: options.to, // 收件列表
43+
bcc: options.bcc, // 收件列表
44+
subject: options.subject, // 标题
45+
html: options.html,
46+
attachments: options.attachment ? [{ path: options.attachment }] : null,
47+
},
48+
(error, data) => {
49+
if (error) return reject(error)
50+
resolve(data)
51+
})
52+
})
53+
54+
}
55+
56+
/**
57+
* 发送 W3 服务通知
58+
* @param {*} payload
59+
*/
60+
async function sendW3BroadcastMaillists(payload) {
61+
62+
if (!MAIL_W3_EVENTS_CC) {
63+
debug("[sendW3BroadcastMaillists] not set ENV MAIL_W3_EVENTS_CC, bypass broadcast.");
64+
return;
65+
}
66+
67+
await send({
68+
to: MAIL_W3_EVENTS_CC,
69+
bcc: MAIL_W3_EVENTS_BCC,
70+
subject: utils.capitalizeFirstLetter(utils.getW3PostCategories(payload) + " - ") + payload.display_name + " - " + payload.post_title + " | " + BRAND_SUFFIX,
71+
html: ` LINK: ${payload.link} <br />
72+
AUTHOR: ${payload.display_name}(${payload.user_email}) <br />
73+
PROFILE: ${payload.user_profile} <br />
74+
<br />
75+
CONTENT: <br />
76+
${payload.content} <br />
77+
78+
<br />
79+
<br />
80+
`
81+
})
82+
}
83+
84+
exports = module.exports = {
85+
send,
86+
sendW3BroadcastMaillists
87+
}

sync/app/services/feishu.service.js

+2-21
Original file line numberDiff line numberDiff line change
@@ -491,25 +491,6 @@ async function sendMilestoneEventNotification(payload) {
491491
}
492492
}
493493

494-
/**
495-
* 获得 W3 文章标签分类信息
496-
* @param {*} payload
497-
*/
498-
function getW3PostCategories(payload){
499-
if(payload.categories && payload.categories.length > 0){
500-
let ret = [];
501-
502-
for(let x of payload.categories){
503-
ret.push(x.name);
504-
}
505-
506-
return ret.join("_")
507-
} else {
508-
return "未分类"
509-
}
510-
}
511-
512-
513494
/**
514495
* Send W3 Post Events into Feishu Groups
515496
* @param {*} payload
@@ -521,7 +502,7 @@ async function sendW3BroadcastNotification(payload) {
521502
elements.push({
522503
"tag": "div",
523504
"text": {
524-
"content": `**标题** ${payload.post_title}\n**作者** [${payload.display_name}(${payload.user_email})](${payload.user_profile})`,
505+
"content": `**标题** ${payload.post_title}\n**作者** [${payload.display_name}(${payload.user_email})](${payload.user_profile})\n**正文**\n ${payload.content.substring(0, 60)}...`,
525506
"tag": "lark_md"
526507
}
527508
})
@@ -555,7 +536,7 @@ async function sendW3BroadcastNotification(payload) {
555536
"elements": elements,
556537
"header": {
557538
"title": {
558-
"content": utils.capitalizeFirstLetter(getW3PostCategories(payload) + " - ") + payload.post_title + " | 春松客服 W3",
539+
"content": utils.capitalizeFirstLetter(utils.getW3PostCategories(payload) + " - ") + payload.post_title + " | 春松客服 W3",
559540
"tag": "plain_text"
560541
}
561542
}

sync/app/utils/index.js

+23-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,27 @@ function writeTmpOutputFileOnDevelopment(obj) {
3131

3232
function capitalizeFirstLetter(string) {
3333
return string.charAt(0).toUpperCase() + string.slice(1);
34-
}
34+
}
35+
36+
37+
38+
/**
39+
* 获得 W3 文章标签分类信息
40+
* @param {*} payload
41+
*/
42+
function getW3PostCategories(payload) {
43+
if (payload.categories && payload.categories.length > 0) {
44+
let ret = [];
45+
46+
for (let x of payload.categories) {
47+
ret.push(x.name);
48+
}
49+
50+
return ret.join("_")
51+
} else {
52+
return "未分类"
53+
}
54+
}
3555

3656

3757
exports = module.exports = {
@@ -40,5 +60,6 @@ exports = module.exports = {
4060
writeJsonFile,
4161
isNodeEnvDevelopment,
4262
writeTmpOutputFileOnDevelopment,
43-
capitalizeFirstLetter
63+
capitalizeFirstLetter,
64+
getW3PostCategories
4465
}

0 commit comments

Comments
 (0)