更新通知服务

This commit is contained in:
hanhh 2021-09-17 00:35:27 +08:00
parent 9534cda1f9
commit f2dac51a62
5 changed files with 319 additions and 51 deletions

View File

@ -16,65 +16,66 @@ abstract class NotificationBaseInfo {
} }
export class GoCqHttpBotNotification extends NotificationBaseInfo { export class GoCqHttpBotNotification extends NotificationBaseInfo {
public GOBOT_URL = ''; public goCqHttpBotUrl = '';
public GOBOT_TOKEN = ''; public goCqHttpBotToken = '';
public GOBOT_QQ = ''; public goCqHttpBotQq = '';
} }
export class ServerChanNotification extends NotificationBaseInfo { export class ServerChanNotification extends NotificationBaseInfo {
public SCKEY = ''; public serverChanKey = '';
} }
export class BarkNotification extends NotificationBaseInfo { export class BarkNotification extends NotificationBaseInfo {
public BARK_PUSH = ''; public barkPush = '';
public BARK_SOUND = ''; public barkSound = '';
public BARK_GROUP = 'qinglong'; public barkGroup = 'qinglong';
} }
export class TelegramBotNotification extends NotificationBaseInfo { export class TelegramBotNotification extends NotificationBaseInfo {
public TG_BOT_TOKEN = ''; public telegramBotToken = '';
public TG_USER_ID = ''; public telegramBotUserId = '';
public TG_PROXY_HOST = ''; public telegramBotProxyHost = '';
public TG_PROXY_PORT = ''; public telegramBotProxyPort = '';
public TG_PROXY_AUTH = ''; public telegramBotProxyAuth = '';
public TG_API_HOST = 'api.telegram.org'; public telegramBotApiHost = 'api.telegram.org';
} }
export class DingtalkBotNotification extends NotificationBaseInfo { export class DingtalkBotNotification extends NotificationBaseInfo {
public DD_BOT_TOKEN = ''; public dingtalkBotToken = '';
public DD_BOT_SECRET = ''; public dingtalkBotSecret = '';
} }
export class WeWorkBotNotification extends NotificationBaseInfo { export class WeWorkBotNotification extends NotificationBaseInfo {
public QYWX_KEY = ''; public weWorkBotKey = '';
} }
export class WeWorkAppNotification extends NotificationBaseInfo { export class WeWorkAppNotification extends NotificationBaseInfo {
public QYWX_AM = ''; public weWorkAppKey = '';
} }
export class IGotNotification extends NotificationBaseInfo { export class IGotNotification extends NotificationBaseInfo {
public IGOT_PUSH_KEY = ''; public iGotPushKey = '';
} }
export class PushPlusNotification extends NotificationBaseInfo { export class PushPlusNotification extends NotificationBaseInfo {
public PUSH_PLUS_TOKEN = ''; public pushPlusToken = '';
public PUSH_PLUS_USER = ''; public pushPlusUser = '';
} }
export class EmailNotification extends NotificationBaseInfo { export class EmailNotification extends NotificationBaseInfo {
public service: string = ''; public emailService: string = '';
public user: string = ''; public emailUser: string = '';
public pass: string = ''; public emailPass: string = '';
} }
export type NotificationInfo = export interface NotificationInfo
| GoCqHttpBotNotification extends GoCqHttpBotNotification,
| ServerChanNotification ServerChanNotification,
| BarkNotification BarkNotification,
| TelegramBotNotification TelegramBotNotification,
| DingtalkBotNotification DingtalkBotNotification,
| WeWorkBotNotification WeWorkBotNotification,
| IGotNotification WeWorkAppNotification,
| PushPlusNotification IGotNotification,
| EmailNotification; PushPlusNotification,
EmailNotification {}

View File

