Skip to content

Commit

Permalink
feat: 完成 企业微信群机器人、企业微信应用推送 接入
Browse files Browse the repository at this point in the history
  • Loading branch information
CaoMeiYouRen committed Feb 28, 2021
1 parent ba205d0 commit 12fa2f7
Show file tree
Hide file tree
Showing 15 changed files with 291 additions and 24 deletions.
8 changes: 8 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@ module.exports = {
},
env: {
},
settings: {
},
extends: [
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
'cmyr',
],
plugins: [
'import',
],
rules: {
'no-console': __WARN__,
Expand All @@ -19,5 +25,7 @@ module.exports = {
allowArgumentsExplicitlyTypedAsAny: true,
}], // 要求导出函数和类的公共类方法的显式返回和参数类型
'@typescript-eslint/comma-dangle': [2, 'always-multiline'], // 要求或禁止使用拖尾逗号
'import/no-unresolved': 0,
'import/order': 1,
},
}
47 changes: 34 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# push-all-in-one

本项目的目标是支持 Server酱、酷推、Bark App、Telegram Bot、钉钉机器人、企业微信机器人、企业微信应用和自定义推送等多种推送方式,目前还在开发中。
本项目的目标是支持 Server酱、酷推、邮件、钉钉机器人、企业微信机器人、企业微信应用等多种推送方式,目前还在开发中。

## 温馨提示:

出于安全考虑,**所有**推送方式请在**服务端**使用!请勿在**客户端**使用!

## 安装

Expand All @@ -11,25 +15,19 @@ npm i push-all-in-one -S
## 使用

```ts
import { ServerChanTurbo, CoolPush, Dingtalk, Email } from 'push-all-in-one'
import { ServerChanTurbo, CoolPush, Dingtalk, Email, WechatRobot, WechatApp } from 'push-all-in-one'

// 官方文档:https://sct.ftqq.com/
// Server酱。官方文档:https://sct.ftqq.com/
const SCTKEY = 'SCTxxxxxxxxxxxxxxxxxxx'
const serverChanTurbo = new ServerChanTurbo(SCTKEY)
serverChanTurbo.send('你好', '你好,我很可爱')

// 官方文档:https://cp.xuthus.cc/
// 酷推。官方文档:https://cp.xuthus.cc/
const SKEY = '022bxxxxxxxxxxxxxxxxxx'
const coolPush = new CoolPush(SKEY)
coolPush.send('你好,我很可爱')

// 官方文档:https://developers.dingtalk.com/document/app/custom-robot-access
const ACCESS_TOKEN = 'xxxxxxxxxxxxxxxxxx'
const SECRET = 'SECxxxxxxxxxxxxxxxx'
const dingtalk = new Dingtalk(ACCESS_TOKEN, SECRET)
dingtalk.send('你好', '你好,我很可爱')
coolPush.send('你好,我很可爱', 'send')

// 官方文档:http://doc.berfen.com/1239397
// BER分邮件系统。官方文档:http://doc.berfen.com/1239397
// 如果不提供 BER_KEY 将会使用免费版本进行推送。免费接口有较多限制,请自行斟酌
const email = new Email('xxxxxxx')
email.send({
Expand All @@ -39,8 +37,31 @@ email.send({
addressee: '123456@example.com',
})

// 钉钉机器人。官方文档:https://developers.dingtalk.com/document/app/custom-robot-access
const ACCESS_TOKEN = 'xxxxxxxxxxxxxxxxxx'
const SECRET = 'SECxxxxxxxxxxxxxxxx'
const dingtalk = new Dingtalk(ACCESS_TOKEN, SECRET)
dingtalk.send('你好', '你好,我很可爱')

// 企业微信群机器人。官方文档:https://work.weixin.qq.com/help?person_id=1&doc_id=13376
// 企业微信群机器人的使用需要两人以上加入企业,如果个人使用微信推送建议使用 企业微信应用+微信插件 推送。虽然需要配置的内容更多了,但是无需下载企业微信,网页端即可完成操作。
const WX_ROBOT_KEY = 'xxxxxxxxxxxxxxxxxxxxxxx'
const wechatRobot = new WechatRobot(WX_ROBOT_KEY)
wechatRobot.send('你好,我很可爱', 'text')

// 企业微信应用推送,官方文档:https://work.weixin.qq.com/api/doc/90000/90135/90664
// 微信插件 https://work.weixin.qq.com/wework_admin/frame#profile/wxPlugin
const wechatApp = new WechatApp({
WX_APP_CORPID: 'wwxxxxxxxxxxxxxxxxxxxx',
WX_APP_AGENTID: 10001,
WX_APP_SECRET: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
WX_APP_USERID: '@all',
})
wechatApp.send('你好,我很可爱')

```

