alemonjs 2.1.4 → 2.1.6

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.
Files changed (76) hide show
  1. package/lib/adapter.js +111 -0
  2. package/lib/app/load.d.ts +14 -0
  3. package/lib/app/load.js +167 -0
  4. package/lib/app/store.js +11 -0
  5. package/lib/cbp/actions.js +48 -0
  6. package/lib/cbp/api.js +48 -0
  7. package/lib/cbp/client.d.ts +10 -0
  8. package/lib/cbp/client.js +159 -0
  9. package/lib/cbp/config.js +43 -0
  10. package/lib/cbp/connect.d.ts +27 -0
  11. package/lib/cbp/connect.js +62 -0
  12. package/lib/cbp/hello.html.js +47 -0
  13. package/lib/cbp/platform.d.ts +18 -0
  14. package/lib/cbp/platform.js +170 -0
  15. package/lib/cbp/processor/actions.js +5 -1
  16. package/lib/cbp/processor/api.js +5 -1
  17. package/lib/cbp/processor/heandle.js +5 -1
  18. package/lib/cbp/processor/hello.html.d.ts +1 -0
  19. package/lib/cbp/processor/hello.html.js +47 -0
  20. package/lib/cbp/router.js +297 -0
  21. package/lib/cbp/routers/middleware.d.ts +2 -0
  22. package/lib/cbp/routers/middleware.js +40 -0
  23. package/lib/cbp/routers/utils.d.ts +2 -0
  24. package/lib/cbp/routers/utils.js +39 -0
  25. package/lib/cbp/server.d.ts +8 -0
  26. package/lib/cbp/server.js +449 -0
  27. package/lib/cbp/testone.js +288 -0
  28. package/lib/core/code.d.ts +16 -0
  29. package/lib/core/code.js +17 -0
  30. package/lib/datastructure/SinglyLinkedList.d.ts +17 -0
  31. package/lib/datastructure/SinglyLinkedList.js +73 -0
  32. package/lib/process/module.js +1 -1
  33. package/lib/process/platform.js +1 -1
  34. package/lib/typing/actions.d.ts +93 -0
  35. package/lib/typing/apis.d.ts +18 -0
  36. package/lib/typing/client/index.d.ts +67 -0
  37. package/lib/typing/cycle/index.d.ts +42 -0
  38. package/lib/typing/event/actions.d.ts +37 -0
  39. package/lib/typing/event/actions.js +72 -0
  40. package/lib/typing/event/base/expansion.d.ts +5 -0
  41. package/lib/typing/event/base/guild.d.ts +18 -0
  42. package/lib/typing/event/base/message.d.ts +28 -0
  43. package/lib/typing/event/base/platform.d.ts +16 -0
  44. package/lib/typing/event/base/user.d.ts +34 -0
  45. package/lib/typing/event/channal/index.d.ts +13 -0
  46. package/lib/typing/event/guild/index.d.ts +13 -0
  47. package/lib/typing/event/index.d.ts +94 -0
  48. package/lib/typing/event/interaction/index.d.ts +14 -0
  49. package/lib/typing/event/map.d.ts +36 -0
  50. package/lib/typing/event/member/index.d.ts +14 -0
  51. package/lib/typing/event/message/message.d.ts +23 -0
  52. package/lib/typing/event/message/private.message.d.ts +16 -0
  53. package/lib/typing/event/request/index.d.ts +13 -0
  54. package/lib/typing/logger/index.d.ts +49 -0
  55. package/lib/typing/message/ark.d.ts +46 -0
  56. package/lib/typing/message/button.d.ts +32 -0
  57. package/lib/typing/message/image.d.ts +23 -0
  58. package/lib/typing/message/index.d.ts +16 -0
  59. package/lib/typing/message/link.d.ts +12 -0
  60. package/lib/typing/message/markdown.d.ts +91 -0
  61. package/lib/typing/message/mention.d.ts +16 -0
  62. package/lib/typing/message/text.d.ts +12 -0
  63. package/lib/typing/package/index.d.ts +12 -0
  64. package/lib/typing/state/index.d.ts +5 -0
  65. package/lib/typing/store/res.d.ts +75 -0
  66. package/lib/typing/subscribe/index.d.ts +30 -0
  67. package/package.json +2 -2
  68. package/dist/alemonjs.es.js +0 -47516
  69. package/lib/cbp/core/connection-manager.d.ts +0 -18
  70. package/lib/cbp/core/connection-manager.js +0 -67
  71. package/lib/cbp/core/constants.d.ts +0 -19
  72. package/lib/cbp/core/constants.js +0 -23
  73. package/lib/cbp/core/load-balancer.d.ts +0 -37
  74. package/lib/cbp/core/load-balancer.js +0 -118
  75. package/lib/cbp/processor/request-handler.d.ts +0 -17
  76. package/lib/cbp/processor/request-handler.js +0 -65
