Skip to content

Commit

Permalink
feat: 添加企业内部机器人支持
Browse files Browse the repository at this point in the history
目前sessionhook发送text内部错误,其他消息类型正常

close #3
  • Loading branch information
ineo6 committed Feb 8, 2020
1 parent 633d160 commit 4bff9e4
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 52 deletions.
39 changes: 35 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,20 @@

**注意: 需要先配置好`hubot`**

目前支持:

- 企业内部机器人(签名sign方式)
- 自定义机器人(token匹配方式)

环境变量:

- `HUBOT_DINGTALK_AUTH_TYPE` (认证类型:token,sign)
- `HUBOT_DINGTALK_TOKEN` (认证类型为token时)
- `HUBOT_DINGTALK_SECRET` (认证类型为sign时)

## Adapter 配置

### 添加机器人
### 添加自定义机器人

打开钉钉添加机器人页面,在底部找到`POST 地址``Token`(需要开通Outgoing权限)

Expand All @@ -16,11 +27,31 @@

`POST 地址`填入`域名/hubot/dingtalk/message/`

#### Token
### 添加企业内部机器人

登录钉钉开发平台创建。

#### 权限认证

1.token比较

配置环境变量:

- `HUBOT_DINGTALK_AUTH_TYPE=token`
- `HUBOT_DINGTALK_TOKEN`

`HUBOT_DINGTALK_TOKEN`对应钉钉自定义机器人`outgoing`回调`token`,用于校验`POST 地址`接收请求的有效性。


2.sign签名

配置环境变量:

- `HUBOT_DINGTALK_AUTH_TYPE=sign`
- `HUBOT_DINGTALK_SECRET`

需要配置环境变量`HUBOT_DINGTALK_TOKEN`
`HUBOT_DINGTALK_SECRET`在企业机器人配置`appSecret`一栏。

对应钉钉自定义机器人`outgoing`回调`token`,用于校验`POST 地址`接收请求的有效性。

## Todo

Expand Down
134 changes: 86 additions & 48 deletions dingtalk.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/**
* dingtalk adapter
*/

const Adapter = require.main.require('hubot/src/adapter')
const { TextMessage } = require.main.require('hubot/src/message')
const User = require.main.require('hubot/src/user')
const crypto = require("crypto");
const Adapter = require.main.require("hubot/src/adapter");
const { TextMessage } = require.main.require("hubot/src/message");
const User = require.main.require("hubot/src/user");

const { Text } = require("./src/template");

Expand All @@ -13,88 +13,117 @@ class Dingtalk extends Adapter {
super(robot);

this.token = options.token;
this.secret = options.secret;
this.authType = options.authType || authDict[0];

// 钉钉发送消息地址,20分钟有效期
// todo cache?
this.sessionWebhook = null;

this.robot.logger.info("Constructor")
this.robot.logger.info("Constructor");
}

request(data, cb) {
if (this.sessionWebhook) {
this.robot.http(this.sessionWebhook)
.header('Content-Type', 'application/json')
this.robot
.http(this.sessionWebhook)
.header("Content-Type", "application/json")
.post(JSON.stringify(data.get()))((err, resp, body) => {
const result = JSON.parse(body);
const result = JSON.parse(body);

if (result.errmsg === 0) {
//this.robot.logger.info("request success")
} else {
this.robot.logger.error("request failed:" + result.errmsg, resp)
}
if (result.errmsg === 0) {
//this.robot.logger.info("request success")
} else {
this.robot.logger.error("request failed:" + result.errmsg, resp);
}

cb && cb();
})
cb && cb();
});
} else {
this.robot.logger.error("sessionWebhook is null")
this.robot.logger.error("sessionWebhook is null");
}
}

