alemonjs 2.1.0-alpha.30 → 2.1.0-alpha.32
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/lib/cbp/index.js +189 -274
- package/lib/cbp/router.js +161 -2
- package/lib/cbp/testone.js +0 -1
- package/lib/global.d.ts +1 -0
- package/lib/main.js +2 -1
- package/package.json +5 -3
- package/lib/typing/event/map.js +0 -20
package/lib/cbp/index.js
CHANGED
|
@@ -1,64 +1,64 @@
|
|
|
1
1
|
import Koa from 'koa';
|
|
2
2
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
3
3
|
import router from './router.js';
|
|
4
|
+
import koaStatic from 'koa-static';
|
|
4
5
|
import koaCors from '@koa/cors';
|
|
5
6
|
import { ResultCode } from '../core/code.js';
|
|
6
|
-
import {
|
|
7
|
+
import { USER_AGENT_HEADER, DEVICE_ID_HEADER, FULL_RECEIVE_HEADER, platformClient, childrenClient, fullClient, childrenBind } from './config.js';
|
|
7
8
|
import { getConfig } from '../core/config.js';
|
|
8
9
|
import * as flattedJSON from 'flatted';
|
|
9
|
-
import { connectionTestOne } from './testone.js';
|
|
10
10
|
|
|
11
|
-
function getReConnectTime() {
|
|
12
|
-
const time = 1000 * 1;
|
|
13
|
-
const curTime = time;
|
|
14
|
-
const mTime = (curTime / 1000 / 60).toFixed(2);
|
|
15
|
-
logger.info({
|
|
16
|
-
code: ResultCode.Fail,
|
|
17
|
-
message: `[ws-discord] 等待 ${mTime} 分钟后重新连接`,
|
|
18
|
-
data: null
|
|
19
|
-
});
|
|
20
|
-
return curTime;
|
|
21
|
-
}
|
|
22
11
|
const cbpServer = (port, listeningListener) => {
|
|
23
12
|
if (global.chatbotServer) {
|
|
24
13
|
delete global.chatbotServer;
|
|
25
14
|
}
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
15
|
+
const app = new Koa();
|
|
16
|
+
app.use(router.routes());
|
|
17
|
+
app.use(router.allowedMethods());
|
|
18
|
+
// 读取静态文件夹 gui
|
|
19
|
+
app.use(koaStatic('/gui'));
|
|
20
|
+
// 允许跨域
|
|
21
|
+
app.use(koaCors({
|
|
22
|
+
origin: '*', // 允许所有来源
|
|
23
|
+
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'] // 允许的 HTTP 方法
|
|
24
|
+
}));
|
|
25
|
+
const server = app.listen(port, listeningListener);
|
|
26
|
+
// 创建 WebSocketServer 并监听同一个端口
|
|
27
|
+
global.chatbotServer = new WebSocketServer({ server });
|
|
28
|
+
/**
|
|
29
|
+
*
|
|
30
|
+
* @param originId
|
|
31
|
+
* @param ws
|
|
32
|
+
*/
|
|
33
|
+
const setPlatformClient = (originId, ws) => {
|
|
34
|
+
// 仅允许有一个平台连接
|
|
35
|
+
if (platformClient.size > 0) {
|
|
36
|
+
logger.error({
|
|
37
|
+
code: ResultCode.Fail,
|
|
38
|
+
message: `平台连接已存在: ${originId}`,
|
|
39
|
+
data: null
|
|
40
|
+
});
|
|
41
|
+
ws.close(); // 关闭新连接
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// 设置平台客户端
|
|
45
|
+
platformClient.set(originId, ws);
|
|
46
|
+
// 得到平台客户端的消息
|
|
47
|
+
ws.on('message', (message) => {
|
|
48
|
+
try {
|
|
49
|
+
// 解析消息
|
|
50
|
+
const parsedMessage = flattedJSON.parse(message.toString());
|
|
51
|
+
// 1. 解析得到 actionId ,说明是消费行为请求。要广播告诉所有客户端。
|
|
52
|
+
// 2. 解析得到 name ,说明是一个事件请求。
|
|
53
|
+
// 3. 解析得到 apiId ,说明是一个接口请求。
|
|
54
|
+
logger.debug({
|
|
55
|
+
code: ResultCode.Ok,
|
|
56
|
+
message: '服务端接收到消息',
|
|
57
|
+
data: parsedMessage
|
|
58
|
+
});
|
|
59
|
+
if (parsedMessage.apiId) {
|
|
61
60
|
// 指定的设备 处理消费。终端有记录每个客户端是谁
|
|
61
|
+
const DeviceId = parsedMessage.DeviceId;
|
|
62
62
|
if (childrenClient.has(DeviceId)) {
|
|
63
63
|
const clientWs = childrenClient.get(DeviceId);
|
|
64
64
|
if (clientWs && clientWs.readyState === WebSocket.OPEN) {
|
|
@@ -81,9 +81,10 @@ const cbpServer = (port, listeningListener) => {
|
|
|
81
81
|
fullClient.delete(DeviceId);
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
}
|
|
85
|
+
else if (parsedMessage?.actionId) {
|
|
86
|
+
// 指定的设备 处理消费。终端有记录每个客户端是谁
|
|
87
|
+
const DeviceId = parsedMessage.DeviceId;
|
|
87
88
|
if (childrenClient.has(DeviceId)) {
|
|
88
89
|
const clientWs = childrenClient.get(DeviceId);
|
|
89
90
|
if (clientWs && clientWs.readyState === WebSocket.OPEN) {
|
|
@@ -106,9 +107,8 @@ const cbpServer = (port, listeningListener) => {
|
|
|
106
107
|
fullClient.delete(DeviceId);
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const handleEvent = (message, ID) => {
|
|
110
|
+
}
|
|
111
|
+
else if (parsedMessage?.name) {
|
|
112
112
|
// 全量客户端
|
|
113
113
|
fullClient.forEach((clientWs, clientId) => {
|
|
114
114
|
// 检查状态 并检查状态
|
|
@@ -122,6 +122,7 @@ const cbpServer = (port, listeningListener) => {
|
|
|
122
122
|
});
|
|
123
123
|
// 根据所在群进行分流。
|
|
124
124
|
// 确保同一个频道的消息。都流向同一个客户端。
|
|
125
|
+
const ID = parsedMessage.ChannelId || parsedMessage.GuildId || parsedMessage.DeviceId;
|
|
125
126
|
if (!ID) {
|
|
126
127
|
logger.error({
|
|
127
128
|
code: ResultCode.Fail,
|
|
@@ -198,237 +199,151 @@ const cbpServer = (port, listeningListener) => {
|
|
|
198
199
|
return;
|
|
199
200
|
}
|
|
200
201
|
clientWs.send(message);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
// 2. 解析得到 name ,说明是一个事件请求。
|
|
209
|
-
// 3. 解析得到 apiId ,说明是一个接口请求。
|
|
210
|
-
// 4. 解析得到 testID ,说明是一个测试请求。
|
|
211
|
-
logger.debug({
|
|
212
|
-
code: ResultCode.Ok,
|
|
213
|
-
message: '服务端接收到消息',
|
|
214
|
-
data: parsedMessage
|
|
215
|
-
});
|
|
216
|
-
if (parsedMessage.apiId) {
|
|
217
|
-
// 指定的设备 处理消费。终端有记录每个客户端是谁
|
|
218
|
-
const DeviceId = parsedMessage.DeviceId;
|
|
219
|
-
handleApi(DeviceId, message);
|
|
220
|
-
}
|
|
221
|
-
else if (parsedMessage?.actionId) {
|
|
222
|
-
// 指定的设备 处理消费。终端有记录每个客户端是谁
|
|
223
|
-
const DeviceId = parsedMessage.DeviceId;
|
|
224
|
-
handleAction(DeviceId, message);
|
|
225
|
-
}
|
|
226
|
-
else if (parsedMessage?.name) {
|
|
227
|
-
const ID = parsedMessage.ChannelId || parsedMessage.GuildId || parsedMessage.DeviceId;
|
|
228
|
-
handleEvent(message, ID);
|
|
229
|
-
}
|
|
230
|
-
else if (parsedMessage?.testID) {
|
|
231
|
-
// 继续解析数据。测试请求。
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
catch (error) {
|
|
235
|
-
logger.error({
|
|
236
|
-
code: ResultCode.Fail,
|
|
237
|
-
message: '服务端解析平台消息失败',
|
|
238
|
-
data: error
|
|
239
|
-
});
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
// 处理关闭事件
|
|
244
|
-
ws.on('close', () => {
|
|
245
|
-
platformClient.delete(originId);
|
|
246
|
-
logger.debug({
|
|
247
|
-
code: ResultCode.Fail,
|
|
248
|
-
message: `Client ${originId} disconnected`,
|
|
249
|
-
data: null
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
ws.on('error', err => {
|
|
253
|
-
logger.error({
|
|
254
|
-
code: ResultCode.Fail,
|
|
255
|
-
message: `Client ${originId} error`,
|
|
256
|
-
data: err
|
|
257
|
-
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
logger.error({
|
|
206
|
+
code: ResultCode.Fail,
|
|
207
|
+
message: '服务端解析平台消息失败',
|
|
208
|
+
data: error
|
|
258
209
|
});
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
// 处理关闭事件
|
|
214
|
+
ws.on('close', () => {
|
|
215
|
+
delete platformClient[originId];
|
|
216
|
+
logger.debug({
|
|
217
|
+
code: ResultCode.Fail,
|
|
218
|
+
message: `Client ${originId} disconnected`,
|
|
219
|
+
data: null
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
ws.on('error', err => {
|
|
223
|
+
logger.error({
|
|
224
|
+
code: ResultCode.Fail,
|
|
225
|
+
message: `Client ${originId} error`,
|
|
226
|
+
data: err
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
};
|
|
230
|
+
// 设置子客户端
|
|
231
|
+
const setChildrenClient = (originId, ws) => {
|
|
232
|
+
childrenClient.set(originId, ws);
|
|
233
|
+
// 得到子客户端的消息。只会是actions请求。
|
|
234
|
+
ws.on('message', (message) => {
|
|
235
|
+
// tudo
|
|
236
|
+
// 为什么 子客户端的行为,不携带目标平台的 DeviceId?
|
|
237
|
+
// 导致无法进行多个平台连接。
|
|
238
|
+
if (platformClient.size > 0) {
|
|
239
|
+
platformClient.forEach(platformWs => {
|
|
240
|
+
// 检查平台客户端状态
|
|
241
|
+
if (platformWs.readyState === WebSocket.OPEN) {
|
|
242
|
+
platformWs.send(message);
|
|
279
243
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
childrenClient.delete(originId);
|
|
284
|
-
logger.debug({
|
|
285
|
-
code: ResultCode.Fail,
|
|
286
|
-
message: `Client ${originId} disconnected`,
|
|
287
|
-
data: null
|
|
288
|
-
});
|
|
289
|
-
});
|
|
290
|
-
ws.on('error', err => {
|
|
291
|
-
logger.error({
|
|
292
|
-
code: ResultCode.Fail,
|
|
293
|
-
message: `Client ${originId} error`,
|
|
294
|
-
data: err
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
};
|
|
298
|
-
// 全量客户端
|
|
299
|
-
const setFullClient = (originId, ws) => {
|
|
300
|
-
fullClient.set(originId, ws);
|
|
301
|
-
// 处理消息事件
|
|
302
|
-
ws.on('message', (message) => {
|
|
303
|
-
// tudo
|
|
304
|
-
// 为什么 子客户端的行为,不携带目标平台的 DeviceId?
|
|
305
|
-
// 导致无法进行多个平台连接。
|
|
306
|
-
if (platformClient.size > 0) {
|
|
307
|
-
platformClient.forEach((platformWs, platformId) => {
|
|
308
|
-
// 检查平台客户端状态
|
|
309
|
-
if (platformWs.readyState === WebSocket.OPEN) {
|
|
310
|
-
platformWs.send(message);
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
-
// 如果连接已关闭,删除该平台客户端
|
|
314
|
-
platformClient.delete(platformId);
|
|
315
|
-
}
|
|
316
|
-
});
|
|
244
|
+
else {
|
|
245
|
+
// 如果连接已关闭,删除该平台客户端
|
|
246
|
+
platformClient.delete(originId);
|
|
317
247
|
}
|
|
318
248
|
});
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
};
|
|
329
|
-
// 处理客户端连接
|
|
330
|
-
global.chatbotServer.on('connection', (ws, request) => {
|
|
331
|
-
// 测试平台的连接
|
|
332
|
-
if (request.url === '/testone') {
|
|
333
|
-
connectionTestOne(ws, request);
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
// 读取请求头中的 来源
|
|
337
|
-
const headers = request.headers;
|
|
338
|
-
const origin = headers[USER_AGENT_HEADER] || USER_AGENT_HEADER_VALUE_MAP.client;
|
|
339
|
-
// 来源id
|
|
340
|
-
const originId = headers[DEVICE_ID_HEADER];
|
|
341
|
-
if (!originId) {
|
|
342
|
-
// 如果没有来源 ID,拒绝连接
|
|
343
|
-
ws.close(4000, 'Missing Device ID');
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
logger.debug({
|
|
347
|
-
code: ResultCode.Ok,
|
|
348
|
-
message: `Client ${originId} connected`,
|
|
349
|
-
data: null
|
|
350
|
-
});
|
|
351
|
-
// 根据来源进行分类
|
|
352
|
-
if (origin === USER_AGENT_HEADER_VALUE_MAP.platform) {
|
|
353
|
-
setPlatformClient(originId, ws);
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
else if (origin === USER_AGENT_HEADER_VALUE_MAP.client) {
|
|
357
|
-
// 连接时,需要给客户端发送主动消息
|
|
358
|
-
ws.send(flattedJSON.stringify({
|
|
359
|
-
active: 'sync',
|
|
360
|
-
payload: {
|
|
361
|
-
value: getConfig().value,
|
|
362
|
-
args: getConfig().argv,
|
|
363
|
-
package: {
|
|
364
|
-
version: getConfig().package?.version
|
|
365
|
-
},
|
|
366
|
-
env: {
|
|
367
|
-
login: process.env.login,
|
|
368
|
-
platform: process.env.platform,
|
|
369
|
-
port: process.env.port
|
|
370
|
-
}
|
|
371
|
-
},
|
|
372
|
-
// 主动消息
|
|
373
|
-
activeId: originId
|
|
374
|
-
}));
|
|
375
|
-
}
|
|
376
|
-
const isFullReceive = headers[FULL_RECEIVE_HEADER] === '1';
|
|
377
|
-
// 如果是全量接收
|
|
378
|
-
if (isFullReceive) {
|
|
379
|
-
setFullClient(originId, ws);
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
setChildrenClient(originId, ws);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
// 处理关闭事件
|
|
252
|
+
ws.on('close', () => {
|
|
253
|
+
delete childrenClient[originId];
|
|
254
|
+
logger.debug({
|
|
255
|
+
code: ResultCode.Fail,
|
|
256
|
+
message: `Client ${originId} disconnected`,
|
|
257
|
+
data: null
|
|
383
258
|
});
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
if (err.code === 'EADDRINUSE') {
|
|
391
|
-
logger.error({
|
|
392
|
-
code: ResultCode.FailInternal,
|
|
393
|
-
message: `端口 ${port} 已被占用,请检查是否有其他服务在运行`,
|
|
394
|
-
data: err.message
|
|
395
|
-
});
|
|
396
|
-
const reCreateTime = getReConnectTime();
|
|
397
|
-
// 清理所有客户端连接,开始重新创建服务器
|
|
398
|
-
setTimeout(() => {
|
|
399
|
-
createServer();
|
|
400
|
-
}, reCreateTime);
|
|
401
|
-
}
|
|
402
|
-
else {
|
|
403
|
-
logger.error({
|
|
404
|
-
code: ResultCode.FailInternal,
|
|
405
|
-
message: 'WebSocket server error',
|
|
406
|
-
data: err.message || 'Unknown error'
|
|
407
|
-
});
|
|
408
|
-
}
|
|
259
|
+
});
|
|
260
|
+
ws.on('error', err => {
|
|
261
|
+
logger.error({
|
|
262
|
+
code: ResultCode.Fail,
|
|
263
|
+
message: `Client ${originId} error`,
|
|
264
|
+
data: err
|
|
409
265
|
});
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
266
|
+
});
|
|
267
|
+
};
|
|
268
|
+
// 全量客户端
|
|
269
|
+
const setFullClient = (originId, ws) => {
|
|
270
|
+
fullClient.set(originId, ws);
|
|
271
|
+
// 处理消息事件
|
|
272
|
+
ws.on('message', (message) => {
|
|
273
|
+
// tudo
|
|
274
|
+
// 为什么 子客户端的行为,不携带目标平台的 DeviceId?
|
|
275
|
+
// 导致无法进行多个平台连接。
|
|
276
|
+
if (platformClient.size > 0) {
|
|
277
|
+
platformClient.forEach(platformWs => {
|
|
278
|
+
// 检查平台客户端状态
|
|
279
|
+
if (platformWs.readyState === WebSocket.OPEN) {
|
|
280
|
+
platformWs.send(message);
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
// 如果连接已关闭,删除该平台客户端
|
|
284
|
+
platformClient.delete(originId);
|
|
285
|
+
}
|
|
415
286
|
});
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
// 处理关闭事件
|
|
290
|
+
ws.on('close', () => {
|
|
291
|
+
delete fullClient[originId];
|
|
292
|
+
logger.debug({
|
|
293
|
+
code: ResultCode.Fail,
|
|
294
|
+
message: `Client ${originId} disconnected`,
|
|
295
|
+
data: null
|
|
420
296
|
});
|
|
297
|
+
});
|
|
298
|
+
};
|
|
299
|
+
// 处理客户端连接
|
|
300
|
+
global.chatbotServer.on('connection', (ws, request) => {
|
|
301
|
+
// 读取请求头中的 来源
|
|
302
|
+
const headers = request.headers;
|
|
303
|
+
const origin = headers[USER_AGENT_HEADER] || 'client';
|
|
304
|
+
// 来源id
|
|
305
|
+
const originId = headers[DEVICE_ID_HEADER];
|
|
306
|
+
if (!originId) {
|
|
307
|
+
// 如果没有来源 ID,拒绝连接
|
|
308
|
+
ws.close(4000, 'Missing Device ID');
|
|
309
|
+
return;
|
|
421
310
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
311
|
+
logger.debug({
|
|
312
|
+
code: ResultCode.Ok,
|
|
313
|
+
message: `Client ${originId} connected`,
|
|
314
|
+
data: null
|
|
315
|
+
});
|
|
316
|
+
// 根据来源进行分类
|
|
317
|
+
if (origin === 'platform') {
|
|
318
|
+
setPlatformClient(originId, ws);
|
|
428
319
|
return;
|
|
429
320
|
}
|
|
430
|
-
|
|
431
|
-
|
|
321
|
+
// 连接时,需要给客户端发送主动消息
|
|
322
|
+
ws.send(flattedJSON.stringify({
|
|
323
|
+
active: 'sync',
|
|
324
|
+
payload: {
|
|
325
|
+
value: getConfig().value,
|
|
326
|
+
args: getConfig().argv,
|
|
327
|
+
package: {
|
|
328
|
+
version: getConfig().package?.version
|
|
329
|
+
},
|
|
330
|
+
env: {
|
|
331
|
+
login: process.env.login,
|
|
332
|
+
platform: process.env.platform,
|
|
333
|
+
port: process.env.port
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
// 主动消息
|
|
337
|
+
activeId: originId
|
|
338
|
+
}));
|
|
339
|
+
const isFullReceive = headers[FULL_RECEIVE_HEADER] === '1';
|
|
340
|
+
// 如果是全量接收
|
|
341
|
+
if (isFullReceive) {
|
|
342
|
+
setFullClient(originId, ws);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
setChildrenClient(originId, ws);
|
|
346
|
+
});
|
|
432
347
|
};
|
|
433
348
|
|
|
434
349
|
export { cbpServer };
|
package/lib/cbp/router.js
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
import KoaRouter from 'koa-router';
|
|
2
|
+
import fs, { existsSync } from 'fs';
|
|
3
|
+
import path, { join, dirname } from 'path';
|
|
4
|
+
import mime from 'mime-types';
|
|
5
|
+
import { createRequire } from 'module';
|
|
2
6
|
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
const mainDirMap = new Map();
|
|
9
|
+
const formatPath = (path) => {
|
|
10
|
+
const pates = path.split('/');
|
|
11
|
+
const lastPath = pates[pates.length - 1];
|
|
12
|
+
if (lastPath.includes('.')) {
|
|
13
|
+
return path;
|
|
14
|
+
}
|
|
15
|
+
path += '.html';
|
|
16
|
+
return path;
|
|
17
|
+
};
|
|
3
18
|
const router = new KoaRouter({
|
|
4
|
-
prefix: '/
|
|
19
|
+
prefix: '/'
|
|
5
20
|
});
|
|
6
21
|
// 响应服务在线
|
|
7
|
-
router.get('/online', ctx => {
|
|
22
|
+
router.get('api/online', ctx => {
|
|
8
23
|
ctx.status = 200;
|
|
9
24
|
ctx.body = {
|
|
10
25
|
code: 200,
|
|
@@ -12,5 +27,149 @@ router.get('/online', ctx => {
|
|
|
12
27
|
data: null
|
|
13
28
|
};
|
|
14
29
|
});
|
|
30
|
+
router.get('app/{*path}', async (ctx) => {
|
|
31
|
+
if (!process.env.input) {
|
|
32
|
+
ctx.status = 400;
|
|
33
|
+
ctx.body = {
|
|
34
|
+
code: 400,
|
|
35
|
+
message: '没有主要入口文件',
|
|
36
|
+
data: null
|
|
37
|
+
};
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const rootPath = process.cwd();
|
|
41
|
+
if (ctx.path.startsWith(`/app/api`)) {
|
|
42
|
+
let mainPath = join(rootPath, process.env.input);
|
|
43
|
+
// 路径
|
|
44
|
+
if (!existsSync(mainPath)) {
|
|
45
|
+
ctx.status = 400;
|
|
46
|
+
ctx.body = {
|
|
47
|
+
code: 400,
|
|
48
|
+
message: '未找到主要入口文件',
|
|
49
|
+
data: null
|
|
50
|
+
};
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const mainDir = dirname(mainPath);
|
|
54
|
+
try {
|
|
55
|
+
const dir = join(mainDir, 'route', ctx.path);
|
|
56
|
+
if (!existsSync(dir)) {
|
|
57
|
+
ctx.status = 404;
|
|
58
|
+
ctx.body = {
|
|
59
|
+
code: 404,
|
|
60
|
+
message: `API 'route/${ctx.path}' 未找到。`,
|
|
61
|
+
data: null
|
|
62
|
+
};
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const apiModule = await import(dir);
|
|
66
|
+
await apiModule.default(ctx);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
console.error(`Error handling API request ${ctx.path}:`, err);
|
|
70
|
+
ctx.status = 500;
|
|
71
|
+
ctx.body = {
|
|
72
|
+
code: 500,
|
|
73
|
+
message: `处理 API 请求时发生错误。`,
|
|
74
|
+
error: err.message
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const resourcePath = formatPath(ctx.params?.path || 'index.html');
|
|
80
|
+
const fullPath = path.join(rootPath, resourcePath);
|
|
81
|
+
try {
|
|
82
|
+
// 返回文件
|
|
83
|
+
const file = await fs.promises.readFile(fullPath);
|
|
84
|
+
const mimeType = mime.lookup(fullPath) || 'application/octet-stream';
|
|
85
|
+
ctx.set('Content-Type', mimeType); // 自动设置响应头
|
|
86
|
+
ctx.body = file;
|
|
87
|
+
ctx.status = 200;
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
if (err.status === 404) {
|
|
91
|
+
ctx.status = 404;
|
|
92
|
+
ctx.body = {
|
|
93
|
+
code: 404,
|
|
94
|
+
message: `资源中未找到。`,
|
|
95
|
+
data: null
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
ctx.status = 500;
|
|
100
|
+
ctx.body = {
|
|
101
|
+
code: 500,
|
|
102
|
+
message: `加载资源时发生服务器错误。`,
|
|
103
|
+
error: err.message
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
router.get('apps/:app/{*path}', async (ctx) => {
|
|
109
|
+
const appName = ctx.params.app;
|
|
110
|
+
const rootPath = process.cwd();
|
|
111
|
+
const apiDir = `/apps/${appName}/api`;
|
|
112
|
+
if (ctx.path.startsWith(apiDir)) {
|
|
113
|
+
try {
|
|
114
|
+
if (!mainDirMap.has(appName)) {
|
|
115
|
+
const mainPath = require.resolve(appName);
|
|
116
|
+
// 不存在 main
|
|
117
|
+
if (!existsSync(mainPath)) {
|
|
118
|
+
ctx.status = 400;
|
|
119
|
+
ctx.body = {
|
|
120
|
+
code: 400,
|
|
121
|
+
message: '未找到主要入口文件',
|
|
122
|
+
data: null
|
|
123
|
+
};
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const mainDir = dirname(mainPath);
|
|
127
|
+
mainDirMap.set(appName, mainDir);
|
|
128
|
+
}
|
|
129
|
+
const dir = join(mainDirMap.get(appName), 'route', ctx.path?.replace(apiDir, '/api') || '');
|
|
130
|
+
const apiModule = await import(dir);
|
|
131
|
+
await apiModule.default(ctx);
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
logger.warn(`Error handling API request ${ctx.path}:`, err?.message || '');
|
|
135
|
+
ctx.status = 500;
|
|
136
|
+
ctx.body = {
|
|
137
|
+
code: 500,
|
|
138
|
+
message: `处理 API 请求时发生错误。`,
|
|
139
|
+
error: err.message
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const resourcePath = formatPath(ctx.params?.path || 'index.html');
|
|
145
|
+
const fullPath = path.join(rootPath, 'packages', appName, resourcePath);
|
|
146
|
+
try {
|
|
147
|
+
// 返回文件
|
|
148
|
+
const file = await fs.promises.readFile(fullPath);
|
|
149
|
+
const mimeType = mime.lookup(fullPath) || 'application/octet-stream';
|
|
150
|
+
ctx.set('Content-Type', mimeType); // 自动设置响应头
|
|
151
|
+
ctx.body = file;
|
|
152
|
+
ctx.status = 200;
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
if (err.status === 404) {
|
|
156
|
+
ctx.status = 404;
|
|
157
|
+
ctx.body = {
|
|
158
|
+
code: 404,
|
|
159
|
+
message: `资源 '${resourcePath}' 在子应用 '${appName}' 中未找到。`,
|
|
160
|
+
data: null
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
ctx.status = 500;
|
|
165
|
+
ctx.body = {
|
|
166
|
+
code: 500,
|
|
167
|
+
message: `加载子应用 '${appName}' 资源时发生服务器错误。`,
|
|
168
|
+
error: err.message
|
|
169
|
+
};
|
|
170
|
+
logger.warn(`Error handling API request ${ctx.path}:`, err?.message || '');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
});
|
|
15
174
|
|
|
16
175
|
export { router as default };
|
package/lib/cbp/testone.js
CHANGED
|
@@ -185,7 +185,6 @@ const connectionTestOne = (ws, _request) => {
|
|
|
185
185
|
try {
|
|
186
186
|
const messages = JSON.parse(readFileSync(messagePath, 'utf-8'));
|
|
187
187
|
const updatedMessages = messages.filter((msg) => msg.CreateAt !== CreateAt);
|
|
188
|
-
console.log('updatedMessages', updatedMessages);
|
|
189
188
|
writeFile(messagePath, JSON.stringify(updatedMessages, null, 2), error => {
|
|
190
189
|
if (error) {
|
|
191
190
|
logger.error(`写入 ${type} 消息失败:`, error);
|
package/lib/global.d.ts
CHANGED
package/lib/main.js
CHANGED
|
@@ -80,8 +80,8 @@ const start = async (options = {}) => {
|
|
|
80
80
|
// 设置环境变量
|
|
81
81
|
process.env.port = port;
|
|
82
82
|
cbpServer(port, async () => {
|
|
83
|
-
console.log(`ws://127.0.0.1:${port}`);
|
|
84
83
|
const url = `ws://127.0.0.1:${port}`;
|
|
84
|
+
logger.info(`[CBP server started at ${url}]`);
|
|
85
85
|
const isFullReceive = options?.is_full_receive || cfg.argv?.is_full_receive || cfg.value?.is_full_receive || true;
|
|
86
86
|
cbpClient(url, { isFullReceive });
|
|
87
87
|
// 加载平台服务
|
|
@@ -121,6 +121,7 @@ const start = async (options = {}) => {
|
|
|
121
121
|
}
|
|
122
122
|
// 获取入口文件
|
|
123
123
|
const input = options.input || cfg.argv?.input || cfg.value?.input || getInputExportPath();
|
|
124
|
+
process.env.input = input;
|
|
124
125
|
// 运行本地模块
|
|
125
126
|
run(input);
|
|
126
127
|
// load module
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "alemonjs",
|
|
3
|
-
"version": "2.1.0-alpha.
|
|
3
|
+
"version": "2.1.0-alpha.32",
|
|
4
4
|
"description": "bot script",
|
|
5
5
|
"author": "lemonade",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,10 +31,11 @@
|
|
|
31
31
|
"commander": "^13.1.0",
|
|
32
32
|
"file-type": "21.0.0",
|
|
33
33
|
"flatted": "^3.3.3",
|
|
34
|
-
"koa": "^
|
|
35
|
-
"koa-router": "^
|
|
34
|
+
"koa": "^3.0.1",
|
|
35
|
+
"koa-router": "^14.0.0",
|
|
36
36
|
"koa-static": "^5.0.0",
|
|
37
37
|
"log4js": "^6.9.1",
|
|
38
|
+
"mime-types": "^3.0.1",
|
|
38
39
|
"public-ip": "^7.0.1",
|
|
39
40
|
"qrcode": "^1.5.4",
|
|
40
41
|
"uuid": "11.1.0",
|
|
@@ -48,6 +49,7 @@
|
|
|
48
49
|
"@types/koa-router": "^7.4.8",
|
|
49
50
|
"@types/koa-static": "^4.0.4",
|
|
50
51
|
"@types/koa__cors": "^5.0.0",
|
|
52
|
+
"@types/mime-types": "^3.0.1",
|
|
51
53
|
"@types/uuid": "^10.0.0"
|
|
52
54
|
},
|
|
53
55
|
"bin": {
|
package/lib/typing/event/map.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
const EventsKeyEnum = [
|
|
2
|
-
'message.create',
|
|
3
|
-
'message.update',
|
|
4
|
-
'message.delete',
|
|
5
|
-
'message.reaction.add',
|
|
6
|
-
'message.reaction.remove',
|
|
7
|
-
'private.message.create',
|
|
8
|
-
'private.message.update',
|
|
9
|
-
'private.message.delete',
|
|
10
|
-
'private.friend.add',
|
|
11
|
-
'private.guild.add',
|
|
12
|
-
'channal.create',
|
|
13
|
-
'channal.delete',
|
|
14
|
-
'guild.join',
|
|
15
|
-
'guild.exit',
|
|
16
|
-
'member.add',
|
|
17
|
-
'member.remove'
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
export { EventsKeyEnum };
|