package/lib/adapter.js ADDED
@@ -0,0 +1,111 @@
1
+ import { fork } from 'child_process';
2
+ import { createRequire } from 'module';
3
+
4
+ const require = createRequire(import.meta.url);
5
+ /**
6
+ * 启动平台平台连接,优先使用子进程(fork),如不支持或平台连接未响应,则自动降级为 import 动态加载。
7
+ * 自动兼容新老版本平台连接。
8
+ * @param modulePath 平台连接模块绝对路径(require.resolve 得到的)
9
+ * @param env 环境变量对象
10
+ * @param logger 日志对象(需实现 info/warn/error)
11
+ */
12
+ function startAdapterWithFallback() {
13
+ let modulePath = '';
14
+ try {
15
+ modulePath = require.resolve(process.env.platform);
16
+ }
17
+ catch {
18
+ void import(process.env.platform).then(res => res?.default());
19
+ return;
20
+ }
21
+ let imported = false; // 标记 import 是否已成功,避免重复
22
+ const startByFork = () => {
23
+ if (imported) {
24
+ return; // 如果已经 import 成功,不再 fork
25
+ }
26
+ let restarted = false;
27
+ let ready = false;
28
+ let child;
29
+ const restart = () => {
30
+ if (restarted || imported) {
31
+ return;
32
+ }
33
+ restarted = true;
34
+ if (child) {
35
+ child.removeAllListeners();
36
+ try {
37
+ child.kill();
38
+ }
39
+ catch { }
40
+ }
41
+ setTimeout(() => {
42
+ startByFork();
43
+ }, 3000);
44
+ };
45
+ try {
46
+ child = fork(modulePath);
47
+ // 超时
48
+ const checkTimeout = async () => {
49
+ if (!ready && !imported) {
50
+ logger?.warn?.('平台连接未及时响应(未发送 ready 消息),降级为 import 加载');
51
+ try {
52
+ child?.kill();
53
+ }
54
+ catch { }
55
+ await startByImport();
56
+ }
57
+ };
58
+ const timer = setTimeout(() => void checkTimeout(), 2000);
59
+ child.on('exit', (code, signal) => {
60
+ clearTimeout(timer);
61
+ if (!imported) {
62
+ logger?.warn?.(`平台连接子进程已退出,code=${code}, signal=${signal},3秒后自动重启`);
63
+ restart();
64
+ }
65
+ });
66
+ child.on('message', msg => {
67
+ try {
68
+ const data = typeof msg === 'string' ? JSON.parse(msg) : msg;
69
+ if (data?.type === 'ready') {
70
+ ready = true;
71
+ clearTimeout(timer);
72
+ logger?.info?.('平台连接已就绪(子进程 fork 模式)');
73
+ }
74
+ }
75
+ catch (err) {
76
+ logger?.error?.('平台连接进程通信数据格式错误', err);
77
+ }
78
+ });
79
+ }
80
+ catch (err) {
81
+ logger?.warn?.('fork 启动平台连接失败,将尝试 import 加载', err);
82
+ void startByImport();
83
+ }
84
+ };
85
+ const startByImport = async () => {
86
+ if (imported) {
87
+ return;
88
+ }
89
+ imported = true;
90
+ try {
91
+ let importPath = modulePath;
92
+ if (!importPath.startsWith('file://')) {
93
+ importPath = 'file://' + importPath;
94
+ }
95
+ const mod = await import(importPath);
96
+ if (typeof mod.default === 'function') {
97
+ await mod.default();
98
+ logger?.info?.('通过 import 启动平台连接完成');
99
+ }
100
+ else {
101
+ logger?.warn?.('通过 import 启动平台连接,但未找到默认导出函数');
102
+ }
103
+ }
104
+ catch (err) {
105
+ logger?.error?.('import 启动平台连接失败', err);
106
+ }
107
+ };
108
+ startByFork();
109
+ }
110
+
111
+ export { startAdapterWithFallback };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 加载子模块
3
+ * @param mainPath
4
+ * @param appName
5
+ * @throws {Error} - 如果 mainPath 无效,抛出错误。
6
+ */
7
+ declare const loadChildren: (mainPath: string, appName: string) => Promise<void>;
8
+ /**
9
+ * 模块文件
10
+ * @param app
11
+ */
12
+ declare const loadChildrenFile: (appName: string) => Promise<void>;
13
+
14
+ export { loadChildren, loadChildrenFile };
@@ -0,0 +1,167 @@
1
+ import { dirname, join } from 'path';
2
+ import { existsSync } from 'fs';
3
+ import { showErrorModule, getRecursiveDirFiles, createEventName } from '../core/utils.js';
4
+ import { createRequire } from 'module';
5
+ import { ChildrenApp } from './store.js';
6
+ import { ResultCode, fileSuffixMiddleware } from '../core/variable.js';
7
+
8
+ const require = createRequire(import.meta.url);
9
+ /**
10
+ * 加载子模块
11
+ * @param mainPath
12
+ * @param appName
13
+ * @throws {Error} - 如果 mainPath 无效,抛出错误。
14
+ */
15
+ const loadChildren = async (mainPath, appName) => {
16
+ if (!mainPath || typeof mainPath !== 'string') {
17
+ logger.error({
18
+ code: ResultCode.FailParams,
19
+ message: 'The module path is not correct',
20
+ data: null
21
+ });
22
+ return;
23
+ }
24
+ const mainDir = dirname(mainPath);
25
+ const App = new ChildrenApp(appName);
26
+ try {
27
+ const moduleApp = await import(`file://${mainPath}`);
28
+ if (!moduleApp?.default) {
29
+ throw new Error('The Children is not default');
30
+ }
31
+ if (!moduleApp?.default?._name) {
32
+ throw new Error('The Children name is not correct');
33
+ }
34
+ if (moduleApp.default._name !== 'app') {
35
+ throw new Error('The Children name is not correct');
36
+ }
37
+ if (!moduleApp?.default?.callback) {
38
+ throw new Error('The Children callback is not correct');
39
+ }
40
+ let app = null;
41
+ if (typeof moduleApp?.default?.callback !== 'function') {
42
+ app = moduleApp?.default.callback;
43
+ }
44
+ else {
45
+ app = await moduleApp.default.callback();
46
+ }
47
+ App.pushSycle(app);
48
+ const unMounted = async (e) => {
49
+ showErrorModule(e);
50
+ // 卸载
51
+ App.un();
52
+ try {
53
+ app?.unMounted && (await app.unMounted(e));
54
+ }
55
+ catch (e) {
56
+ // 卸载周期出意外,不需要进行卸载
57
+ showErrorModule(e);
58
+ }
59
+ };
60
+ // onCreated 创建
61
+ try {
62
+ app?.onCreated && (await app?.onCreated());
63
+ }
64
+ catch (e) {
65
+ unMounted(e);
66
+ // 出错了,结束后续的操作。
67
+ return;
68
+ }
69
+ // onMounted 加载
70
+ try {
71
+ /**
72
+ * load response files
73
+ */
74
+ const appsDir = join(mainDir, 'apps');
75
+ const appsFiles = getRecursiveDirFiles(appsDir);
76
+ // 使用 新 目录 response
77
+ const responseDir = join(mainDir, 'response');
78
+ const responseFiles = getRecursiveDirFiles(responseDir);
79
+ const files = [...appsFiles, ...responseFiles];
80
+ const resData = [];
81
+ for (const file of files) {
82
+ // 切掉 mainDir
83
+ const url = file.path.replace(mainDir, '');
84
+ const stateKey = createEventName(url, appName);
85
+ const reesponse = {
86
+ input: mainDir,
87
+ dir: dirname(file.path),
88
+ path: file.path,
89
+ name: file.name,
90
+ stateKey,
91
+ appName: appName
92
+ };
93
+ resData.push(reesponse);
94
+ }
95
+ App.pushResponse(resData);
96
+ /**
97
+ * load middleware files
98
+ */
99
+ const mwDir = join(mainDir, 'middleware');
100
+ const mwFiles = getRecursiveDirFiles(mwDir, item => fileSuffixMiddleware.test(item.name));
101
+ const mwData = [];
102
+ for (const file of mwFiles) {
103
+ // 切掉 mainDir
104
+ const url = file.path.replace(mainDir, '');
105
+ const stateKey = createEventName(url, appName);
106
+ const middleware = {
107
+ input: mainDir,
108
+ dir: dirname(file.path),
109
+ path: file.path,
110
+ name: file.name,
111
+ stateKey,
112
+ appName: appName
113
+ };
114
+ mwData.push(middleware);
115
+ }
116
+ App.pushMiddleware(mwData);
117
+ App.on();
118
+ try {
119
+ app?.onMounted && (await app.onMounted({ response: resData, middleware: mwData }));
120
+ }
121
+ catch (e) {
122
+ unMounted(e);
123
+ }
124
+ }
125
+ catch (e) {
126
+ unMounted(e);
127
+ }
128
+ // unMounted 卸载
129
+ }
130
+ catch (e) {
131
+ showErrorModule(e);
132
+ // 卸载
133
+ App.un();
134
+ }
135
+ };
136
+ /**
137
+ * 模块文件
138
+ * @param app
139
+ */
140
+ const loadChildrenFile = async (appName) => {
141
+ if (typeof appName !== 'string') {
142
+ logger.error({
143
+ code: ResultCode.FailParams,
144
+ message: 'The module name is not correct',
145
+ data: null
146
+ });
147
+ return;
148
+ }
149
+ try {
150
+ const mainPath = require.resolve(appName);
151
+ // 不存在 main
152
+ if (!existsSync(mainPath)) {
153
+ logger.error({
154
+ code: ResultCode.FailParams,
155
+ message: 'The main file does not exist,' + mainPath,
156
+ data: null
157
+ });
158
+ return;
159
+ }
160
+ loadChildren(mainPath, appName);
161
+ }
162
+ catch (e) {
163
+ showErrorModule(e);
164
+ }
165
+ };
166
+
167
+ export { loadChildren, loadChildrenFile };
package/lib/app/store.js CHANGED
@@ -3,6 +3,17 @@ import { mkdirSync } from 'node:fs';
3
3
  import log4js from 'log4js';