//for room
send(envelope, ...strings) {
this.robot.logger.info("Send")
this.robot.logger.info("Send");

if (strings.length === 0) {
return
return;
}

const string = strings.shift()
const string = strings.shift();

const text = new Text();
text.setContent(string);

if (envelope.user && envelope.user.id) {
text.atId(envelope.user.id)
text.atId(envelope.user.id);
}

this.robot.logger.debug(`dingtalk sending message: ${text}`)
this.robot.logger.debug(`dingtalk sending message: ${text}`);

this.request(text, () => {
this.send.apply(this, [envelope].concat(strings))
})

this.send.apply(this, [envelope].concat(strings));
});
}

// for user
reply(envelope, ...strings) {
this.robot.logger.info("reply")
this.robot.logger.info("reply");

this.send.apply(
this,
[envelope].concat(strings.map(str => `${envelope.user.name}: ${str}`))
);
}

this.send.apply(this, [envelope].concat(strings.map(str => `${envelope.user.name}: ${str}`)))
referrerCheck(request) {
if (this.authType === "token") {
const requestToken = request.get("token");

return requestToken === this.token;
} else {
const timestamp = request.get("timestamp");
const sign = request.get("sign");

const hash = crypto
.createHmac("sha256", this.secret)
.update(timestamp + "\n" + this.secret)
.digest("base64");

return hash === sign;
}
}

listen() {
this.robot.router.post('/hubot/dingtalk/message/', (request, response) => {
this.robot.router.post("/hubot/dingtalk/message/", (request, response) => {
let data = {};

if (request.body.payload) {
JSON.parse(data = request.body.payload)
}
else {
data = request.body
JSON.parse((data = request.body.payload));
} else {
data = request.body;
}

const requestToken = request.get('token');

if (requestToken === this.token) {
this.robot.logger.debug(`dingtalk receive data ${JSON.stringify(data)}`);
if (this.referrerCheck(request)) {
this.robot.logger.debug(
`dingtalk receive data ${JSON.stringify(data)}`
);

this.sessionWebhook = data.sessionWebhook;
this.receiveMessageFromUrl(data.text.content, data.createAt, data.senderId, data.senderNick)

response.send(JSON.stringify({
"msgtype": "empty",
}));
this.receiveMessageFromUrl(
data.text.content,
data.createAt,
data.senderId,
data.senderNick
);

response.send(
JSON.stringify({
msgtype: "empty"
})
);
} else {
response.send("who are you!!!");
}
Expand All @@ -103,7 +132,7 @@ class Dingtalk extends Adapter {

// 针对钉钉talk特性,给message添加机器人名称才能出发hubot的respond
addPrefixMessage(message) {
if (!message.match(this.robot.respondPattern(''))) {
if (!message.match(this.robot.respondPattern(""))) {
return `${this.robot.name}: ${message}`;
}

Expand All @@ -114,23 +143,32 @@ class Dingtalk extends Adapter {
this.robot.logger.info(msg);

const user = new User(senderId, { name: username });
this.receive(new TextMessage(user, this.addPrefixMessage(msg), msgId))
this.receive(new TextMessage(user, this.addPrefixMessage(msg), msgId));
}

run() {
this.robot.logger.info("Run");

if (this.token) {
this.listen();
if (authDict.indexOf(this.authType) === -1) {
this.robot.logger.error("Allowed auth type is token or sign!");
return false;
}

if (!this.token || !this.secret) {
this.robot.logger.error("No token or secret is provided to dingtalk!");
} else {
this.robot.logger.error("No token provided to dingtalk!");
this.listen();
}

this.emit("connected")
this.emit("connected");
}
}

// 钉钉自定义机器人outgoing回调token
const authDict = ["token", "sign"];

// 钉钉自定义机器人签名
const token = process.env.HUBOT_DINGTALK_TOKEN;
const secret = process.env.HUBOT_DINGTALK_SECRET;
const authType = process.env.HUBOT_DINGTALK_AUTH_TYPE;

exports.use = robot => new Dingtalk(robot, { token })
exports.use = robot => new Dingtalk(robot, { token, secret, authType });

0 comments on commit 4bff9e4

Please sign in to comment.