@ -14,12 +14,11 @@ import NotificationService from './notify';
@Service() @Service()
export default class AuthService { export default class AuthService {
@Inject((type) => NotificationService)
private notificationService!: NotificationService;
private authDb = new DataStore({ filename: config.authDbFile }); private authDb = new DataStore({ filename: config.authDbFile });
constructor( constructor(@Inject('logger') private logger: winston.Logger) {
@Inject('logger') private logger: winston.Logger,
private notificationService: NotificationService,
) {
this.authDb.loadDatabase((err) => { this.authDb.loadDatabase((err) => {
if (err) throw err; if (err) throw err;
}); });

View File

@ -2,9 +2,16 @@ import { NotificationInfo } from '../data/notify';
import { Service, Inject } from 'typedi'; import { Service, Inject } from 'typedi';
import winston from 'winston'; import winston from 'winston';
import AuthService from './auth'; import AuthService from './auth';
import got from 'got';
import nodemailer from 'nodemailer';
import crypto from 'crypto';
import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent';
@Service() @Service()
export default class NotificationService { export default class NotificationService {
@Inject((type) => AuthService)
private authService!: AuthService;
private modeMap = new Map([ private modeMap = new Map([
['goCqHttpBot', this.goCqHttpBot], ['goCqHttpBot', this.goCqHttpBot],
['serverChan', this.serverChan], ['serverChan', this.serverChan],
@ -18,14 +25,12 @@ export default class NotificationService {
['email', this.email], ['email', this.email],
]); ]);
private timeout = 10000;
private title = ''; private title = '';
private content = ''; private content = '';
private params!: Omit<NotificationInfo, 'type'>; private params!: Omit<NotificationInfo, 'type'>;
constructor( constructor(@Inject('logger') private logger: winston.Logger) {}
@Inject('logger') private logger: winston.Logger,
private authService: AuthService,
) {}
public async notify(title: string, content: string) { public async notify(title: string, content: string) {
const { type, ...rest } = await this.authService.getNotificationMode(); const { type, ...rest } = await this.authService.getNotificationMode();
@ -38,22 +43,257 @@ export default class NotificationService {
} }
} }
private async goCqHttpBot() {} private async goCqHttpBot() {
const { goCqHttpBotQq, goCqHttpBotToken, goCqHttpBotUrl } = this.params;
const res: any = await got
.post(
`${goCqHttpBotUrl}?access_token=${goCqHttpBotToken}&${goCqHttpBotQq}`,
{
timeout: this.timeout,
retry: 0,
json: { message: `${this.title}\n${this.content}` },
},
)
.json();
return res.retcode === 0;
}
private async serverChan() {} private async serverChan() {
const { serverChanKey } = this.params;
const url = serverChanKey.includes('SCT')
? `https://sctapi.ftqq.com/${SCKEY}.send`
: `https://sc.ftqq.com/${SCKEY}.send`;
const res: any = await got
.post(url, {
timeout: this.timeout,
retry: 0,
body: `text=${this.title}&desp=${this.content}`,
})
.json();
return res.errno === 0 || res.data.errno === 0;
}
private async bark() {} private async bark() {
const { barkPush, barkSound, barkGroup } = this.params;
const url = `${barkPush}/${encodeURIComponent(
this.title,
)}/${encodeURIComponent(
this.content,
)}?sound=${barkSound}&group=${barkGroup}`;
const res: any = await got
.get(url, {
timeout: this.timeout,
retry: 0,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
})
.json();
return res.code === 200;
}
private async telegramBot() {} private async telegramBot() {
const {
telegramBotApiHost,
telegramBotProxyAuth,
telegramBotProxyHost,
telegramBotProxyPort,
telegramBotToken,
telegramBotUserId,
} = this.params;
const url = `https://${telegramBotApiHost}/bot${telegramBotToken}/sendMessage`;
const options = {
keepAlive: true,
keepAliveMsecs: 1000,
maxSockets: 256,
maxFreeSockets: 256,
proxy: `http://${telegramBotProxyHost}:${telegramBotProxyPort}`,
};
const httpAgent = new HttpProxyAgent(options);
const httpsAgent = new HttpsProxyAgent(options);
const res: any = await got
.post(url, {
timeout: this.timeout,
retry: 0,
body: `chat_id=${telegramBotUserId}&text=${this.title}\n\n${this.content}&disable_web_page_preview=true`,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
agent: {
http: httpAgent,
https: httpsAgent,
},
})
.json();
return !!res.ok;
}
private async dingtalkBot() {} private async dingtalkBot() {
const { dingtalkBotSecret, dingtalkBotToken } = this.params;
let secretParam = '';
if (dingtalkBotSecret) {
const dateNow = Date.now();
const hmac = crypto.createHmac('sha256', dingtalkBotSecret);
hmac.update(`${dateNow}\n${dingtalkBotSecret}`);
const result = encodeURIComponent(hmac.digest('base64'));
secretParam = `&timestamp=${dateNow}&sign=${result}`;
}
const url = `https://oapi.dingtalk.com/robot/send?access_token=${dingtalkBotToken}${secretParam}`;
const res: any = await got
.post(url, {
timeout: this.timeout,
retry: 0,
json: {
msgtype: 'text',
text: {
content: ` ${this.title}\n\n${this.content}`,
},
},
})
.json();
return res.errcode === 0;
}
private async weWorkBot() {} private async weWorkBot() {
const { weWorkBotKey } = this.params;
const url = `https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${weWorkBotKey}`;
const res: any = await got
.post(url, {
timeout: this.timeout,
retry: 0,
json: {
msgtype: 'text',
text: {
content: ` ${this.title}\n\n${this.content}`,
},
},
})
.json();
return res.errcode === 0;
}
private async weWorkApp() {} private async weWorkApp() {
private async iGot() {} const { weWorkAppKey } = this.params;
const [corpid, corpsecret, touser, agentid, thumb_media_id = '1'] =
weWorkAppKey.split(',');
const url = `https://qyapi.weixin.qq.com/cgi-bin/gettoken`;
const { access_token } = await got
.post(url, {
timeout: this.timeout,
retry: 0,
json: {
corpid,
corpsecret,
},
})
.json();
private async pushPlus() {} let options: any = {
msgtype: 'mpnews',
mpnews: {
articles: [
{
title: `${this.title}`,
thumb_media_id,
author: `智能助手`,
content_source_url: ``,
content: `${this.content.replace(/\n/g, '<br/>')}`,
digest: `${this.content}`,
},
],
},
};
switch (thumb_media_id) {
case '0':
options = {
msgtype: 'textcard',
textcard: {
title: `${this.title}`,
description: `${this.content}`,
url: 'https://github.com/whyour/qinglong',
btntxt: '更多',
},
};
break;
private async email() {} case '1':
options = {
msgtype: 'text',
text: {
content: `${this.title}\n\n${this.content}`,
},
};
break;
}
const res: any = await got
.post(
`https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${access_token}`,
{
timeout: this.timeout,
retry: 0,
json: {
touser,
agentid,
safe: '0',
...options,
},
},
)
.json();
return res.errcode === 0;
}
private async iGot() {
const { iGotPushKey } = this.params;
const url = `https://push.hellyw.com/${iGotPushKey.toLowerCase()}`;
const res: any = await got
.post(url, {
timeout: this.timeout,
retry: 0,
body: `title=${this.title}&content=${this.content}`,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
})
.json();
return res.ret === 0;
}
private async pushPlus() {
const { pushPlusToken, pushPlusUser } = this.params;
const url = `https://www.pushplus.plus/send`;
const res: any = await got
.post(url, {
timeout: this.timeout,
retry: 0,
json: {
token: `${pushPlusToken}`,
title: `${this.title}`,
content: `${this.content.replace(/[\n\r]/g, '<br>')}`,
topic: `${pushPlusUser}`,
},
})
.json();
return res.code === 200;
}
private async email() {
const { emailPass, emailService, emailUser } = this.params;
const transporter = nodemailer.createTransport({
service: emailService,
auth: {
user: emailUser,
pass: emailPass,
},
});
const info = await transporter.sendMail({
from: `"青龙快讯" <${emailUser}>`,
to: `${emailUser}`,
subject: `${this.title}`,
html: `${this.content.replace(/\n/g, '<br/>')}`,
});
transporter.close();
return !!info.messageId;
}
} }

View File

@ -34,12 +34,14 @@
"express-jwt": "^6.0.0", "express-jwt": "^6.0.0",
"express-urlrewrite": "^1.4.0", "express-urlrewrite": "^1.4.0",
"got": "^11.8.2", "got": "^11.8.2",
"hpagent": "^0.1.2",
"iconv-lite": "^0.6.3", "iconv-lite": "^0.6.3",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"nedb": "^1.8.0", "nedb": "^1.8.0",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"node-schedule": "^2.0.0", "node-schedule": "^2.0.0",
"nodemailer": "^6.6.3",
"p-queue": "6.6.2", "p-queue": "6.6.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"typedi": "^0.8.0", "typedi": "^0.8.0",
@ -58,6 +60,7 @@
"@types/nedb": "^1.8.11", "@types/nedb": "^1.8.11",
"@types/node": "^14.11.2", "@types/node": "^14.11.2",
"@types/node-fetch": "^2.5.8", "@types/node-fetch": "^2.5.8",
"@types/nodemailer": "^6.4.4",
"@types/qrcode.react": "^1.0.1", "@types/qrcode.react": "^1.0.1",
"@types/react": "^17.0.0", "@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0", "@types/react-dom": "^17.0.0",

View File

@ -13,6 +13,7 @@ specifiers:
'@types/nedb': ^1.8.11 '@types/nedb': ^1.8.11
'@types/node': ^14.11.2 '@types/node': ^14.11.2
'@types/node-fetch': ^2.5.8 '@types/node-fetch': ^2.5.8
'@types/nodemailer': ^6.4.4
'@types/qrcode.react': ^1.0.1 '@types/qrcode.react': ^1.0.1
'@types/react': ^17.0.0 '@types/react': ^17.0.0
'@types/react-dom': ^17.0.0 '@types/react-dom': ^17.0.0
@ -30,6 +31,7 @@ specifiers:
express-jwt: ^6.0.0 express-jwt: ^6.0.0
express-urlrewrite: ^1.4.0 express-urlrewrite: ^1.4.0
got: ^11.8.2 got: ^11.8.2
hpagent: ^0.1.2
iconv-lite: ^0.6.3 iconv-lite: ^0.6.3
jsonwebtoken: ^8.5.1 jsonwebtoken: ^8.5.1
lint-staged: ^10.0.7 lint-staged: ^10.0.7
@ -37,6 +39,7 @@ specifiers:
nedb: ^1.8.0 nedb: ^1.8.0
node-fetch: ^2.6.1 node-fetch: ^2.6.1
node-schedule: ^2.0.0 node-schedule: ^2.0.0
nodemailer: ^6.6.3
nodemon: ^2.0.4 nodemon: ^2.0.4
p-queue: 6.6.2 p-queue: 6.6.2
prettier: ^2.2.0 prettier: ^2.2.0
@ -71,12 +74,14 @@ dependencies:
express-jwt: 6.0.0 express-jwt: 6.0.0
express-urlrewrite: 1.4.0 express-urlrewrite: 1.4.0
got: 11.8.2 got: 11.8.2
hpagent: 0.1.2
iconv-lite: 0.6.3 iconv-lite: 0.6.3
jsonwebtoken: 8.5.1 jsonwebtoken: 8.5.1
lodash: 4.17.21 lodash: 4.17.21
nedb: 1.8.0 nedb: 1.8.0
node-fetch: 2.6.1 node-fetch: 2.6.1
node-schedule: 2.0.0 node-schedule: 2.0.0
nodemailer: 6.6.3
p-queue: 6.6.2 p-queue: 6.6.2
reflect-metadata: 0.1.13 reflect-metadata: 0.1.13
typedi: 0.8.0 typedi: 0.8.0
@ -95,6 +100,7 @@ devDependencies:
'@types/nedb': 1.8.11 '@types/nedb': 1.8.11
'@types/node': 14.14.45 '@types/node': 14.14.45
'@types/node-fetch': 2.5.10 '@types/node-fetch': 2.5.10
'@types/nodemailer': 6.4.4
'@types/qrcode.react': 1.0.1 '@types/qrcode.react': 1.0.1
'@types/react': 17.0.5 '@types/react': 17.0.5
'@types/react-dom': 17.0.5 '@types/react-dom': 17.0.5
@ -1186,6 +1192,16 @@ packages:
resolution: {integrity: sha512-n2OQ+0Bz6WEsUjrvcHD1xZ8K+Kgo4cn9/w94s1bJS690QMUWfJPW/m7CCb7gPkA1fcYwL2UpjXP/rq/Eo41m6w==} resolution: {integrity: sha512-n2OQ+0Bz6WEsUjrvcHD1xZ8K+Kgo4cn9/w94s1bJS690QMUWfJPW/m7CCb7gPkA1fcYwL2UpjXP/rq/Eo41m6w==}
dev: false dev: false
/@types/node/14.17.16:
resolution: {integrity: sha512-WiFf2izl01P1CpeY8WqFAeKWwByMueBEkND38EcN8N68qb0aDG3oIS1P5MhAX5kUdr469qRyqsY/MjanLjsFbQ==}
dev: true
/@types/nodemailer/6.4.4:
resolution: {integrity: sha512-Ksw4t7iliXeYGvIQcSIgWQ5BLuC/mljIEbjf615svhZL10PE9t+ei8O9gDaD3FPCasUJn9KTLwz2JFJyiiyuqw==}
dependencies:
'@types/node': 14.17.16
dev: true
/@types/normalize-package-data/2.4.0: /@types/normalize-package-data/2.4.0:
resolution: {integrity: sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==} resolution: {integrity: sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==}
dev: true dev: true
@ -4232,6 +4248,10 @@ packages:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
dev: true dev: true
/hpagent/0.1.2:
resolution: {integrity: sha512-ePqFXHtSQWAFXYmj+JtOTHr84iNrII4/QRlAAPPE+zqnKy4xJo7Ie1Y4kC7AdB+LxLxSTTzBMASsEcy0q8YyvQ==}
dev: false
/html-encoding-sniffer/1.0.2: /html-encoding-sniffer/1.0.2:
resolution: {integrity: sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==} resolution: {integrity: sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==}
dependencies: dependencies:
@ -6162,6 +6182,11 @@ packages:
sorted-array-functions: 1.3.0 sorted-array-functions: 1.3.0
dev: false dev: false
/nodemailer/6.6.3:
resolution: {integrity: sha512-faZFufgTMrphYoDjvyVpbpJcYzwyFnbAMmQtj1lVBYAUSm3SOy2fIdd9+Mr4UxPosBa0JRw9bJoIwQn+nswiew==}
engines: {node: '>=6.0.0'}
dev: false
/nodemon/2.0.7: /nodemon/2.0.7:
resolution: {integrity: sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA==} resolution: {integrity: sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA==}
engines: {node: '>=8.10.0'} engines: {node: '>=8.10.0'}