@naplink/naplink 0.0.2 → 0.0.4
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 +8 -12
- package/dist/index.d.ts +33 -6
- package/dist/index.js +49 -14
- package/dist/index.js.map +1 -1
- package/package.json +57 -55
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# NapLink
|
|
2
2
|
|
|
3
|
-
> 现代化的 NapCat WebSocket 客户端 SDK
|
|
3
|
+
> 现代化的 NapCat WebSocket TypeScript/JavaScript 客户端 SDK
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/@naplink/naplink)
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
7
|
[](https://nodejs.org/)
|
|
8
8
|
[](https://naplink.github.io/)
|
|
@@ -27,16 +27,12 @@
|
|
|
27
27
|
- [事件处理](https://naplink.github.io/guide/events) - 事件系统详解
|
|
28
28
|
- [最佳实践](https://naplink.github.io/guide/best-practices) - 生产环境建议
|
|
29
29
|
- [架构设计](https://naplink.github.io/guide/architecture) - 内部实现
|
|
30
|
-
- 最佳实践项目:[NapGram](https://github.com/
|
|
30
|
+
- 最佳实践项目:[NapGram](https://github.com/NapGram/NapGram) - 基于 NapLink 的 QQ ↔ Telegram 消息桥接
|
|
31
31
|
|
|
32
32
|
## 📦 安装
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
npm install naplink
|
|
36
|
-
# 或
|
|
37
35
|
pnpm add naplink
|
|
38
|
-
# 或
|
|
39
|
-
yarn add naplink
|
|
40
36
|
```
|
|
41
37
|
|
|
42
38
|
## 🔧 配置示例
|
|
@@ -68,19 +64,19 @@ const client = new NapLink({
|
|
|
68
64
|
|
|
69
65
|
```bash
|
|
70
66
|
# 安装依赖
|
|
71
|
-
|
|
67
|
+
pnpm install
|
|
72
68
|
|
|
73
69
|
# 开发模式
|
|
74
|
-
|
|
70
|
+
pnpm dev
|
|
75
71
|
|
|
76
72
|
# 构建
|
|
77
|
-
|
|
73
|
+
pnpm build
|
|
78
74
|
|
|
79
75
|
# 测试
|
|
80
|
-
|
|
76
|
+
pnpm test
|
|
81
77
|
|
|
82
78
|
# 类型检查
|
|
83
|
-
|
|
79
|
+
pnpm typecheck
|
|
84
80
|
```
|
|
85
81
|
|
|
86
82
|
## 📄 许可证
|
package/dist/index.d.ts
CHANGED
|
@@ -101,13 +101,15 @@ declare class ConnectionManager {
|
|
|
101
101
|
private logger;
|
|
102
102
|
private onMessage;
|
|
103
103
|
private onStateChange;
|
|
104
|
+
private emitter?;
|
|
104
105
|
private ws?;
|
|
105
106
|
private state;
|
|
106
107
|
private reconnectService;
|
|
107
108
|
private heartbeatService?;
|
|
108
109
|
private connectPromise?;
|
|
109
110
|
private connectTimeout?;
|
|
110
|
-
|
|
111
|
+
private wasReconnecting;
|
|
112
|
+
constructor(config: NapLinkConfig, logger: Logger, onMessage: (data: string) => void, onStateChange: (state: ConnectionState, wasReconnecting?: boolean) => void, emitter?: any | undefined);
|
|
111
113
|
/**
|
|
112
114
|
* 连接到WebSocket服务器
|
|
113
115
|
*/
|
|
@@ -350,6 +352,15 @@ interface PokeNotice extends NotifyNotice {
|
|
|
350
352
|
user_id: number;
|
|
351
353
|
group_id?: number;
|
|
352
354
|
}
|
|
355
|
+
interface GroupGrayTipNotice extends NotifyNotice {
|
|
356
|
+
sub_type: 'gray_tip';
|
|
357
|
+
group_id: number;
|
|
358
|
+
user_id: number;
|
|
359
|
+
message_id: number;
|
|
360
|
+
busi_id: string;
|
|
361
|
+
content: string;
|
|
362
|
+
raw_info: unknown;
|
|
363
|
+
}
|
|
353
364
|
|
|
354
365
|
interface RequestEvent extends BaseEvent {
|
|
355
366
|
post_type: 'request';
|
|
@@ -439,6 +450,7 @@ type MessageApi = {
|
|
|
439
450
|
group_id?: number | string;
|
|
440
451
|
user_id?: number | string;
|
|
441
452
|
count?: number;
|
|
453
|
+
cookie?: string;
|
|
442
454
|
}): Promise<any>;
|
|
443
455
|
sendGroupPoke(groupId: number | string, userId: number | string): Promise<any>;
|
|
444
456
|
sendFriendPoke(userId: number | string): Promise<any>;
|
|
@@ -479,8 +491,8 @@ type GroupApi = {
|
|
|
479
491
|
};
|
|
480
492
|
|
|
481
493
|
type FileApi = {
|
|
482
|
-
uploadGroupFile(groupId: number | string, file: string | Buffer | Uint8Array | NodeJS.ReadableStream, name: string): Promise<any>;
|
|
483
|
-
uploadPrivateFile(userId: number | string, file: string | Buffer | Uint8Array | NodeJS.ReadableStream, name: string): Promise<any>;
|
|
494
|
+
uploadGroupFile(groupId: number | string, file: string | Buffer | Uint8Array | NodeJS.ReadableStream, name: string, folder?: string, uploadFile?: boolean): Promise<any>;
|
|
495
|
+
uploadPrivateFile(userId: number | string, file: string | Buffer | Uint8Array | NodeJS.ReadableStream, name: string, uploadFile?: boolean): Promise<any>;
|
|
484
496
|
setGroupPortrait(groupId: number | string, file: string | Buffer | Uint8Array | NodeJS.ReadableStream): Promise<any>;
|
|
485
497
|
getGroupFileSystemInfo(groupId: number | string): Promise<any>;
|
|
486
498
|
getGroupRootFiles(groupId: number | string): Promise<any>;
|
|
@@ -650,6 +662,13 @@ type NapCatApi = {
|
|
|
650
662
|
setGroupSign(groupId: number | string): Promise<any>;
|
|
651
663
|
sendGroupSign(groupId: number | string): Promise<any>;
|
|
652
664
|
fetchCustomFace(params?: any): Promise<any>;
|
|
665
|
+
getEmojiLikes(params: {
|
|
666
|
+
message_id: string;
|
|
667
|
+
emoji_id: string;
|
|
668
|
+
emoji_type?: string;
|
|
669
|
+
group_id?: string;
|
|
670
|
+
count?: number;
|
|
671
|
+
}): Promise<any>;
|
|
653
672
|
getClientkey(): Promise<any>;
|
|
654
673
|
clickInlineKeyboardButton(params: {
|
|
655
674
|
group_id: number | string;
|
|
@@ -800,6 +819,14 @@ type OneBotApiMethods = {
|
|
|
800
819
|
group_id?: number | string;
|
|
801
820
|
user_id?: number | string;
|
|
802
821
|
count?: number;
|
|
822
|
+
cookie?: string;
|
|
823
|
+
}): Promise<any>;
|
|
824
|
+
getEmojiLikes(params: {
|
|
825
|
+
message_id: string;
|
|
826
|
+
emoji_id: string;
|
|
827
|
+
emoji_type?: string;
|
|
828
|
+
group_id?: string;
|
|
829
|
+
count?: number;
|
|
803
830
|
}): Promise<any>;
|
|
804
831
|
fetchCustomFace(params?: any): Promise<any>;
|
|
805
832
|
sendGroupPoke(groupId: number | string, userId: number | string): Promise<any>;
|
|
@@ -827,8 +854,8 @@ type OneBotApiMethods = {
|
|
|
827
854
|
deleteEssenceMessage(messageId: number | string): Promise<any>;
|
|
828
855
|
setGroupSpecialTitle(groupId: number | string, userId: number | string, specialTitle: string, duration?: number): Promise<any>;
|
|
829
856
|
sendLike(userId: number | string, times?: number): Promise<any>;
|
|
830
|
-
uploadGroupFile(groupId: number | string, file: string, name: string): Promise<any>;
|
|
831
|
-
uploadPrivateFile(userId: number | string, file: string, name: string): Promise<any>;
|
|
857
|
+
uploadGroupFile(groupId: number | string, file: string, name: string, folder?: string, uploadFile?: boolean): Promise<any>;
|
|
858
|
+
uploadPrivateFile(userId: number | string, file: string, name: string, uploadFile?: boolean): Promise<any>;
|
|
832
859
|
getStrangerInfo(userId: number | string, noCache?: boolean): Promise<any>;
|
|
833
860
|
getVersionInfo(): Promise<any>;
|
|
834
861
|
handleFriendRequest(flag: string, approve?: boolean, remark?: string): Promise<any>;
|
|
@@ -1098,4 +1125,4 @@ type MarkdownSegment = {
|
|
|
1098
1125
|
};
|
|
1099
1126
|
type OneBotMessageSegment = TextSegment | AtSegment | FaceSegment | ReplySegment | ImageSegment | RecordSegment | VideoSegment | FileSegment | JsonSegment | XmlSegment | MarkdownSegment;
|
|
1100
1127
|
|
|
1101
|
-
export { type Anonymous, ApiError, ApiTimeoutError, type AtSegment, type BaseEvent, ConnectionClosedError, ConnectionError, ConnectionState, type FaceSegment, type FileInfo, type FileSegment, type FriendAddNotice, type FriendRecallNotice, type FriendRequest, type GroupAdminNotice, type GroupDecreaseNotice, type GroupHonorInfo, type GroupIncreaseNotice, type GroupInviteRequest, type GroupJoinRequest, type GroupMessageEvent, type GroupRecallNotice, type GroupRequest, type GroupSystemMessages, type GroupUploadNotice, type HeartbeatMetaEvent, type ImageSegment, InvalidConfigError, type JsonSegment, type LifecycleMetaEvent, type Logger, type MarkdownSegment, MaxReconnectAttemptsError, type MessageEvent, type MessageSegment, type MetaEvent, NapLink, type NapLinkConfig, NapLinkError, type NoticeEvent, type NotifyNotice, OneBotApi, type OneBotMessageSegment, type PartialNapLinkConfig, type PokeNotice, type PostType, type PrivateMessageEvent, type RecordSegment, type ReplySegment, type RequestEvent, type Sender, type TextSegment, type VideoSegment, type XmlSegment, NapLink as default };
|
|
1128
|
+
export { type Anonymous, ApiError, ApiTimeoutError, type AtSegment, type BaseEvent, ConnectionClosedError, ConnectionError, ConnectionState, type FaceSegment, type FileInfo, type FileSegment, type FriendAddNotice, type FriendRecallNotice, type FriendRequest, type GroupAdminNotice, type GroupDecreaseNotice, type GroupGrayTipNotice, type GroupHonorInfo, type GroupIncreaseNotice, type GroupInviteRequest, type GroupJoinRequest, type GroupMessageEvent, type GroupRecallNotice, type GroupRequest, type GroupSystemMessages, type GroupUploadNotice, type HeartbeatMetaEvent, type ImageSegment, InvalidConfigError, type JsonSegment, type LifecycleMetaEvent, type Logger, type MarkdownSegment, MaxReconnectAttemptsError, type MessageEvent, type MessageSegment, type MetaEvent, NapLink, type NapLinkConfig, NapLinkError, type NoticeEvent, type NotifyNotice, OneBotApi, type OneBotMessageSegment, type PartialNapLinkConfig, type PokeNotice, type PostType, type PrivateMessageEvent, type RecordSegment, type ReplySegment, type RequestEvent, type Sender, type TextSegment, type VideoSegment, type XmlSegment, NapLink as default };
|
package/dist/index.js
CHANGED
|
@@ -328,11 +328,14 @@ function startHeartbeat(deps) {
|
|
|
328
328
|
|
|
329
329
|
// src/core/connection/retry-handler.ts
|
|
330
330
|
function handleReconnect(deps) {
|
|
331
|
-
const { config, logger, reconnectService, setState, connect } = deps;
|
|
331
|
+
const { config, logger, reconnectService, setState, connect, onMaxAttemptsReached } = deps;
|
|
332
332
|
if (!reconnectService.hasRemainingAttempts()) {
|
|
333
333
|
setState("disconnected" /* DISCONNECTED */);
|
|
334
334
|
const err = new MaxReconnectAttemptsError(reconnectService.getMaxAttempts());
|
|
335
335
|
logger.error("\u81EA\u52A8\u91CD\u8FDE\u5DF2\u8FBE\u4E0A\u9650\uFF0C\u505C\u6B62\u91CD\u8FDE", err);
|
|
336
|
+
if (onMaxAttemptsReached) {
|
|
337
|
+
onMaxAttemptsReached();
|
|
338
|
+
}
|
|
336
339
|
return false;
|
|
337
340
|
}
|
|
338
341
|
setState("reconnecting" /* RECONNECTING */);
|
|
@@ -341,6 +344,9 @@ function handleReconnect(deps) {
|
|
|
341
344
|
setState("disconnected" /* DISCONNECTED */);
|
|
342
345
|
const err = new MaxReconnectAttemptsError(config.reconnect.maxAttempts);
|
|
343
346
|
logger.error("\u81EA\u52A8\u91CD\u8FDE\u5DF2\u8FBE\u4E0A\u9650\uFF0C\u505C\u6B62\u91CD\u8FDE", err);
|
|
347
|
+
if (onMaxAttemptsReached) {
|
|
348
|
+
onMaxAttemptsReached();
|
|
349
|
+
}
|
|
344
350
|
return false;
|
|
345
351
|
}
|
|
346
352
|
return true;
|
|
@@ -348,7 +354,7 @@ function handleReconnect(deps) {
|
|
|
348
354
|
|
|
349
355
|
// src/core/connection/close-handler.ts
|
|
350
356
|
function handleCloseEvent(deps, event) {
|
|
351
|
-
const { getState, setState, stopHeartbeat, logger, config, reconnectService, reconnect } = deps;
|
|
357
|
+
const { getState, setState, stopHeartbeat, logger, config, reconnectService, reconnect, onMaxAttemptsReached } = deps;
|
|
352
358
|
stopHeartbeat();
|
|
353
359
|
logger.info(`\u8FDE\u63A5\u5173\u95ED (code: ${event.code}, reason: ${event.reason})`);
|
|
354
360
|
if (event.code === 1e3) {
|
|
@@ -362,7 +368,9 @@ function handleCloseEvent(deps, event) {
|
|
|
362
368
|
logger,
|
|
363
369
|
reconnectService,
|
|
364
370
|
setState,
|
|
365
|
-
connect: reconnect
|
|
371
|
+
connect: reconnect,
|
|
372
|
+
onMaxAttemptsReached
|
|
373
|
+
// 传递回调
|
|
366
374
|
});
|
|
367
375
|
} else {
|
|
368
376
|
setState("disconnected" /* DISCONNECTED */);
|
|
@@ -412,11 +420,13 @@ function attachWebSocketHandlers(ws, deps, resolve, reject) {
|
|
|
412
420
|
|
|
413
421
|
// src/core/connection/manager.ts
|
|
414
422
|
var ConnectionManager = class {
|
|
415
|
-
|
|
423
|
+
// 跟踪是否处于重连状态
|
|
424
|
+
constructor(config, logger, onMessage, onStateChange, emitter) {
|
|
416
425
|
this.config = config;
|
|
417
426
|
this.logger = logger;
|
|
418
427
|
this.onMessage = onMessage;
|
|
419
428
|
this.onStateChange = onStateChange;
|
|
429
|
+
this.emitter = emitter;
|
|
420
430
|
this.reconnectService = new ReconnectService(config.reconnect, logger);
|
|
421
431
|
}
|
|
422
432
|
ws;
|
|
@@ -425,6 +435,7 @@ var ConnectionManager = class {
|
|
|
425
435
|
heartbeatService;
|
|
426
436
|
connectPromise;
|
|
427
437
|
connectTimeout;
|
|
438
|
+
wasReconnecting = false;
|
|
428
439
|
/**
|
|
429
440
|
* 连接到WebSocket服务器
|
|
430
441
|
*/
|
|
@@ -480,7 +491,15 @@ var ConnectionManager = class {
|
|
|
480
491
|
logger: this.logger,
|
|
481
492
|
config: this.config,
|
|
482
493
|
reconnectService: this.reconnectService,
|
|
483
|
-
reconnect: () => this.connect()
|
|
494
|
+
reconnect: () => this.connect(),
|
|
495
|
+
onMaxAttemptsReached: () => {
|
|
496
|
+
if (this.emitter) {
|
|
497
|
+
this.emitter.emit("connection:lost", {
|
|
498
|
+
timestamp: Date.now(),
|
|
499
|
+
attempts: this.reconnectService.getMaxAttempts()
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
484
503
|
}, event),
|
|
485
504
|
clearConnectTimeout: () => this.clearConnectTimeout()
|
|
486
505
|
},
|
|
@@ -540,7 +559,14 @@ var ConnectionManager = class {
|
|
|
540
559
|
if (this.state !== state) {
|
|
541
560
|
this.state = state;
|
|
542
561
|
this.logger.debug(`\u72B6\u6001\u53D8\u66F4: ${state}`);
|
|
543
|
-
|
|
562
|
+
if (state === "reconnecting" /* RECONNECTING */) {
|
|
563
|
+
this.wasReconnecting = true;
|
|
564
|
+
} else if (state === "connected" /* CONNECTED */) {
|
|
565
|
+
this.onStateChange(state, this.wasReconnecting);
|
|
566
|
+
this.wasReconnecting = false;
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
this.onStateChange(state, false);
|
|
544
570
|
}
|
|
545
571
|
}
|
|
546
572
|
/**
|
|
@@ -1050,11 +1076,14 @@ var MessageDispatcher = class {
|
|
|
1050
1076
|
};
|
|
1051
1077
|
|
|
1052
1078
|
// src/core/connection/state-handler.ts
|
|
1053
|
-
function handleConnectionStateChange(emitter, state) {
|
|
1079
|
+
function handleConnectionStateChange(emitter, state, wasReconnecting = false) {
|
|
1054
1080
|
emitter.emit("state_change", state);
|
|
1055
1081
|
switch (state) {
|
|
1056
1082
|
case "connected" /* CONNECTED */:
|
|
1057
1083
|
emitter.emit("connect");
|
|
1084
|
+
if (wasReconnecting) {
|
|
1085
|
+
emitter.emit("connection:restored", { timestamp: Date.now() });
|
|
1086
|
+
}
|
|
1058
1087
|
break;
|
|
1059
1088
|
case "disconnected" /* DISCONNECTED */:
|
|
1060
1089
|
emitter.emit("disconnect");
|
|
@@ -1308,13 +1337,13 @@ function createFileApi(client) {
|
|
|
1308
1337
|
return tempPath;
|
|
1309
1338
|
};
|
|
1310
1339
|
return {
|
|
1311
|
-
async uploadGroupFile(groupId, file, name) {
|
|
1340
|
+
async uploadGroupFile(groupId, file, name, folder, uploadFile = true) {
|
|
1312
1341
|
const normalized = await normalizeFileInput(file, name);
|
|
1313
|
-
return client.call("upload_group_file", { group_id: groupId, file: normalized, name });
|
|
1342
|
+
return client.call("upload_group_file", { group_id: groupId, file: normalized, name, folder, upload_file: uploadFile });
|
|
1314
1343
|
},
|
|
1315
|
-
async uploadPrivateFile(userId, file, name) {
|
|
1344
|
+
async uploadPrivateFile(userId, file, name, uploadFile = true) {
|
|
1316
1345
|
const normalized = await normalizeFileInput(file, name);
|
|
1317
|
-
return client.call("upload_private_file", { user_id: userId, file: normalized, name });
|
|
1346
|
+
return client.call("upload_private_file", { user_id: userId, file: normalized, name, upload_file: uploadFile });
|
|
1318
1347
|
},
|
|
1319
1348
|
async setGroupPortrait(groupId, file) {
|
|
1320
1349
|
return this.uploadGroupFile(groupId, file, "portrait");
|
|
@@ -1677,6 +1706,9 @@ function createNapCatApi(client) {
|
|
|
1677
1706
|
fetchCustomFace(params) {
|
|
1678
1707
|
return client.call("fetch_custom_face", params ?? {});
|
|
1679
1708
|
},
|
|
1709
|
+
getEmojiLikes(params) {
|
|
1710
|
+
return client.call("get_emoji_likes", params);
|
|
1711
|
+
},
|
|
1680
1712
|
getClientkey() {
|
|
1681
1713
|
return client.call("get_clientkey");
|
|
1682
1714
|
},
|
|
@@ -1938,6 +1970,7 @@ function bindOneBotApiMethods(api, target) {
|
|
|
1938
1970
|
getRecentContact: (count) => api.getRecentContact(count),
|
|
1939
1971
|
setMsgEmojiLike: (messageId, emojiId, set) => api.setMsgEmojiLike(messageId, emojiId, set),
|
|
1940
1972
|
fetchEmojiLike: (params) => api.fetchEmojiLike(params),
|
|
1973
|
+
getEmojiLikes: (params) => api.getEmojiLikes(params),
|
|
1941
1974
|
sendGroupPoke: (groupId, userId) => api.sendGroupPoke(groupId, userId),
|
|
1942
1975
|
sendFriendPoke: (userId) => api.sendFriendPoke(userId),
|
|
1943
1976
|
sendPoke: (targetId, groupId) => api.sendPoke(targetId, groupId),
|
|
@@ -1963,8 +1996,8 @@ function bindOneBotApiMethods(api, target) {
|
|
|
1963
1996
|
deleteEssenceMessage: (messageId) => api.deleteEssenceMessage(messageId),
|
|
1964
1997
|
setGroupSpecialTitle: (groupId, userId, specialTitle, duration = -1) => api.setGroupSpecialTitle(groupId, userId, specialTitle, duration),
|
|
1965
1998
|
sendLike: (userId, times = 1) => api.sendLike(userId, times),
|
|
1966
|
-
uploadGroupFile: (groupId, file, name) => api.uploadGroupFile(groupId, file, name),
|
|
1967
|
-
uploadPrivateFile: (userId, file, name) => api.uploadPrivateFile(userId, file, name),
|
|
1999
|
+
uploadGroupFile: (groupId, file, name, folder, uploadFile) => api.uploadGroupFile(groupId, file, name, folder, uploadFile),
|
|
2000
|
+
uploadPrivateFile: (userId, file, name, uploadFile) => api.uploadPrivateFile(userId, file, name, uploadFile),
|
|
1968
2001
|
getStrangerInfo: (userId, noCache = false) => api.getStrangerInfo(userId, noCache),
|
|
1969
2002
|
getVersionInfo: () => api.getVersionInfo(),
|
|
1970
2003
|
handleFriendRequest: (flag, approve = true, remark) => api.handleFriendRequest(flag, approve, remark),
|
|
@@ -2097,7 +2130,9 @@ var NapLink = class extends EventEmitter2 {
|
|
|
2097
2130
|
this.config,
|
|
2098
2131
|
this.logger,
|
|
2099
2132
|
(message) => this.dispatcher.dispatch(message),
|
|
2100
|
-
(state) => handleConnectionStateChange(this, state)
|
|
2133
|
+
(state, wasReconnecting) => handleConnectionStateChange(this, state, wasReconnecting || false),
|
|
2134
|
+
this
|
|
2135
|
+
// 传递 emitter 用于发送 connection:lost 事件
|
|
2101
2136
|
);
|
|
2102
2137
|
this.apiClient = new ApiClient(this.connection, this.config, this.logger);
|
|
2103
2138
|
this.dispatcher = new MessageDispatcher(this.apiClient, this.eventRouter, this.logger);
|