## 关于设计理念

本人比较认同 `Server酱` 的设计理念,即简化使用的流程,用最简单的方法实现推送,因此,在集成推送功能时不会完整的接入所有功能,而是有所取舍,部分我觉得使用较为麻烦的功能将会移除,只保留最核心的推送功能。【如果想用完整版直接用官方sdk就行了】
本人比较认同 `Server酱` 的设计理念,即简化使用的流程,用最简单的方法实现推送。因此,在集成推送功能时不会完整的接入所有功能,而是有所取舍,部分我觉得使用较为麻烦的功能将会移除,只保留最核心的推送功能。【如果想用完整版直接用官方sdk就行了】

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "push-all-in-one",
"version": "1.1.0",
"description": "Push All In One!支持 Server酱、酷推、Bark App、Telegram Bot、钉钉机器人、企业微信机器人、企业微信应用和自定义推送等多种推送方式",
"description": "Push All In One!支持 Server酱、酷推、邮件、钉钉机器人、企业微信机器人、企业微信应用等多种推送方式",
"author": "CaoMeiYouRen",
"license": "MIT",
"main": "dist/index.js",
Expand Down Expand Up @@ -54,6 +54,7 @@
"cz-conventional-changelog": "^3.3.0",
"eslint": "^7.20.0",
"eslint-config-cmyr": "^1.1.7",
"eslint-plugin-import": "^2.22.1",
"husky": "^5.1.1",
"lint-staged": "^10.5.4",
"lodash": "^4.17.21",
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ export * from './push/cool-push'
export * from './push/email'
export * from './push/server-chan'
export * from './push/server-chan-turbo'
export * from './push/wechat-app'
export * from './push/wechat-robot'
3 changes: 2 additions & 1 deletion src/push/cool-push.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Send } from '../interfaces/send'
import { ajax } from '@/utils/ajax'
import { AxiosResponse } from 'axios'
import debug from 'debug'
import { Send } from '../interfaces/send'

const Debugger = debug('push:cool-push')

