├── .babelrc ├── .gitignore ├── CHANGELOG.md ├── README.md ├── commitlint.config.js ├── package.json ├── rollup.config.js ├── src ├── index.ts └── types.ts ├── tsconfig.json └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-typescript", 4 | [ 5 | "@babel/preset-env", 6 | { 7 | "modules": false 8 | } 9 | ] 10 | ], 11 | "plugins": ["@babel/plugin-transform-runtime", "@babel/plugin-proposal-class-properties"] 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | lib -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [1.2.1](https://github.com/leitingting08/ding-bot-sdk/compare/v1.4.0...v1.2.1) (2021-12-22) 6 | 7 | ## [1.4.0](https://github.com/leitingting08/ding-bot-sdk/compare/v1.2.0...v1.4.0) (2021-12-22) 8 | 9 | 10 | ### Features 11 | 12 | * update timestamp ([665bff1](https://github.com/leitingting08/ding-bot-sdk/commit/665bff1b0324197345bf07a7e5ccf8894e714481)) 13 | 14 | ## [1.2.0](https://github.com/leitingting08/ding-bot-sdk/compare/v1.1.1...v1.2.0) (2021-10-09) 15 | 16 | 17 | ### Features 18 | 19 | * 返回回调内容 ([597ece2](https://github.com/leitingting08/ding-bot-sdk/commit/597ece23dce2b296bae11d75af509a1c12559f92)) 20 | 21 | ### [1.1.1](https://github.com/leitingting08/ding-bot-sdk/compare/v1.1.0...v1.1.1) (2021-02-03) 22 | 23 | 24 | ### Bug Fixes 25 | 26 | * add request ([d6f38b8](https://github.com/leitingting08/ding-bot-sdk/commit/d6f38b8cf65164413273eb288bba0e06d8b1db6b)) 27 | 28 | ## [1.1.0](https://github.com/leitingting08/ding-bot-sdk/compare/v1.0.0...v1.1.0) (2021-01-25) 29 | 30 | 31 | ### Features 32 | 33 | * 更改配置 ([43e80cb](https://github.com/leitingting08/ding-bot-sdk/commit/43e80cb61f672a549d11daf014753b7c66f0caa0)) 34 | 35 | ## [1.0.0](https://github.com/leitingting08/ding-bot-sdk/compare/v0.0.3...v1.0.0) (2021-01-19) 36 | 37 | 38 | ### Features 39 | 40 | * **✨:** 增加可选参数 ([1b86167](https://github.com/leitingting08/ding-bot-sdk/commit/1b8616766552106fb33a67f0fd497b6cfddc3bfd)) 41 | 42 | ### [0.0.3](https://github.com/leitingting08/ding-bot-sdk/compare/v0.0.2...v0.0.3) (2021-01-17) 43 | 44 | ### [0.0.2](https://github.com/leitingting08/ding-bot-sdk/compare/v0.0.1...v0.0.2) (2021-01-17) 45 | 46 | 47 | ### Features 48 | 49 | * update description ([29de0a2](https://github.com/leitingting08/ding-bot-sdk/commit/29de0a2a10c8341271df7cdc375feb3b700e1e02)) 50 | * update description ([1f0a4b4](https://github.com/leitingting08/ding-bot-sdk/commit/1f0a4b447d72ae133868f4a6b3cb42deba0e8e90)) 51 | 52 | ### 0.0.2 (2021-01-17) 53 | 54 | 55 | ### Features 56 | 57 | * **🎉:** 自定义钉钉机器人node封装 ([77dff0b](https://github.com/leitingting08/ding-bot/commit/77dff0bf791ec9d977eee2ef21a98ff4ad96c767)) 58 | * **🎉:** 自定义钉钉机器人node封装 ([525d656](https://github.com/leitingting08/ding-bot/commit/525d65600029d21956f2d46d158fa25c74ec9476)) 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 钉钉自定义机器人的封装 2 | 3 | [官方文档](https://ding-doc.dingtalk.com/document/app/custom-robot-access) 4 | 5 | ## 安装 6 | 7 | ``` 8 | npm install ding-bot-sdk 9 | # or 10 | yarn add ding-bot-sdk 11 | ``` 12 | 13 | ## 初始化 14 | 15 | ``` 16 | import Bot from 'ding-bot-sdk' 17 | # or 18 | const Bot = require('ding-bot-sdk') 19 | 20 | // Webhook地址: https://oapi.dingtalk.com/robot/send?access_token=xxx 21 | const bot = new Bot({ 22 | base_url: 'https://oapi.dingtalk.com/robot/send', // 可选 不填默认 https://oapi.dingtalk.com/robot/send 23 | access_token: 'xxx', // Webhook地址后的access_token // 必填 24 | secret: 'xxx' // 安全设置:加签的secret 必填 25 | }) 26 | ``` 27 | 28 | ## 发送消息 29 | 30 | [格式和钉钉机器人开发文档保持一致](https://ding-doc.dingtalk.com/document/app/custom-robot-access) 31 | 32 | text类型,返回回调内容 33 | 34 | ```js 35 | bot.send({ 36 | "msgtype": "text", 37 | "text": { 38 | "content": "我就是我, @150XXXXXXXX 是不一样的烟火" 39 | }, 40 | "at": { 41 | "atMobiles": [ 42 | "150XXXXXXXX" 43 | ], 44 | "isAtAll": false 45 | } 46 | }).then(res=>{ 47 | console.log('res',res) 48 | }).catch(error=>{ 49 | console.log('error',error) 50 | }) 51 | ``` 52 | 53 | link类型 54 | 55 | ```js 56 | bot.send({ 57 | "msgtype": "link", 58 | "link": { 59 | "text": "这个即将发布的新版本,创始人xx称它为红树林。而在此之前,每当面临重大升级,产品经理们都会取一个应景的代号,这一次,为什么是红树林", 60 | "title": "时代的火车向前开", 61 | "picUrl": "", 62 | "messageUrl": "https://www.dingtalk.com/s?__biz=MzA4NjMwMTA2Ng==&mid=2650316842&idx=1&sn=60da3ea2b29f1dcc43a7c8e4a7c97a16&scene=2&srcid=09189AnRJEdIiWVaKltFzNTw&from=timeline&isappinstalled=0&key=&ascene=2&uin=&devicetype=android-23&version=26031933&nettype=WIFI" 63 | } 64 | }) 65 | ``` 66 | 67 | markdown类型 68 | 69 | ```js 70 | bot.send({ 71 | "msgtype": "markdown", 72 | "markdown": { 73 | "title":"杭州天气", 74 | "text": "#### 杭州天气 @150XXXXXXXX \n> 9度,西北风1级,空气良89,相对温度73%\n> ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\n> ###### 10点20分发布 [天气](https://www.dingtalk.com) \n" 75 | }, 76 | "at": { 77 | "atMobiles": [ 78 | "150XXXXXXXX" 79 | ], 80 | "isAtAll": false 81 | } 82 | }) 83 | ``` 84 | 85 | 整体跳转ActionCard类型 86 | 87 | ```js 88 | bot.send({ 89 | "actionCard": { 90 | "title": "乔布斯 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身", 91 | "text": "![screenshot](https://gw.alicdn.com/tfs/TB1ut3xxbsrBKNjSZFpXXcXhFXa-846-786.png) 92 | ### 乔布斯 20 年前想打造的苹果咖啡厅 93 | Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划", 94 | "btnOrientation": "0", 95 | "singleTitle" : "阅读全文", 96 | "singleURL" : "https://www.dingtalk.com/" 97 | }, 98 | "msgtype": "actionCard" 99 | }) 100 | ``` 101 | 102 | 独立跳转ActionCard类型 103 | 104 | ```js 105 | bot.send({ 106 | "msgtype": "actionCard", 107 | "actionCard": { 108 | "title": "我 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身", 109 | "text": "![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png) \n\n #### 乔布斯 20 年前想打造的苹果咖啡厅 \n\n Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划", 110 | "hideAvatar": "0", 111 | "btnOrientation": "0", 112 | "btns": [ 113 | { 114 | "title": "内容不错", 115 | "actionURL": "https://www.dingtalk.com/" 116 | }, 117 | { 118 | "title": "不感兴趣", 119 | "actionURL": "https://www.dingtalk.com/" 120 | } 121 | ] 122 | } 123 | }) 124 | ``` 125 | 126 | FeedCard类型 127 | 128 | ```js 129 | bot.send({ 130 | "msgtype": "feedCard", 131 | "feedCard": { 132 | "links": [ 133 | { 134 | "title": "时代的火车向前开1", 135 | "messageURL": "https://www.dingtalk.com/", 136 | "picURL": "https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png" 137 | }, 138 | { 139 | "title": "时代的火车向前开2", 140 | "messageURL": "https://www.dingtalk.com/", 141 | "picURL": "https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png" 142 | } 143 | ] 144 | } 145 | }) 146 | ``` 147 | 148 | ## 常见问题 149 | 150 | 当出现以下错误时,表示消息校验未通过,请查看机器人的安全设置。 151 | 152 | ```json 153 | // 消息内容中不包含任何关键词 154 | { 155 | "errcode":310000, 156 | "errmsg":"keywords not in content" 157 | } 158 | 159 | // timestamp 无效 160 | { 161 | "errcode":310000, 162 | "errmsg":"invalid timestamp" 163 | } 164 | 165 | // 签名不匹配 166 | { 167 | "errcode":310000, 168 | "errmsg":"sign not match" 169 | } 170 | 171 | // IP地址不在白名单 172 | { 173 | "errcode":310000, 174 | "errmsg":"ip X.X.X.X not in whitelist" 175 | } 176 | ``` 177 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {extends: ['@commitlint/config-conventional']}; 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ding-bot-sdk", 3 | "version": "1.2.1", 4 | "description": "钉钉自定义机器人sdk", 5 | "main": "lib/dingBot.main.js", 6 | "module": "lib/dingBot.module.js", 7 | "typings": "lib/index.d.ts", 8 | "author": "ltt", 9 | "license": "MIT", 10 | "homepage": "https://github.com/leitingting08/ding-bot-sdk/blob/master/README.md", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/leitingting08/ding-bot-sdk" 14 | }, 15 | "scripts": { 16 | "watch": "rollup -c -w", 17 | "build": "npm run clean && cross-env NODE_ENV=prod && rollup -c", 18 | "build:debug": "npm run clean && cross-env NODE_ENV=dev rollup -c -w", 19 | "clean": "rm -rf ./lib", 20 | "deploy": "npm run release && npm run build && npm publish && git push --follow-tags", 21 | "lint": "prettier --write src", 22 | "release": "standard-version" 23 | }, 24 | "typescript": ">=4.0", 25 | "files": [ 26 | "lib" 27 | ], 28 | "devDependencies": { 29 | "@babel/core": "^7.12.9", 30 | "@babel/plugin-proposal-class-properties": "^7.12.1", 31 | "@babel/plugin-transform-runtime": "^7.12.1", 32 | "@babel/preset-env": "^7.12.1", 33 | "@babel/preset-typescript": "^7.12.7", 34 | "@commitlint/cli": "^11.0.0", 35 | "@commitlint/config-conventional": "^11.0.0", 36 | "@rollup/plugin-babel": "^5.2.2", 37 | "@rollup/plugin-commonjs": "^17.0.0", 38 | "@rollup/plugin-json": "^4.1.0", 39 | "@rollup/plugin-node-resolve": "^10.0.0", 40 | "@types/node": "^14.14.21", 41 | "@typescript-eslint/eslint-plugin": "^4.9.0", 42 | "@typescript-eslint/parser": "^4.9.0", 43 | "babel-eslint": "^10.1.0", 44 | "cross-env": "^7.0.2", 45 | "eslint": "^7.15.0", 46 | "eslint-config-prettier": "^7.0.0", 47 | "eslint-plugin-prettier": "^3.2.0", 48 | "husky": "^4.3.4", 49 | "lint-staged": "^10.5.3", 50 | "prettier": "^2.2.1", 51 | "rollup": "^2.33.2", 52 | "rollup-plugin-terser": "^7.0.2", 53 | "rollup-plugin-typescript2": "^0.29.0", 54 | "standard-version": "^9.0.0", 55 | "tslib": "^2.0.3", 56 | "typescript": "^4.1.2" 57 | }, 58 | "husky": { 59 | "hooks": { 60 | "pre-commit": "lint-staged", 61 | "commit-msg": "commitlint -e $HUSKY_GIT_PARAMS" 62 | } 63 | }, 64 | "lint-staged": { 65 | "*.{.ts,.tsx}": [ 66 | "prettier --write", 67 | "eslint --fix", 68 | "git add" 69 | ] 70 | }, 71 | "dependencies": { 72 | "request": "^2.88.2" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | /** @format */ 2 | 3 | import typescript from 'rollup-plugin-typescript2' // 处理typescript 4 | import babel from '@rollup/plugin-babel' 5 | import commonjs from '@rollup/plugin-commonjs' 6 | import { nodeResolve } from '@rollup/plugin-node-resolve' 7 | import { terser } from 'rollup-plugin-terser' 8 | import { DEFAULT_EXTENSIONS } from '@babel/core' 9 | const isDev = process.env.NODE_ENV === 'dev' 10 | 11 | export default { 12 | input: './src/index.ts', // 入口文件 13 | output: [ 14 | { 15 | file: `lib/dingBot.main.js`, // 打包之后的文件名以及存放位置 16 | format: 'umd', // 以什么模式打包,支持umd,cmd,esm... 17 | name: 'dingBot' 18 | }, 19 | { 20 | file: `lib/logHub.module.js`, // 打包之后的文件名以及存放位置 21 | format: 'es', // 以什么模式打包,支持umd,cmd,esm... 22 | name: 'dingBot' 23 | } 24 | ], 25 | plugins: [ 26 | // https://github.com/rollup/rollup-plugin-babel/issues/318 27 | // put TS plugin before babel plugin in your plugins array 28 | typescript({ 29 | exclude: 'node_modules/**', 30 | typescript: require('typescript') 31 | }), 32 | babel({ 33 | exclude: 'node_modules/**', 34 | babelHelpers: 'runtime', 35 | // babel 默认不支持 ts 需要手动添加 36 | extensions: [...DEFAULT_EXTENSIONS, '.ts'] 37 | }), 38 | nodeResolve({ 39 | mainField: ['jsnext:main', 'browser', 'module', 'main'], 40 | browser: true 41 | }), 42 | commonjs(), 43 | !isDev && terser() 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | const crypto = require('crypto'); 3 | import { InitData } from './types' 4 | const headers= { 5 | "Content-Type": "application/json;charset=utf-8" 6 | }; 7 | 8 | const defaultOptions = { 9 | msgtype: "text", 10 | text: { 11 | content: 'hello~' 12 | } 13 | } 14 | // https://ding-doc.dingtalk.com/document/app/custom-robot-access 15 | class Bot{ 16 | _initData: InitData = { 17 | base_url:'', 18 | access_token: '', 19 | secret: '' 20 | } 21 | _webhookUrl: string 22 | constructor(_initData: InitData){ 23 | this._initData = _initData; 24 | } 25 | 26 | webhookUrlFn = (_initData: InitData) =>{ 27 | const { access_token, secret, base_url = 'https://oapi.dingtalk.com/robot/send' } = _initData 28 | const timestamp = new Date().getTime() 29 | const sign = this.signFn(secret,`${timestamp}\n${secret}`) 30 | this._webhookUrl = `${base_url}?access_token=${access_token}×tamp=${timestamp}&sign=${sign}` 31 | } 32 | 33 | signFn = (secret, content) =>{ 34 | const str = crypto.createHmac('sha256', secret).update(content) 35 | .digest() 36 | .toString('base64'); 37 | return encodeURIComponent(str); 38 | } 39 | 40 | send (json = defaultOptions){ 41 | let options = { 42 | headers, 43 | json 44 | }; 45 | this.webhookUrlFn(this._initData); 46 | return new Promise((resolve, reject)=>{ 47 | request.post(this._webhookUrl, options, function(_error, _response, body){ 48 | console.log(`send msg, response: ${JSON.stringify(body)}`); 49 | resolve(_response) 50 | reject(_error) 51 | }); 52 | }) 53 | } 54 | } 55 | 56 | module.exports = Bot 57 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * dingBot中initData的interface 3 | * @format 4 | */ 5 | 6 | export interface InitData { 7 | base_url?: '', 8 | access_token: '', 9 | secret: '', 10 | } 11 | 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": "./src", 4 | "outDir": "./lib", 5 | "declaration": true // 生成*.d.ts 6 | }, 7 | "exclude": [ 8 | "node_modules" 9 | ] 10 | } 11 | --------------------------------------------------------------------------------