4
4
 
5
5
  const createLogger = () => {
6
+ if (process.env.BROWSER_ENV === 'browser') {
7
+ return {
8
+ trace: console.trace.bind(console),
9
+ debug: console.debug.bind(console),
10
+ info: console.info.bind(console),
11
+ mark: console.info.bind(console),
12
+ warn: console.warn.bind(console),
13
+ error: console.error.bind(console),
14
+ fatal: console.error.bind(console)
15
+ };
16
+ }
6
17
  const logDir = process.env?.LOG_PATH ?? `./logs/${process.env.LOG_NAME ?? ''}`;
7
18
  mkdirSync(logDir, { recursive: true });
8
19
  const level = process.env.NODE_ENV === 'development' ? 'trace' : 'info';
@@ -0,0 +1,48 @@
1
+ import { ResultCode } from '../core/variable.js';
2
+ import { createResult } from '../core/utils.js';
3
+ import { generateUniqueId, deviceId, actionResolves, actionTimeouts, timeoutTime } from './config.js';
4
+ import * as flattedJSON from 'flatted';
5
+
6
+ /**
7
+ * 发送行为
8
+ * @param data
9
+ */
10
+ const sendAction = (data) => {
11
+ const actionId = generateUniqueId();
12
+ return new Promise(resolve => {
13
+ // 设置唯一标识符
14
+ data.actionId = actionId;
15
+ // 设置设备 ID
16
+ data.DeviceId = deviceId;
17
+ // 沙盒模式
18
+ if (global.sandbox) {
19
+ if (!global.testoneClient) {
20
+ return resolve([createResult(ResultCode.Fail, '未连接到客户端', null)]);
21
+ }
22
+ // 发送消息
23
+ global.testoneClient.send(flattedJSON.stringify(data));
24
+ }
25
+ else {
26
+ global.chatbotClient.send(flattedJSON.stringify(data));
27
+ }
28
+ // 设置回调函数
29
+ actionResolves.set(actionId, resolve);
30
+ // 超时
31
+ const timeout = setTimeout(() => {
32
+ // 被清理了
33
+ if (!actionResolves.has(actionId) || !actionTimeouts.has(actionId)) {
34
+ return;
35
+ }
36
+ // 删除回调
37
+ actionResolves.delete(actionId);
38
+ // 删除超时器
39
+ actionTimeouts.delete(actionId);
40
+ // 不会当错误进行处理。而是传入错误码
41
+ resolve([createResult(ResultCode.Fail, '行为超时', null)]);
42
+ }, timeoutTime);
43
+ // 设置超时
44
+ actionTimeouts.set(actionId, timeout);
45
+ });
46
+ };
47
+
48
+ export { sendAction };
package/lib/cbp/api.js ADDED
@@ -0,0 +1,48 @@
1
+ import { ResultCode } from '../core/variable.js';
2
+ import { createResult } from '../core/utils.js';
3
+ import { generateUniqueId, deviceId, apiResolves, 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
+ // 设置唯一标识符
14
+ data.apiId = ApiId;
15
+ // 设置设备 ID
16
+ data.DeviceId = deviceId;
17
+ // 沙盒模式
18
+ if (global.sandbox) {
19
+ if (!global.testoneClient) {
20
+ return resolve([createResult(ResultCode.Fail, '未连接到客户端', null)]);
21
+ }
22
+ // 发送消息
23
+ global.testoneClient.send(flattedJSON.stringify(data));
24
+ }
25
+ else {
26
+ // 发送消息
27
+ global.chatbotClient.send(flattedJSON.stringify(data));
28
+ }
29
+ apiResolves.set(ApiId, resolve);
30
+ // 超时
31
+ const timeout = setTimeout(() => {
32
+ // 被清理了
33
+ if (!apiResolves.has(ApiId) || !apiTimeouts.has(ApiId)) {
34
+ return;
35
+ }
36
+ // 删除回调
37
+ apiResolves.delete(ApiId);
38
+ // 删除超时器
39
+ apiTimeouts.delete(ApiId);
40
+ // 不会当错误进行处理。而是传入错误码
41
+ resolve([createResult(ResultCode.Fail, '接口超时', null)]);
42
+ }, timeoutTime);
43
+ // 设置超时
44
+ apiTimeouts.set(ApiId, timeout);
45
+ });
46
+ };
47
+
48
+ export { sendAPI };
@@ -0,0 +1,10 @@
1
+ import { CBPClientOptions } from './typings.js';
2
+
3
+ /**
4
+ * CBP 客户端
5
+ * @param url
6
+ * @param onopen
7
+ */
8
+ declare const cbpClient: (url: string, options?: CBPClientOptions) => void;
9
+
10
+ export { cbpClient };
@@ -0,0 +1,159 @@
1
+ import { WebSocket } from 'ws';
2
+ import { onProcessor } from '../app/event-processor.js';
3
+ import { ResultCode } from '../core/variable.js';
4
+ import { deviceId, FULL_RECEIVE_HEADER, DEVICE_ID_HEADER, USER_AGENT_HEADER, apiResolves, apiTimeouts, actionResolves, actionTimeouts, reconnectInterval } from './config.js';
5
+ import { createResult } from '../core/utils.js';
6
+ import * as flattedJSON from 'flatted';
7
+ import { useHeartbeat } from './connect.js';
8
+
9
+ /**
10
+ * CBP 客户端
11
+ * @param url
12
+ * @param onopen
13
+ */
14
+ const cbpClient = (url, options = {}) => {
15
+ /**
16
+ * 纯 cbpClient 连接,会没有 一些 全局变量。
17
+ * 需要在此处进行判断并设置
18
+ */
19
+ if (global.chatbotClient) {
20
+ delete global.chatbotClient;
21
+ }
22
+ const { open = () => { }, isFullReceive = true } = options;
23
+ const [heartbeatControl] = useHeartbeat({
24
+ ping: () => {
25
+ global?.chatbotClient?.ping?.();
26
+ },
27
+ isConnected: () => {
28
+ return global?.chatbotClient && global?.chatbotClient?.readyState === WebSocket.OPEN;
29
+ },
30
+ terminate: () => {
31
+ try {
32
+ // 强制断开连接
33
+ global?.chatbotClient?.terminate?.();
34
+ }
35
+ catch (error) {
36
+ logger.debug({
37
+ code: ResultCode.Fail,
38
+ message: '强制断开连接失败',
39
+ data: error
40
+ });
41
+ }
42
+ }
43
+ });
44
+ const start = () => {
45
+ global.chatbotClient = new WebSocket(url, {
46
+ headers: {
47
+ [USER_AGENT_HEADER]: 'client',
48
+ [DEVICE_ID_HEADER]: deviceId,
49
+ [FULL_RECEIVE_HEADER]: isFullReceive ? '1' : '0'
50
+ }
51
+ });
52
+ global.chatbotClient.on('open', () => {
53
+ open();
54
+ heartbeatControl.start(); // 启动心跳
55
+ });
56
+ global.chatbotClient.on('pong', () => {
57
+ heartbeatControl.pong(); // 更新 pong 时间
58
+ });
59
+ // 客户端接收,被标准化的平台消息
60
+ global.chatbotClient.on('message', message => {
61
+ try {
62
+ // 解析消息
63
+ const parsedMessage = flattedJSON.parse(message.toString());
64
+ logger.debug({
65
+ code: ResultCode.Ok,
66
+ message: '客户端接收到消息',
67
+ data: parsedMessage
68
+ });
69
+ if (parsedMessage?.activeId) {
70
+ // 主端端主动消息。
71
+ if (parsedMessage.active === 'sync') {
72
+ const configs = parsedMessage.payload;
73
+ // env 同步
74
+ const env = configs.env || {};
75
+ for (const key in env) {
76
+ process.env[key] = env[key];
77
+ }
78
+ }
79
+ }
80
+ else if (parsedMessage?.apiId) {
81
+ // 如果有 apiId,说明是一个接口请求。要进行处理
82
+ const resolve = apiResolves.get(parsedMessage.apiId);
83
+ if (resolve) {
84
+ apiResolves.delete(parsedMessage.apiId);
85
+ // 清除超时器
86
+ const timeout = apiTimeouts.get(parsedMessage.apiId);
87
+ if (timeout) {
88
+ apiTimeouts.delete(parsedMessage.apiId);
89
+ clearTimeout(timeout);
90
+ }
91
+ // 调用回调函数
92
+ if (Array.isArray(parsedMessage.payload)) {
93
+ resolve(parsedMessage.payload);
94
+ }
95
+ else {
96
+ // 错误处理
97
+ resolve([createResult(ResultCode.Fail, '接口处理错误', null)]);
98
+ }
99
+ }
100
+ }
101
+ else if (parsedMessage?.actionId) {
102
+ // 如果有 actionId
103
+ const resolve = actionResolves.get(parsedMessage.actionId);
104
+ if (resolve) {
105
+ actionResolves.delete(parsedMessage.actionId);
106
+ // 清除超时器
107
+ const timeout = actionTimeouts.get(parsedMessage.actionId);
108
+ if (timeout) {
109
+ actionTimeouts.delete(parsedMessage.actionId);
110
+ clearTimeout(timeout);
111
+ }
112
+ // 调用回调函数
113
+ if (Array.isArray(parsedMessage.payload)) {
114
+ resolve(parsedMessage.payload);
115
+ }
116
+ else {
117
+ // 错误处理
118
+ resolve([createResult(ResultCode.Fail, '消费处理错误', null)]);
119
+ }
120
+ }
121
+ }
122
+ else if (parsedMessage.name) {
123
+ // 如果有 name,说明是一个事件请求。要进行处理
124
+ onProcessor(parsedMessage.name, parsedMessage, parsedMessage.value);
125
+ }
126
+ }
127
+ catch (error) {
128
+ logger.error({
129
+ code: ResultCode.Fail,
130
+ message: '客户端解析消息失败',
131
+ data: error
132
+ });
133
+ }
134
+ });
135
+ global.chatbotClient.on('close', () => {
136
+ heartbeatControl.stop(); // 停止心跳
137
+ logger.warn({
138
+ code: ResultCode.Fail,
139
+ message: '连接关闭,尝试重新连接...',
140
+ data: null
141
+ });
142
+ delete global.chatbotClient;
143
+ // 重新连接逻辑
144
+ setTimeout(() => {
145
+ start(); // 重新连接
146
+ }, reconnectInterval); // 6秒后重连
147
+ });
148
+ global.chatbotClient.on('error', err => {
149
+ logger.error({
150
+ code: ResultCode.Fail,
151
+ message: '客户端错误',
152
+ data: err
153
+ });
154
+ });
155
+ };
156
+ start();
157
+ };
158
+
159
+ export { cbpClient };
@@ -0,0 +1,43 @@
1
+ import { v4 } from 'uuid';
2
+
3
+ // 子客户端
4
+ const childrenClient = new Map();
5
+ // 平台客户端
6
+ const platformClient = new Map();
7
+ // 全量客户端
8
+ const fullClient = new Map();
9
+ const deviceId = v4();
10
+ // 连接类型
11
+ const USER_AGENT_HEADER = 'user-agent';
12
+ //
13
+ const USER_AGENT_HEADER_VALUE_MAP = {
14
+ platform: 'platform',
15
+ client: 'client',
16
+ testone: 'testone'
17
+ };
18
+ // 设备 ID
19
+ const DEVICE_ID_HEADER = 'x-device-id';
20
+ // 是否全量接收
21
+ const FULL_RECEIVE_HEADER = 'x-full-receive';
22
+ // 行为回调
23
+ const actionResolves = new Map();
24
+ // 接口回调
25
+ const apiResolves = new Map();
26
+ // 超时器
27
+ const actionTimeouts = new Map();
28
+ // 接口超时器
29
+ const apiTimeouts = new Map();
30
+ // 分配绑定记录
31
+ const childrenBind = new Map();
32
+ // 生成唯一标识符
33
+ const generateUniqueId = () => {
34
+ return Date.now().toString(36) + Math.random().toString(36).substring(2);
35
+ };
36
+ // 超时时间
37
+ const timeoutTime = 1000 * 60 * 3; // 3分钟
38
+ // 失败重连
39
+ const reconnectInterval = 1000 * 6; // 6秒
40
+ // 心跳间隔
41
+ const HEARTBEAT_INTERVAL = 1000 * 18; // 18秒
42
+
43
+ export { DEVICE_ID_HEADER, FULL_RECEIVE_HEADER, HEARTBEAT_INTERVAL, USER_AGENT_HEADER, USER_AGENT_HEADER_VALUE_MAP, actionResolves, actionTimeouts, apiResolves, apiTimeouts, childrenBind, childrenClient, deviceId, fullClient, generateUniqueId, platformClient, reconnectInterval, timeoutTime };
@@ -0,0 +1,27 @@
1
+ import { Actions } from '../typing/actions.js';
2
+ import { EventsEnum } from '../typing/event/map.js';
3
+ import { Result } from '../core/utils.js';
4
+ import '../global.js';
5
+ import { Apis } from '../typing/apis.js';
6
+
7
+ type CBPClientOptions = {
8
+ open?: () => void;
9
+ isFullReceive?: boolean;
10
+ };
11
+ /**
12
+ * CBP 客户端
13
+ * @param url
14
+ * @param onopen
15
+ */
16
+ declare const cbpClient: (url: string, options?: CBPClientOptions) => void;
17
+ type ActionReplyFunc = (data: Actions, consume: (payload: Result[]) => void) => void;
18
+ type ApiReplyFunc = (data: Apis, consume: (payload: Result[]) => void) => void;
19
+ declare const cbpPlatform: (url: string, options?: {
20
+ open: () => void;
21
+ }) => {
22
+ send: (data: EventsEnum) => void;
23
+ onactions: (reply: ActionReplyFunc) => void;
24
+ onapis: (reply: ApiReplyFunc) => void;
25
+ };
26
+
27
+ export { cbpClient, cbpPlatform };