Expand Down Expand Up @@ -49,6 +49,7 @@ export class CoolPush implements Send {
* @returns
*/
async send(content: string, type: PushType = 'send'): Promise<AxiosResponse<any>> {
Debugger('content: "%s" ,type: "%s"', content, type)
return ajax({
url: `https://push.xuthus.cc/${type}/${this.SKEY}`,
query: {
Expand Down
7 changes: 4 additions & 3 deletions src/push/dingtalk.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Send } from '../interfaces/send'
import { ajax } from '@/utils/ajax'
import { AxiosResponse } from 'axios'
import debug from 'debug'
import colors from 'colors'
import CryptoJS from 'crypto-js'
import { warn } from '@/utils/helper'
import { Send } from '../interfaces/send'
import { MessageTemplateAbs } from './dingtalk/template'
import { Text } from './dingtalk/Text'
import { Markdown } from './dingtalk/Markdown'
Expand Down Expand Up @@ -32,11 +32,12 @@ export class Dingtalk implements Send {
constructor(ACCESS_TOKEN: string, SECRET?: string) {
this.ACCESS_TOKEN = ACCESS_TOKEN
this.SECRET = SECRET
Debugger('ACCESS_TOKEN: %s , SECRET: %s', ACCESS_TOKEN, SECRET)
if (!this.ACCESS_TOKEN) {
throw new Error('ACCESS_TOKEN 是必须的!')
}
if (!this.SECRET) {
console.warn(colors.yellow('未提供 SECRET !'))
warn('未提供 SECRET !')
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/push/email.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Send } from '../interfaces/send'
import { ajax } from '@/utils/ajax'
import { warn } from '@/utils/helper'
import { AxiosResponse } from 'axios'
import colors from 'colors'
import debug from 'debug'
import { Send } from '../interfaces/send'

const Debugger = debug('push:email')

Expand Down Expand Up @@ -54,7 +54,7 @@ export class Email implements Send {
this.BER_KEY = BER_KEY
Debugger('set BER_KEY: "%s"', BER_KEY)
if (!this.BER_KEY) {
console.warn(colors.yellow('未提供 BER_KEY!将使用免费版本进行推送!官方文档:http://doc.berfen.com/1239397'))
warn('未提供 BER_KEY!将使用免费版本进行推送!官方文档:http://doc.berfen.com/1239397')
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/push/server-chan-turbo.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Send } from '../interfaces/send'
import { ajax } from '@/utils/ajax'
import { AxiosResponse } from 'axios'
import debug from 'debug'
import { Send } from '../interfaces/send'

const Debugger = debug('push:server-chan-turbo')

Expand Down
6 changes: 5 additions & 1 deletion src/push/server-chan.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Send } from '../interfaces/send'
import { ajax } from '@/utils/ajax'
import { AxiosResponse } from 'axios'
import debug from 'debug'
import { Send } from '../interfaces/send'

const Debugger = debug('push:server-chan')
/**
*
*
Expand All @@ -21,6 +23,7 @@ export class ServerChan implements Send {
*/
constructor(SCKEY: string) {
this.SCKEY = SCKEY
Debugger('set SCKEY: "%s"', SCKEY)
if (!this.SCKEY) {
throw new Error('SCKEY 是必须的!')
}
Expand All @@ -40,6 +43,7 @@ export class ServerChan implements Send {
* @param desp 消息的内容,支持 Markdown
*/
async send(text: string, desp: string = ''): Promise<AxiosResponse<any>> {
Debugger('text: "%s", desp: "%s"', text, desp)
return ajax({
url: `https://sc.ftqq.com/${this.SCKEY}.send`,
method: 'POST',
Expand Down
120 changes: 120 additions & 0 deletions src/push/wechat-app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { ajax } from '@/utils/ajax'
import { AxiosResponse } from 'axios'
import debug from 'debug'
import { error, warn } from '@/utils/helper'
import { Send } from '../interfaces/send'

const Debugger = debug('push:wechat-app')

type WechatAppOption = {
/**
* 企业ID,获取方式参考:[术语说明-corpid](https://work.weixin.qq.com/api/doc/90000/90135/91039#14953/corpid)
*
*/
WX_APP_CORPID: string
/**
* 应用的凭证密钥,获取方式参考:[术语说明-secret](https://work.weixin.qq.com/api/doc/90000/90135/91039#14953/secret)
*
*/
WX_APP_SECRET: string
/**
* 企业应用的id。企业内部开发,可在应用的设置页面查看
*
*/
WX_APP_AGENTID: number
/**
* 指定接收消息的成员。若指定默认为 ”@all”。
* 成员ID列表(多个接收者用‘|’分隔,最多支持1000个)。
* 特殊情况:指定为”@all”,则向该企业应用的全部成员发送。
*
*/
WX_APP_USERID?: string
}

/**
* 企业微信应用推送,文档:https://work.weixin.qq.com/api/doc/90000/90135/90664
*
* @author CaoMeiYouRen
* @date 2021-02-28
* @export
* @class WechatApp
*/
export class WechatApp implements Send {

private WX_APP_CORPID: string
private WX_APP_SECRET: string
private WX_APP_AGENTID: number
private WX_APP_USERID?: string
private ACCESS_TOKEN: string
/**
* ACCESS_TOKEN 的过期时间(时间戳)
*
* @private
*/
private expiresTime: number

constructor(option: WechatAppOption) {
Debugger('option: %O', option)
Object.assign(this, option)
if (!this.WX_APP_CORPID) {
throw new Error('WX_APP_CORPID 是必须的!')
}
if (!this.WX_APP_SECRET) {
throw new Error('WX_APP_SECRET 是必须的!')
}
if (!this.WX_APP_AGENTID) {
throw new Error('WX_APP_AGENTID 是必须的!')
}
if (!this.WX_APP_USERID) {
warn('未提供 WX_APP_USERID!将使用 "@all" 向全体成员推送')
this.WX_APP_USERID = '@all'
}
}

private async getAccessToken(): Promise<string> {
const { data } = await ajax({
url: 'https://qyapi.weixin.qq.com/cgi-bin/gettoken',
query: {
corpid: this.WX_APP_CORPID,
corpsecret: this.WX_APP_SECRET,
},
})
if (data?.errcode === 0) { // 出错返回码,为0表示成功,非0表示调用失败
Debugger('获取 access_token 成功: %O', data)
this.expiresTime = Date.now() + (data.expires_in || 7200) * 1000 // 设置过期时间
this.ACCESS_TOKEN = data.access_token
return data.access_token
}
error(data?.errmsg)
throw new Error(data?.errmsg || '获取 access_token 失败!')
}
/**
*
*
* @author CaoMeiYouRen
* @date 2021-02-28
* @param content 消息内容,最长不超过2048个字节,超过将截断(支持id转译)
* @returns
*/
async send(content: string): Promise<AxiosResponse<any>> {
Debugger('content: %s', content)
if (!this.ACCESS_TOKEN || Date.now() >= this.expiresTime) {
await this.getAccessToken()
}
return ajax({
url: 'https://qyapi.weixin.qq.com/cgi-bin/message/send',
method: 'POST',
query: {
access_token: this.ACCESS_TOKEN,
},
data: {
touser: this.WX_APP_USERID,
msgtype: 'text',
agentid: this.WX_APP_AGENTID,
text: {
content,
},
},
})
}
}
Loading

0 comments on commit 12fa2f7

Please sign in to comment.