@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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # NapLink
2
2
 
3
- > 现代化的 NapCat WebSocket 客户端 SDK
3
+ > 现代化的 NapCat WebSocket TypeScript/JavaScript 客户端 SDK
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/naplink)](https://www.npmjs.com/package/naplink)
5
+ [![npm](https://img.shields.io/npm/v/@naplink/naplink?logo=npm)](https://www.npmjs.com/package/@naplink/naplink)
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.4+-blue)](https://www.typescriptlang.org/)
7
7
  [![Node.js](https://img.shields.io/badge/Node.js-18+-green)](https://nodejs.org/)
8
8
  [![Documentation](https://img.shields.io/badge/docs-naplink.github.io-blue)](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/NapLink/NapGram) - 基于 NapLink 的 QQ ↔ Telegram 消息桥接
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
- npm install
67
+ pnpm install
72
68
 
73
69
  # 开发模式
74
- npm run dev
70
+ pnpm dev
75
71
 
76
72
  # 构建
77
- npm run build
73
+ pnpm build
78
74
 
79
75
  # 测试
80
- npm test
76
+ pnpm test
81
77
 
82
78
  # 类型检查
83
- npm run typecheck
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
- constructor(config: NapLinkConfig, logger: Logger, onMessage: (data: string) => void, onStateChange: (state: ConnectionState) => void);
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
- constructor(config, logger, onMessage, onStateChange) {
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
- this.onStateChange(state);
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);