Skip to content

Commit a96e84f

Browse files
committed
fix:新增消息响方法应,避免服务端重试.
1 parent 96e3be3 commit a96e84f

File tree

4 files changed

+91
-41
lines changed

4 files changed

+91
-41
lines changed

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,30 @@
1010
* [教程文档](https://opensource.dingtalk.com/developerpedia/docs/explore/tutorials/stream/overview)
1111
* [常见问题](https://opensource.dingtalk.com/developerpedia/docs/learn/stream/faq)
1212
* [Stream 模式共创群](https://opensource.dingtalk.com/developerpedia/docs/explore/support/?via=moon-group)
13+
14+
### 调试方法
15+
16+
1、创建企业内部应用
17+
18+
进入钉钉开发者后台,创建企业内部应用,获取ClientID(即 AppKey)和ClientSecret( 即AppSecret)。
19+
20+
2、开通Stream 模式的机器人
21+
22+
进入开发者后台新建的应用,点击应用能力 - 添加应用能力 - 机器人,完善机器人信息,选择stream模式并发布。
23+
24+
3、使用demo项目测试,启动服务:
25+
26+
a、获取demo项目
27+
28+
git clone [email protected]:open-dingtalk/dingtalk-stream-sdk-nodejs.git
29+
b、在example/config.json里配置应用信息。
30+
31+
c、启动测试case
32+
33+
cd dingtalk-stream-sdk-nodejs
34+
yarn
35+
npm run build
36+
npm start
37+
38+
39+
注意:ts-node-esm启动ts文件调试时,ts文件内import引用的文件后缀必须是js,ts会报找不到模块异常。

example/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,22 @@ client.registerCallbackListener(TOPIC_ROBOT, async (res) => {
3636
msgtype: "text",
3737
};
3838

39+
const accessToken = await client.getAccessToken();
3940
const result = await axios({
4041
url: sessionWebhook,
4142
method: "POST",
4243
responseType: "json",
4344
data: body,
4445
headers: {
45-
"x-acs-dingtalk-access-token": client.getConfig().access_token,
46+
"x-acs-dingtalk-access-token": accessToken,
4647
},
4748
});
4849

49-
return result.data;
50+
// stream模式下,服务端推送消息到client后,会监听client响应,如果消息长时间未响应会在一定时间内(60s)重试推消息,可以通过此方法返回消息响应,避免多次接收服务端消息。
51+
// 机器人topic,可以通过socketCallBackResponse方法返回消息响应
52+
if(result?.data){
53+
client.socketCallBackResponse(res.headers.messageId, result.data);
54+
}
5055
});
5156
client
5257
.registerCallbackListener(

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/client.ts

Lines changed: 55 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import WebSocket from 'ws';
22
import axios from 'axios';
33
import EventEmitter from 'events';
4-
import { GET_TOKEN_URL, GATEWAY_URL,GraphAPIResponse } from './constants';
4+
import { TOPIC_ROBOT,GET_TOKEN_URL, GATEWAY_URL,GraphAPIResponse } from './constants.js';
55

66
export enum EventAck {
77
SUCCESS = "SUCCESS",
@@ -149,49 +149,52 @@ export class DWClient extends EventEmitter {
149149
return this;
150150
}
151151

152-
async getEndpoint() {
153-
this.printDebug('get connect endpoint by config');
154-
this.printDebug(this.config);
152+
async getAccessToken() {
155153
const result = await axios.get(
156154
`${GET_TOKEN_URL}?appkey=${this.config.clientId}&appsecret=${this.config.clientSecret}`
157155
);
158156
if (result.status === 200 && result.data.access_token) {
159157
this.config.access_token = result.data.access_token;
160-
const res = await axios({
161-
url: GATEWAY_URL,
162-
method: 'POST',
163-
responseType: 'json',
164-
data: {
165-
clientId: this.config.clientId,
166-
clientSecret: this.config.clientSecret,
167-
ua: this.config.ua,
168-
subscriptions: this.config.subscriptions,
169-
},
170-
headers: {
171-
// 这个接口得加个,否则默认返回的会是xml
172-
Accept: 'application/json',
173-
'access-token': result.data.access_token, // 'd136e657-5998-4cc4-a055-2b7ceab0f212'
174-
},
175-
});
158+
return result.data.access_token;
159+
} else {
160+
throw new Error('getAccessToken: get access_token failed');
161+
}
162+
}
176163

177-
this.printDebug('res.data ' + JSON.stringify(res.data));
178-
if (res.data) {
179-
this.config.endpoint = res.data;
180-
const { endpoint, ticket } = res.data;
181-
if (!endpoint || !ticket) {
182-
this.printDebug('endpoint or ticket is null');
183-
throw new Error('endpoint or ticket is null');
184-
}
185-
this.dw_url = `${endpoint}?ticket=${ticket}`;
186-
return this;
187-
} else {
188-
throw new Error('build: get endpoint failed');
164+
async getEndpoint() {
165+
this.printDebug('get connect endpoint by config');
166+
this.printDebug(this.config);
167+
const res = await axios({
168+
url: GATEWAY_URL,
169+
method: 'POST',
170+
responseType: 'json',
171+
data: {
172+
clientId: this.config.clientId,
173+
clientSecret: this.config.clientSecret,
174+
ua: this.config.ua,
175+
subscriptions: this.config.subscriptions,
176+
},
177+
headers: {
178+
// 这个接口得加个,否则默认返回的会是xml
179+
Accept: 'application/json'
180+
},
181+
});
182+
183+
this.printDebug('res.data ' + JSON.stringify(res.data));
184+
if (res.data) {
185+
this.config.endpoint = res.data;
186+
const { endpoint, ticket } = res.data;
187+
if (!endpoint || !ticket) {
188+
this.printDebug('endpoint or ticket is null');
189+
throw new Error('endpoint or ticket is null');
189190
}
191+
this.dw_url = `${endpoint}?ticket=${ticket}`;
192+
return this;
190193
} else {
191-
throw new Error('build: get access_token failed');
194+
throw new Error('build: get endpoint failed');
192195
}
193196
}
194-
197+
195198
_connect() {
196199
return new Promise<void>((resolve, reject) => {
197200
this.userDisconnect = false;
@@ -279,10 +282,9 @@ export class DWClient extends EventEmitter {
279282

280283
onDownStream(data: string) {
281284
this.printDebug('Received message from dingtalk websocket server');
282-
this.printDebug(data);
283-
// {"specVersion":"1.0","type":"SYSTEM","headers":{"contentType":"application/json","messageId":"c0a800e9168327945646012d43","time":"1683279456460","topic":"disconnect"},"data":"{\"reason\":\"persistent connection is timeout\"}"}
284-
// {"specVersion":"1.0","type":"CALLBACK","headers":{"appId":"9256b875-17e5-46a8-890a-bf4246dc5349","connectionId":"c3faec14-ebe9-11ed-8943-0ec429f1b9a1","contentType":"application/json","messageId":"213f1d00_853_187b7df781f_225d","time":"1683362492940","topic":"bot_got_msg"},"data":"{\"conversationId\":\"cidFbEwwavwcAsXDZbYqSBLnA==\",\"atUsers\":[{\"dingtalkId\":\"$:LWCP_v1:$25jBd/IW606RTMrGnMs9AuLMeuAztDrv\"}],\"chatbotCorpId\":\"ding9f50b15bccd16741\",\"chatbotUserId\":\"$:LWCP_v1:$25jBd/IW606RTMrGnMs9AuLMeuAztDrv\",\"msgId\":\"msgqEufncv9gVqy7ia60LYs3w==\",\"senderNick\":\"骏隆(主用钉)\",\"isAdmin\":true,\"senderStaffId\":\"01426861-1254332033\",\"sessionWebhookExpiredTime\":1683363692884,\"createAt\":1683362491872,\"senderCorpId\":\"ding9f50b15bccd16741\",\"conversationType\":\"2\",\"senderId\":\"$:LWCP_v1:$+PxJZVhRkpC139mPH6L7aw==\",\"conversationTitle\":\"机器人长链接事件测试群\",\"isInAtList\":true,\"sessionWebhook\":\"https://oapi.dingtalk.com/robot/sendBySession?session=2664f1467475bd90fcba36234c735997\",\"text\":{\"content\":\" ss\"},\"robotCode\":\"dingphtembyvlbeq2y4d\",\"msgtype\":\"text\"}"}
285+
285286
const msg = JSON.parse(data) as DWClientDownStream;
287+
this.printDebug(msg);
286288
switch (msg.type) {
287289
case 'SYSTEM':
288290
this.onSystem(msg);
@@ -369,6 +371,22 @@ export class DWClient extends EventEmitter {
369371
this.socket?.send(JSON.stringify(msg));
370372
}
371373

374+
/**
375+
* 消息响应,避免服务端重试.
376+
* stream模式下,服务端推送消息到client后,会监听client响应,如果消息长时间未响应会在一定时间内(60s)重试推消息,可以通过此方法返回消息响应,避免多次接收服务端消息。
377+
* @param messageId
378+
* @param result
379+
* @returns
380+
* @memberof DWClient
381+
* @example
382+
* ```javascript
383+
* client.socketResponse(res.headers.messageId, result.data);
384+
* ```
385+
*/
386+
socketCallBackResponse(messageId: string, result: any) {
387+
this.send(messageId, {response : result});
388+
}
389+
372390
sendGraphAPIResponse(messageId: string, value: GraphAPIResponse) {
373391
if (!messageId) {
374392
console.error('send: messageId must be defined');

0 commit comments

Comments
 (0)