alemonjs 2.1.0-alpha.2 → 2.1.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -25
- package/lib/app/event-group.d.ts +11 -0
- package/lib/app/event-group.js +28 -0
- package/lib/app/event-middleware.d.ts +10 -2
- package/lib/app/event-middleware.js +11 -1
- package/lib/app/event-processor-event.js +1 -1
- package/lib/app/event-processor-middleware.js +1 -1
- package/lib/app/event-processor.js +1 -1
- package/lib/app/event-response.d.ts +10 -2
- package/lib/app/event-response.js +12 -1
- package/lib/app/hook-use-api.d.ts +19 -24
- package/lib/app/hook-use-api.js +136 -117
- package/lib/app/hook-use-subscribe.d.ts +20 -3
- package/lib/app/hook-use-subscribe.js +13 -1
- package/lib/app/load.js +10 -14
- package/lib/app/message-api.d.ts +28 -8
- package/lib/app/message-api.js +31 -14
- package/lib/app/message-format.d.ts +5 -12
- package/lib/app/message-format.js +16 -7
- package/lib/app/store.js +4 -3
- package/lib/cbp/actions.js +11 -7
- package/lib/cbp/api.js +37 -0
- package/lib/cbp/config.js +8 -2
- package/lib/cbp/connect.d.ts +6 -2
- package/lib/cbp/connect.js +213 -30
- package/lib/cbp/index.js +53 -6
- package/lib/{app → core}/utils.d.ts +2 -3
- package/lib/{app → core}/utils.js +3 -4
- package/lib/core/variable.js +7 -1
- package/lib/global.d.ts +18 -1
- package/lib/index.d.ts +11 -10
- package/lib/index.js +7 -7
- package/lib/main.js +6 -5
- package/lib/typing/actions.d.ts +1 -1
- package/lib/typing/apis.d.ts +18 -0
- package/lib/typing/client/index.d.ts +1 -1
- package/lib/typing/event/base/expansion.d.ts +5 -0
- package/lib/typing/event/base/guild.d.ts +4 -0
- package/lib/typing/event/base/user.d.ts +4 -0
- package/lib/typing/event/channal/index.d.ts +3 -2
- package/lib/typing/event/guild/index.d.ts +3 -2
- package/lib/typing/event/index.d.ts +13 -1
- package/lib/typing/event/interaction/index.d.ts +3 -2
- package/lib/typing/event/map.d.ts +0 -2
- package/lib/typing/event/member/index.d.ts +3 -2
- package/lib/typing/event/message/message.d.ts +6 -5
- package/lib/typing/event/message/private.message.d.ts +4 -3
- package/lib/typing/event/request/index.d.ts +3 -2
- package/lib/typing/message/button.d.ts +6 -5
- package/lib/typing/message/index.d.ts +4 -4
- package/lib/typing/message/markdown.d.ts +13 -3
- package/lib/utils.d.ts +80 -0
- package/lib/utils.js +203 -0
- package/package.json +12 -3
- package/lib/app/define-bot.d.ts +0 -17
- package/lib/app/define-bot.js +0 -39
- package/lib/cbp/index.d.ts +0 -32
- package/lib/cbp/message.js +0 -9
- package/lib/jsx.d.ts +0 -142
- package/lib/jsx.js +0 -234
package/lib/app/store.js
CHANGED
|
@@ -15,6 +15,8 @@ import log4js from 'log4js';
|
|
|
15
15
|
const createLogger = () => {
|
|
16
16
|
const logDir = process.env?.LOG_PATH ?? `./logs/${process.env.LOG_NAME ?? ''}`;
|
|
17
17
|
mkdirSync(logDir, { recursive: true });
|
|
18
|
+
// 当环境被设置为 development 时。被视为 trace
|
|
19
|
+
const level = process.env.NODE_ENV === 'development' ? 'trace' : 'info';
|
|
18
20
|
log4js.configure({
|
|
19
21
|
appenders: {
|
|
20
22
|
console: {
|
|
@@ -48,13 +50,12 @@ const createLogger = () => {
|
|
|
48
50
|
}
|
|
49
51
|
},
|
|
50
52
|
categories: {
|
|
51
|
-
default: { appenders: ['console'], level:
|
|
52
|
-
// 记录级别
|
|
53
|
+
default: { appenders: ['console'], level: level },
|
|
53
54
|
command: { appenders: ['console', 'command'], level: 'info' },
|
|
54
55
|
error: { appenders: ['console', 'command', 'error'], level: 'warn' }
|
|
55
56
|
}
|
|
56
57
|
});
|
|
57
|
-
const defaultLogger = log4js.getLogger('
|
|
58
|
+
const defaultLogger = log4js.getLogger('default');
|
|
58
59
|
const commandLogger = log4js.getLogger('command');
|
|
59
60
|
const errorLogger = log4js.getLogger('error');
|
|
60
61
|
return {
|
package/lib/cbp/actions.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ResultCode } from '../core/code.js';
|
|
2
|
-
import { createResult } from '../
|
|
2
|
+
import { createResult } from '../core/utils.js';
|
|
3
3
|
import { generateUniqueId, actionResolves, deviceId, actionTimeouts, timeoutTime } from './config.js';
|
|
4
|
+
import * as flattedJSON from 'flatted';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* 发送行为
|
|
@@ -11,20 +12,23 @@ const sendAction = (data) => {
|
|
|
11
12
|
return new Promise(resolve => {
|
|
12
13
|
actionResolves.set(actionId, resolve);
|
|
13
14
|
// 设置唯一标识符
|
|
14
|
-
data.
|
|
15
|
+
data.actionId = actionId;
|
|
15
16
|
// 设置设备 ID
|
|
16
17
|
data.DeviceId = deviceId;
|
|
17
|
-
|
|
18
|
+
// 发送消息
|
|
19
|
+
global.chatbotClient.send(flattedJSON.stringify(data));
|
|
18
20
|
// 12 秒后超时
|
|
19
21
|
const timeout = setTimeout(() => {
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
// 被清理了
|
|
23
|
+
if (!actionResolves.has(actionId) || !actionTimeouts.has(actionId)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
24
26
|
// 删除回调
|
|
25
27
|
actionResolves.delete(actionId);
|
|
26
28
|
// 删除超时器
|
|
27
29
|
actionTimeouts.delete(actionId);
|
|
30
|
+
// 不会当错误进行处理。而是传入错误码
|
|
31
|
+
resolve([createResult(ResultCode.Fail, '行为超时', null)]);
|
|
28
32
|
}, timeoutTime);
|
|
29
33
|
actionTimeouts.set(actionId, timeout);
|
|
30
34
|
});
|
package/lib/cbp/api.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ResultCode } from '../core/code.js';
|
|
2
|
+
import { createResult } from '../core/utils.js';
|
|
3
|
+
import { generateUniqueId, apiResolves, deviceId, apiTimeouts, timeoutTime } from './config.js';
|
|
4
|
+
import * as flattedJSON from 'flatted';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 发送行为
|
|
8
|
+
* @param data
|
|
9
|
+
*/
|
|
10
|
+
const sendAPI = (data) => {
|
|
11
|
+
const ApiId = generateUniqueId();
|
|
12
|
+
return new Promise(resolve => {
|
|
13
|
+
apiResolves.set(ApiId, resolve);
|
|
14
|
+
// 设置唯一标识符
|
|
15
|
+
data.apiId = ApiId;
|
|
16
|
+
// 设置设备 ID
|
|
17
|
+
data.DeviceId = deviceId;
|
|
18
|
+
// 发送消息
|
|
19
|
+
global.chatbotClient.send(flattedJSON.stringify(data));
|
|
20
|
+
// 12 秒后超时
|
|
21
|
+
const timeout = setTimeout(() => {
|
|
22
|
+
// 被清理了
|
|
23
|
+
if (!apiResolves.has(ApiId) || !apiTimeouts.has(ApiId)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// 删除回调
|
|
27
|
+
apiResolves.delete(ApiId);
|
|
28
|
+
// 删除超时器
|
|
29
|
+
apiTimeouts.delete(ApiId);
|
|
30
|
+
// 不会当错误进行处理。而是传入错误码
|
|
31
|
+
resolve([createResult(ResultCode.Fail, '接口超时', null)]);
|
|
32
|
+
}, timeoutTime);
|
|
33
|
+
apiTimeouts.set(ApiId, timeout);
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export { sendAPI };
|
package/lib/cbp/config.js
CHANGED
|
@@ -15,8 +15,12 @@ const DEVICE_ID_HEADER = 'x-device-id';
|
|
|
15
15
|
const FULL_RECEIVE_HEADER = 'x-full-receive';
|
|
16
16
|
// 行为回调
|
|
17
17
|
const actionResolves = new Map();
|
|
18
|
+
// 接口回调
|
|
19
|
+
const apiResolves = new Map();
|
|
18
20
|
// 超时器
|
|
19
21
|
const actionTimeouts = new Map();
|
|
22
|
+
// 接口超时器
|
|
23
|
+
const apiTimeouts = new Map();
|
|
20
24
|
// 分配绑定记录
|
|
21
25
|
const childrenBind = new Map();
|
|
22
26
|
// 生成唯一标识符
|
|
@@ -26,6 +30,8 @@ const generateUniqueId = () => {
|
|
|
26
30
|
// 超时时间
|
|
27
31
|
const timeoutTime = 1000 * 12; // 12秒
|
|
28
32
|
// 失败重连
|
|
29
|
-
const reconnectInterval =
|
|
33
|
+
const reconnectInterval = 1000 * 6; // 6秒
|
|
34
|
+
// 心跳间隔
|
|
35
|
+
const HEARTBEAT_INTERVAL = 1000 * 18; // 18秒
|
|
30
36
|
|
|
31
|
-
export { DEVICE_ID_HEADER, FULL_RECEIVE_HEADER, USER_AGENT_HEADER, actionResolves, actionTimeouts, childrenBind, childrenClient, deviceId, fullClient, generateUniqueId, platformClient, reconnectInterval, timeoutTime };
|
|
37
|
+
export { DEVICE_ID_HEADER, FULL_RECEIVE_HEADER, HEARTBEAT_INTERVAL, USER_AGENT_HEADER, actionResolves, actionTimeouts, apiResolves, apiTimeouts, childrenBind, childrenClient, deviceId, fullClient, generateUniqueId, platformClient, reconnectInterval, timeoutTime };
|
package/lib/cbp/connect.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Actions } from '../typing/actions.js';
|
|
2
2
|
import { EventsEnum } from '../typing/event/map.js';
|
|
3
|
+
import { Result } from '../core/utils.js';
|
|
3
4
|
import '../global.js';
|
|
5
|
+
import { Apis } from '../typing/apis.js';
|
|
4
6
|
|
|
5
7
|
type CBPClientOptions = {
|
|
6
8
|
open?: () => void;
|
|
@@ -12,12 +14,14 @@ type CBPClientOptions = {
|
|
|
12
14
|
* @param onopen
|
|
13
15
|
*/
|
|
14
16
|
declare const cbpClient: (url: string, options?: CBPClientOptions) => void;
|
|
15
|
-
type
|
|
17
|
+
type ActionReplyFunc = (data: Actions, consume: (payload: Result[]) => void) => void;
|
|
18
|
+
type ApiReplyFunc = (data: Apis, consume: (payload: Result[]) => void) => void;
|
|
16
19
|
declare const cbpPlatform: (url: string, options?: {
|
|
17
20
|
open: () => void;
|
|
18
21
|
}) => {
|
|
19
22
|
send: (data: EventsEnum) => void;
|
|
20
|
-
onactions: (reply:
|
|
23
|
+
onactions: (reply: ActionReplyFunc) => void;
|
|
24
|
+
onapis: (reply: ApiReplyFunc) => void;
|
|
21
25
|
};
|
|
22
26
|
|
|
23
27
|
export { cbpClient, cbpPlatform };
|
package/lib/cbp/connect.js
CHANGED
|
@@ -1,8 +1,67 @@
|
|
|
1
1
|
import { WebSocket } from 'ws';
|
|
2
2
|
import { onProcessor } from '../app/event-processor.js';
|
|
3
3
|
import { ResultCode } from '../core/code.js';
|
|
4
|
-
import { deviceId, FULL_RECEIVE_HEADER, DEVICE_ID_HEADER, USER_AGENT_HEADER, actionResolves, actionTimeouts, reconnectInterval } from './config.js';
|
|
4
|
+
import { deviceId, FULL_RECEIVE_HEADER, DEVICE_ID_HEADER, USER_AGENT_HEADER, apiResolves, apiTimeouts, actionResolves, actionTimeouts, reconnectInterval, HEARTBEAT_INTERVAL } from './config.js';
|
|
5
|
+
import { createResult } from '../core/utils.js';
|
|
6
|
+
import * as flattedJSON from 'flatted';
|
|
5
7
|
|
|
8
|
+
// 心跳
|
|
9
|
+
const useHeartbeat = ({ ping, isConnected, terminate }) => {
|
|
10
|
+
let heartbeatTimer = null;
|
|
11
|
+
let lastPong = Date.now();
|
|
12
|
+
const stopHeartbeat = () => {
|
|
13
|
+
if (heartbeatTimer) {
|
|
14
|
+
clearInterval(heartbeatTimer);
|
|
15
|
+
heartbeatTimer = null;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const callback = () => {
|
|
19
|
+
if (isConnected()) {
|
|
20
|
+
const diff = Date.now() - lastPong;
|
|
21
|
+
const max = HEARTBEAT_INTERVAL * 2; // 最大心跳间隔
|
|
22
|
+
// 检查上次 pong 是否超时
|
|
23
|
+
if (diff > max) {
|
|
24
|
+
logger.debug({
|
|
25
|
+
code: ResultCode.Fail,
|
|
26
|
+
message: '心跳超时,断开重连',
|
|
27
|
+
data: null
|
|
28
|
+
});
|
|
29
|
+
terminate(); // 强制断开
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
ping();
|
|
33
|
+
logger.debug({
|
|
34
|
+
code: ResultCode.Ok,
|
|
35
|
+
message: `发送 ping`,
|
|
36
|
+
data: null
|
|
37
|
+
});
|
|
38
|
+
heartbeatTimer = setTimeout(callback, HEARTBEAT_INTERVAL);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
stopHeartbeat(); // 如果连接已关闭,停止心跳
|
|
42
|
+
terminate(); // 强制断开
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const startHeartbeat = () => {
|
|
46
|
+
lastPong = Date.now();
|
|
47
|
+
stopHeartbeat();
|
|
48
|
+
callback();
|
|
49
|
+
};
|
|
50
|
+
const control = {
|
|
51
|
+
start: startHeartbeat,
|
|
52
|
+
stop: stopHeartbeat,
|
|
53
|
+
pong: () => {
|
|
54
|
+
// 收到 pong,说明连接正常
|
|
55
|
+
lastPong = Date.now();
|
|
56
|
+
logger.debug({
|
|
57
|
+
code: ResultCode.Ok,
|
|
58
|
+
message: `收到 pong`,
|
|
59
|
+
data: null
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
return [control];
|
|
64
|
+
};
|
|
6
65
|
/**
|
|
7
66
|
* CBP 客户端
|
|
8
67
|
* @param url
|
|
@@ -17,6 +76,27 @@ const cbpClient = (url, options = {}) => {
|
|
|
17
76
|
delete global.chatbotClient;
|
|
18
77
|
}
|
|
19
78
|
const { open = () => { }, isFullReceive = true } = options;
|
|
79
|
+
const [heartbeatControl] = useHeartbeat({
|
|
80
|
+
ping: () => {
|
|
81
|
+
global?.chatbotClient?.ping?.();
|
|
82
|
+
},
|
|
83
|
+
isConnected: () => {
|
|
84
|
+
return global?.chatbotClient && global?.chatbotClient?.readyState === WebSocket.OPEN;
|
|
85
|
+
},
|
|
86
|
+
terminate: () => {
|
|
87
|
+
try {
|
|
88
|
+
// 强制断开连接
|
|
89
|
+
global?.chatbotClient?.terminate?.();
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
logger.debug({
|
|
93
|
+
code: ResultCode.Fail,
|
|
94
|
+
message: '强制断开连接失败',
|
|
95
|
+
data: error
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
20
100
|
const start = () => {
|
|
21
101
|
global.chatbotClient = new WebSocket(url, {
|
|
22
102
|
headers: {
|
|
@@ -25,15 +105,21 @@ const cbpClient = (url, options = {}) => {
|
|
|
25
105
|
[FULL_RECEIVE_HEADER]: isFullReceive ? '1' : '0'
|
|
26
106
|
}
|
|
27
107
|
});
|
|
28
|
-
global.chatbotClient.on('open',
|
|
108
|
+
global.chatbotClient.on('open', () => {
|
|
109
|
+
open();
|
|
110
|
+
heartbeatControl.start(); // 启动心跳
|
|
111
|
+
});
|
|
112
|
+
global.chatbotClient.on('pong', () => {
|
|
113
|
+
heartbeatControl.pong(); // 更新 pong 时间
|
|
114
|
+
});
|
|
29
115
|
// 客户端接收,被标准化的平台消息
|
|
30
116
|
global.chatbotClient.on('message', message => {
|
|
31
117
|
try {
|
|
32
118
|
// 解析消息
|
|
33
|
-
const parsedMessage =
|
|
119
|
+
const parsedMessage = flattedJSON.parse(message.toString());
|
|
34
120
|
logger.debug({
|
|
35
121
|
code: ResultCode.Ok,
|
|
36
|
-
message: '
|
|
122
|
+
message: '客户端接收到消息',
|
|
37
123
|
data: parsedMessage
|
|
38
124
|
});
|
|
39
125
|
if (parsedMessage?.activeId) {
|
|
@@ -47,19 +133,46 @@ const cbpClient = (url, options = {}) => {
|
|
|
47
133
|
}
|
|
48
134
|
}
|
|
49
135
|
}
|
|
50
|
-
else if (parsedMessage?.
|
|
51
|
-
// 如果有
|
|
52
|
-
const resolve =
|
|
136
|
+
else if (parsedMessage?.apiId) {
|
|
137
|
+
// 如果有 apiId,说明是一个接口请求。要进行处理
|
|
138
|
+
const resolve = apiResolves.get(parsedMessage.apiId);
|
|
53
139
|
if (resolve) {
|
|
140
|
+
apiResolves.delete(parsedMessage.apiId);
|
|
54
141
|
// 清除超时器
|
|
55
|
-
const timeout =
|
|
142
|
+
const timeout = apiTimeouts.get(parsedMessage.apiId);
|
|
56
143
|
if (timeout) {
|
|
144
|
+
apiTimeouts.delete(parsedMessage.apiId);
|
|
57
145
|
clearTimeout(timeout);
|
|
58
|
-
actionTimeouts.delete(parsedMessage.actionID);
|
|
59
146
|
}
|
|
60
147
|
// 调用回调函数
|
|
61
|
-
|
|
62
|
-
|
|
148
|
+
if (Array.isArray(parsedMessage.payload)) {
|
|
149
|
+
resolve(parsedMessage.payload);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
// 错误处理
|
|
153
|
+
resolve([createResult(ResultCode.Fail, '接口处理错误', null)]);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else if (parsedMessage?.actionId) {
|
|
158
|
+
// 如果有 actionId
|
|
159
|
+
const resolve = actionResolves.get(parsedMessage.actionId);
|
|
160
|
+
if (resolve) {
|
|
161
|
+
actionResolves.delete(parsedMessage.actionId);
|
|
162
|
+
// 清除超时器
|
|
163
|
+
const timeout = actionTimeouts.get(parsedMessage.actionId);
|
|
164
|
+
if (timeout) {
|
|
165
|
+
actionTimeouts.delete(parsedMessage.actionId);
|
|
166
|
+
clearTimeout(timeout);
|
|
167
|
+
}
|
|
168
|
+
// 调用回调函数
|
|
169
|
+
if (Array.isArray(parsedMessage.payload)) {
|
|
170
|
+
resolve(parsedMessage.payload);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
// 错误处理
|
|
174
|
+
resolve([createResult(ResultCode.Fail, '消费处理错误', null)]);
|
|
175
|
+
}
|
|
63
176
|
}
|
|
64
177
|
}
|
|
65
178
|
else if (parsedMessage.name) {
|
|
@@ -70,13 +183,14 @@ const cbpClient = (url, options = {}) => {
|
|
|
70
183
|
catch (error) {
|
|
71
184
|
logger.error({
|
|
72
185
|
code: ResultCode.Fail,
|
|
73
|
-
message: '
|
|
186
|
+
message: '客户端解析消息失败',
|
|
74
187
|
data: error
|
|
75
188
|
});
|
|
76
189
|
}
|
|
77
190
|
});
|
|
78
191
|
global.chatbotClient.on('close', () => {
|
|
79
|
-
|
|
192
|
+
heartbeatControl.stop(); // 停止心跳
|
|
193
|
+
logger.warn({
|
|
80
194
|
code: ResultCode.Fail,
|
|
81
195
|
message: '连接关闭,尝试重新连接...',
|
|
82
196
|
data: null
|
|
@@ -87,6 +201,13 @@ const cbpClient = (url, options = {}) => {
|
|
|
87
201
|
start(); // 重新连接
|
|
88
202
|
}, reconnectInterval); // 6秒后重连
|
|
89
203
|
});
|
|
204
|
+
global.chatbotClient.on('error', err => {
|
|
205
|
+
logger.error({
|
|
206
|
+
code: ResultCode.Fail,
|
|
207
|
+
message: '客户端错误',
|
|
208
|
+
data: err
|
|
209
|
+
});
|
|
210
|
+
});
|
|
90
211
|
};
|
|
91
212
|
start();
|
|
92
213
|
};
|
|
@@ -97,6 +218,26 @@ const cbpPlatform = (url, options = {
|
|
|
97
218
|
delete global.chatbotPlatform;
|
|
98
219
|
}
|
|
99
220
|
const { open = () => { } } = options;
|
|
221
|
+
const [heartbeatControl] = useHeartbeat({
|
|
222
|
+
ping: () => {
|
|
223
|
+
global?.chatbotPlatform?.ping?.();
|
|
224
|
+
},
|
|
225
|
+
isConnected: () => {
|
|
226
|
+
return global?.chatbotPlatform && global?.chatbotPlatform?.readyState === WebSocket.OPEN;
|
|
227
|
+
},
|
|
228
|
+
terminate: () => {
|
|
229
|
+
try {
|
|
230
|
+
global?.chatbotPlatform?.terminate?.();
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
logger.debug({
|
|
234
|
+
code: ResultCode.Fail,
|
|
235
|
+
message: '强制断开连接失败',
|
|
236
|
+
data: error
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
});
|
|
100
241
|
/**
|
|
101
242
|
* 发送数据
|
|
102
243
|
* @param data
|
|
@@ -104,32 +245,50 @@ const cbpPlatform = (url, options = {
|
|
|
104
245
|
const send = (data) => {
|
|
105
246
|
if (global.chatbotPlatform && global.chatbotPlatform.readyState === WebSocket.OPEN) {
|
|
106
247
|
data.DeviceId = deviceId; // 设置设备 ID
|
|
107
|
-
global.chatbotPlatform.send(
|
|
248
|
+
global.chatbotPlatform.send(flattedJSON.stringify(data));
|
|
108
249
|
}
|
|
109
250
|
};
|
|
110
|
-
const
|
|
251
|
+
const actionReplys = [];
|
|
252
|
+
const apiReplys = [];
|
|
111
253
|
/**
|
|
112
254
|
* 消费数据
|
|
113
255
|
* @param data
|
|
114
256
|
* @param payload
|
|
115
257
|
*/
|
|
116
|
-
const
|
|
258
|
+
const replyAction = (data, payload) => {
|
|
117
259
|
if (global.chatbotPlatform && global.chatbotPlatform.readyState === WebSocket.OPEN) {
|
|
118
|
-
|
|
260
|
+
// 透传消费。也就是对应的设备进行处理消费。
|
|
261
|
+
global.chatbotPlatform.send(flattedJSON.stringify({
|
|
119
262
|
action: data.action,
|
|
120
263
|
payload: payload,
|
|
121
|
-
|
|
122
|
-
// 透传消费。也就是对应的设备进行处理消费。
|
|
264
|
+
actionId: data.actionId,
|
|
123
265
|
DeviceId: data.DeviceId
|
|
124
266
|
}));
|
|
125
267
|
}
|
|
126
268
|
};
|
|
269
|
+
const replyApi = (data, payload) => {
|
|
270
|
+
if (global.chatbotPlatform && global.chatbotPlatform.readyState === WebSocket.OPEN) {
|
|
271
|
+
// 透传消费。也就是对应的设备进行处理消费。
|
|
272
|
+
global.chatbotPlatform.send(flattedJSON.stringify({
|
|
273
|
+
action: data.action,
|
|
274
|
+
apiId: data.apiId,
|
|
275
|
+
DeviceId: data.DeviceId,
|
|
276
|
+
payload: payload
|
|
277
|
+
}));
|
|
278
|
+
}
|
|
279
|
+
};
|
|
127
280
|
/**
|
|
128
281
|
* 接收行为
|
|
129
282
|
* @param reply
|
|
130
283
|
*/
|
|
131
284
|
const onactions = (reply) => {
|
|
132
|
-
|
|
285
|
+
actionReplys.push(reply);
|
|
286
|
+
};
|
|
287
|
+
/**
|
|
288
|
+
* 接收接口
|
|
289
|
+
*/
|
|
290
|
+
const onapis = (reply) => {
|
|
291
|
+
apiReplys.push(reply);
|
|
133
292
|
};
|
|
134
293
|
/**
|
|
135
294
|
* 启动 WebSocket 连接
|
|
@@ -141,19 +300,34 @@ const cbpPlatform = (url, options = {
|
|
|
141
300
|
[DEVICE_ID_HEADER]: deviceId
|
|
142
301
|
}
|
|
143
302
|
});
|
|
144
|
-
global.chatbotPlatform.on('open',
|
|
303
|
+
global.chatbotPlatform.on('open', () => {
|
|
304
|
+
open();
|
|
305
|
+
heartbeatControl.start(); // 启动心跳
|
|
306
|
+
});
|
|
307
|
+
global.chatbotPlatform.on('pong', () => {
|
|
308
|
+
heartbeatControl.pong(); // 更新 pong 时间
|
|
309
|
+
});
|
|
145
310
|
global.chatbotPlatform.on('message', message => {
|
|
146
311
|
try {
|
|
147
|
-
const data =
|
|
312
|
+
const data = flattedJSON.parse(message.toString());
|
|
148
313
|
logger.debug({
|
|
149
314
|
code: ResultCode.Ok,
|
|
150
|
-
message: '
|
|
315
|
+
message: '平台端接收消息',
|
|
151
316
|
data: data
|
|
152
317
|
});
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
318
|
+
if (data.apiId) {
|
|
319
|
+
for (const cb of apiReplys) {
|
|
320
|
+
cb(data,
|
|
321
|
+
// 传入一个消费函数
|
|
322
|
+
val => replyApi(data, val));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
else if (data.actionId) {
|
|
326
|
+
for (const cb of actionReplys) {
|
|
327
|
+
cb(data,
|
|
328
|
+
// 传入一个消费函数
|
|
329
|
+
val => replyAction(data, val));
|
|
330
|
+
}
|
|
157
331
|
}
|
|
158
332
|
}
|
|
159
333
|
catch (error) {
|
|
@@ -165,9 +339,10 @@ const cbpPlatform = (url, options = {
|
|
|
165
339
|
}
|
|
166
340
|
});
|
|
167
341
|
global.chatbotPlatform.on('close', err => {
|
|
168
|
-
|
|
342
|
+
heartbeatControl.stop(); // 停止心跳
|
|
343
|
+
logger.warn({
|
|
169
344
|
code: ResultCode.Fail,
|
|
170
|
-
message: '
|
|
345
|
+
message: '平台端连接关闭,尝试重新连接...',
|
|
171
346
|
data: err
|
|
172
347
|
});
|
|
173
348
|
delete global.chatbotPlatform;
|
|
@@ -176,11 +351,19 @@ const cbpPlatform = (url, options = {
|
|
|
176
351
|
start(); // 重新连接
|
|
177
352
|
}, reconnectInterval); // 6秒后重连
|
|
178
353
|
});
|
|
354
|
+
global.chatbotPlatform.on('error', err => {
|
|
355
|
+
logger.error({
|
|
356
|
+
code: ResultCode.Fail,
|
|
357
|
+
message: '平台端错误',
|
|
358
|
+
data: err
|
|
359
|
+
});
|
|
360
|
+
});
|
|
179
361
|
};
|
|
180
362
|
start();
|
|
181
363
|
const client = {
|
|
182
364
|
send,
|
|
183
|
-
onactions
|
|
365
|
+
onactions,
|
|
366
|
+
onapis
|
|
184
367
|
};
|
|
185
368
|
return client;
|
|
186
369
|
};
|
package/lib/cbp/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import koaCors from '@koa/cors';
|
|
|
6
6
|
import { ResultCode } from '../core/code.js';
|
|
7
7
|
import { USER_AGENT_HEADER, DEVICE_ID_HEADER, FULL_RECEIVE_HEADER, platformClient, childrenClient, fullClient, childrenBind } from './config.js';
|
|
8
8
|
import { getConfig } from '../core/config.js';
|
|
9
|
+
import * as flattedJSON from 'flatted';
|
|
9
10
|
|
|
10
11
|
const cbpServer = (port, listeningListener) => {
|
|
11
12
|
if (global.chatbotServer) {
|
|
@@ -46,10 +47,42 @@ const cbpServer = (port, listeningListener) => {
|
|
|
46
47
|
ws.on('message', (message) => {
|
|
47
48
|
try {
|
|
48
49
|
// 解析消息
|
|
49
|
-
const parsedMessage =
|
|
50
|
-
// 1. 解析得到
|
|
50
|
+
const parsedMessage = flattedJSON.parse(message.toString());
|
|
51
|
+
// 1. 解析得到 actionId ,说明是消费行为请求。要广播告诉所有客户端。
|
|
51
52
|
// 2. 解析得到 name ,说明是一个事件请求。
|
|
52
|
-
|
|
53
|
+
// 3. 解析得到 apiId ,说明是一个接口请求。
|
|
54
|
+
logger.debug({
|
|
55
|
+
code: ResultCode.Ok,
|
|
56
|
+
message: '服务端接收到消息',
|
|
57
|
+
data: parsedMessage
|
|
58
|
+
});
|
|
59
|
+
if (parsedMessage.apiId) {
|
|
60
|
+
// 指定的设备 处理消费。终端有记录每个客户端是谁
|
|
61
|
+
const DeviceId = parsedMessage.DeviceId;
|
|
62
|
+
if (childrenClient.has(DeviceId)) {
|
|
63
|
+
const clientWs = childrenClient.get(DeviceId);
|
|
64
|
+
if (clientWs && clientWs.readyState === WebSocket.OPEN) {
|
|
65
|
+
// 发送消息到指定的子客户端
|
|
66
|
+
clientWs.send(message);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// 如果连接已关闭,删除该客户端
|
|
70
|
+
childrenClient.delete(DeviceId);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else if (fullClient.has(DeviceId)) {
|
|
74
|
+
const clientWs = fullClient.get(DeviceId);
|
|
75
|
+
if (clientWs && clientWs.readyState === WebSocket.OPEN) {
|
|
76
|
+
// 发送消息到指定的全量客户端
|
|
77
|
+
clientWs.send(message);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// 如果连接已关闭,删除该客户端
|
|
81
|
+
fullClient.delete(DeviceId);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else if (parsedMessage?.actionId) {
|
|
53
86
|
// 指定的设备 处理消费。终端有记录每个客户端是谁
|
|
54
87
|
const DeviceId = parsedMessage.DeviceId;
|
|
55
88
|
if (childrenClient.has(DeviceId)) {
|
|
@@ -139,7 +172,7 @@ const cbpServer = (port, listeningListener) => {
|
|
|
139
172
|
else {
|
|
140
173
|
logger.error({
|
|
141
174
|
code: ResultCode.Fail,
|
|
142
|
-
message: '
|
|
175
|
+
message: '服务端出现意外,无法绑定客户端',
|
|
143
176
|
data: null
|
|
144
177
|
});
|
|
145
178
|
}
|
|
@@ -171,7 +204,7 @@ const cbpServer = (port, listeningListener) => {
|
|
|
171
204
|
catch (error) {
|
|
172
205
|
logger.error({
|
|
173
206
|
code: ResultCode.Fail,
|
|
174
|
-
message: '
|
|
207
|
+
message: '服务端解析平台消息失败',
|
|
175
208
|
data: error
|
|
176
209
|
});
|
|
177
210
|
return;
|
|
@@ -186,6 +219,13 @@ const cbpServer = (port, listeningListener) => {
|
|
|
186
219
|
data: null
|
|
187
220
|
});
|
|
188
221
|
});
|
|
222
|
+
ws.on('error', err => {
|
|
223
|
+
logger.error({
|
|
224
|
+
code: ResultCode.Fail,
|
|
225
|
+
message: `Client ${originId} error`,
|
|
226
|
+
data: err
|
|
227
|
+
});
|
|
228
|
+
});
|
|
189
229
|
};
|
|
190
230
|
// 设置子客户端
|
|
191
231
|
const setChildrenClient = (originId, ws) => {
|
|
@@ -217,6 +257,13 @@ const cbpServer = (port, listeningListener) => {
|
|
|
217
257
|
data: null
|
|
218
258
|
});
|
|
219
259
|
});
|
|
260
|
+
ws.on('error', err => {
|
|
261
|
+
logger.error({
|
|
262
|
+
code: ResultCode.Fail,
|
|
263
|
+
message: `Client ${originId} error`,
|
|
264
|
+
data: err
|
|
265
|
+
});
|
|
266
|
+
});
|
|
220
267
|
};
|
|
221
268
|
// 全量客户端
|
|
222
269
|
const setFullClient = (originId, ws) => {
|
|
@@ -272,7 +319,7 @@ const cbpServer = (port, listeningListener) => {
|
|
|
272
319
|
return;
|
|
273
320
|
}
|
|
274
321
|
// 连接时,需要给客户端发送主动消息
|
|
275
|
-
ws.send(
|
|
322
|
+
ws.send(flattedJSON.stringify({
|
|
276
323
|
active: 'sync',
|
|
277
324
|
payload: {
|
|
278
325
|
value: getConfig().value,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Dirent } from 'fs';
|
|
2
|
-
import { ResultCode } from '
|
|
2
|
+
import { ResultCode } from './code.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* 将字符串转为定长字符串
|
|
@@ -30,8 +30,7 @@ declare const useUserHashKey: (event: {
|
|
|
30
30
|
declare const createEventName: (url: string, appKey: string) => string;
|
|
31
31
|
/**
|
|
32
32
|
* 将字符串转为数字
|
|
33
|
-
* @
|
|
34
|
-
* @returns
|
|
33
|
+
* @deprecated 已废弃
|
|
35
34
|
*/
|
|
36
35
|
declare const stringToNumber: (str: string, size?: number) => number;
|
|
37
36
|
/**
|
|
@@ -2,8 +2,8 @@ import crypto from 'crypto';
|
|
|
2
2
|
import fs, { existsSync, readdirSync } from 'fs';
|
|
3
3
|
import path, { join } from 'path';
|
|
4
4
|
import { createRequire } from 'module';
|
|
5
|
-
import { ResultCode } from '
|
|
6
|
-
import { file_suffix_response } from '
|
|
5
|
+
import { ResultCode } from './code.js';
|
|
6
|
+
import { file_suffix_response } from './variable.js';
|
|
7
7
|
|
|
8
8
|
const require = createRequire(import.meta.url);
|
|
9
9
|
/**
|
|
@@ -48,8 +48,7 @@ const createEventName = (url, appKey) => {
|
|
|
48
48
|
};
|
|
49
49
|
/**
|
|
50
50
|
* 将字符串转为数字
|
|
51
|
-
* @
|
|
52
|
-
* @returns
|
|
51
|
+
* @deprecated 已废弃
|
|
53
52
|
*/
|
|
54
53
|
const stringToNumber = (str, size = 33) => {
|
|
55
54
|
let hash = 5381;
|