@meet-im/meet-bot-jssdk 0.0.7 → 1.0.0

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,6 +1,6 @@
1
1
  # @meet-im/meet-bot-jssdk
2
2
 
3
- MeetIM Chatbot JavaScript SDK - 支持 Long Polling 消息获取和消息发送
3
+ MeetIM Chatbot JavaScript SDK - 支持 Long Polling 消息获取、消息发送和媒体文件处理
4
4
 
5
5
  ## 安装
6
6
 
@@ -17,14 +17,30 @@ import { MeetBot } from '@meet-im/meet-bot-jssdk'
17
17
 
18
18
  const bot = new MeetBot({
19
19
  token: 'bot_id:secret',
20
- // baseUrl: 'https://staging-meet-api.miyachat.com', // 可选,默认值
20
+ useV2: true, // 启用 getUpdatesV2,支持引用消息
21
21
  })
22
22
 
23
- bot.on('message', (msg) => {
24
- console.log('收到消息:', msg.content)
23
+ bot.on('message', ({ message, quoteMsgMap }) => {
24
+ console.log('收到消息:', message.content)
25
+
26
+ // 处理引用消息
27
+ if (message.quoteSeqID && message.sessionInfo) {
28
+ const { getConvID, getQuoteMsgKey } = await import('@meet-im/meet-bot-jssdk')
29
+ const convID = getConvID(
30
+ message.sessionInfo.firstID,
31
+ message.sessionInfo.secondID,
32
+ message.sessionInfo.sessionType,
33
+ message.sessionInfo.companyID
34
+ )
35
+ const quoteKey = getQuoteMsgKey(convID, message.quoteSeqID)
36
+ const quoteMsg = quoteMsgMap[quoteKey]
37
+ if (quoteMsg) {
38
+ console.log('引用消息:', quoteMsg.content)
39
+ }
40
+ }
25
41
 
26
- if (msg.sessionInfo) {
27
- bot.sendMessage(msg.sessionInfo, { content: `收到: ${msg.content}` })
42
+ if (message.sessionInfo) {
43
+ bot.sendMessage(message.sessionInfo, { content: `收到: ${message.content}` })
28
44
  }
29
45
  })
30
46
 
@@ -34,21 +50,21 @@ bot.startPolling()
34
50
  ### 函数式风格
35
51
 
36
52
  ```typescript
37
- import { getUpdates, sendMessage } from '@meet-im/meet-bot-jssdk'
53
+ import { getUpdatesV2, sendMessage } from '@meet-im/meet-bot-jssdk'
38
54
 
39
55
  const token = 'bot_id:secret'
40
56
  const baseUrl = 'https://staging-meet-api.miyachat.com'
41
57
 
42
- const updates = await getUpdates({ token, baseUrl })
58
+ const { msgs, quoteMsgMap } = await getUpdatesV2({ token, baseUrl })
43
59
 
44
- for (const update of updates) {
45
- if (update.message?.sessionInfo) {
46
- console.log('收到消息:', update.message.content)
60
+ for (const { message } of msgs) {
61
+ if (message.sessionInfo) {
62
+ console.log('收到消息:', message.content)
47
63
 
48
64
  await sendMessage({
49
65
  token,
50
66
  baseUrl,
51
- sessionInfo: update.message.sessionInfo,
67
+ sessionInfo: message.sessionInfo,
52
68
  msgContent: { content: '收到!' },
53
69
  })
54
70
  }
@@ -65,28 +81,37 @@ for (const update of updates) {
65
81
  new MeetBot(config: MeetBotConfig)
66
82
  ```
67
83
 
68
- | 参数 | 类型 | 必填 | 说明 |
69
- | ------------------ | ---------------- | ---- | ------------------------------------------------------ |
70
- | token | string | 是\* | Bot Token,格式:`bot_id:secret` |
71
- | botId | string \| number | 是\* | Bot ID(与 botToken 配合使用) |
72
- | botToken | string | 是\* | Bot Token(与 botId 配合使用) |
73
- | baseUrl | string | 否 | API 地址,默认 `https://staging-meet-api.miyachat.com` |
74
- | pollingLimit | number | 否 | 每次拉取消息条数,默认 100 |
75
- | longPollingTimeout | number | 否 | 长轮询超时时间(秒),默认 30 |
84
+ | 参数 | 类型 | 必填 | 说明 |
85
+ | ------------------ | ------------------ | ---- | ------------------------------------------------------ |
86
+ | token | string | 是\* | Bot Token,格式:`bot_id:secret` |
87
+ | botId | string \| number | 是\* | Bot ID(与 botToken 配合使用) |
88
+ | botToken | string | 是\* | Bot Token(与 botId 配合使用) |
89
+ | baseUrl | string | 否 | API 地址,默认 `https://staging-meet-api.miyachat.com` |
90
+ | pollingLimit | number | 否 | 每次拉取消息条数,默认 100 |
91
+ | longPollingTimeout | number | 否 | 长轮询超时时间(秒),默认 30 |
92
+ | useV2 | boolean | 否 | 使用 getUpdatesV2 接口,默认 false |
93
+ | logLevel | 'silent' \| 'info' | 否 | 日志级别,默认 'silent' |
76
94
 
77
95
  \*token 或 (botId + botToken) 二选一
78
96
 
79
97
  #### 方法
80
98
 
81
- | 方法 | 说明 |
82
- | -------------------------------------- | ------------ |
83
- | `on(event, handler)` | 监听事件 |
84
- | `off(event, handler)` | 移除事件监听 |
85
- | `startPolling(options?)` | 启动长轮询 |
86
- | `stopPolling()` | 停止轮询 |
87
- | `isPolling()` | 获取轮询状态 |
88
- | `getUpdates(options?)` | 手动获取消息 |
89
- | `sendMessage(sessionInfo, msgContent)` | 发送消息 |
99
+ | 方法 | 说明 |
100
+ | -------------------------------------- | ------------------ |
101
+ | `on(event, handler)` | 监听事件 |
102
+ | `off(event, handler)` | 移除事件监听 |
103
+ | `startPolling(options?)` | 启动长轮询 |
104
+ | `stopPolling()` | 停止轮询 |
105
+ | `isPolling()` | 获取轮询状态 |
106
+ | `getUpdates(options?)` | 手动获取消息(V1) |
107
+ | `getUpdatesV2(options?)` | 手动获取消息(V2) |
108
+ | `sendMessage(sessionInfo, msgContent)` | 发送消息 |
109
+ | `getUploadURL(params)` | 获取单文件上传地址 |
110
+ | `getMultiPartUploadURL(params)` | 获取分片上传地址 |
111
+ | `completeMultipartUpload(params)` | 完成分片上传 |
112
+ | `getAccessURL(params)` | 获取文件下载地址 |
113
+ | `uploadFile(buffer, options)` | 上传文件 |
114
+ | `sendMedia(sessionInfo, options)` | 发送媒体消息 |
90
115
 
91
116
  #### startPolling options
92
117
 
@@ -100,16 +125,16 @@ new MeetBot(config: MeetBotConfig)
100
125
 
101
126
  #### 事件
102
127
 
103
- | 事件 | 参数 | 说明 |
104
- | --------------- | ------------ | ---------- |
105
- | `message` | `MsgContent` | 收到新消息 |
106
- | `error` | `Error` | 发生错误 |
107
- | `polling_start` | `void` | 轮询开始 |
108
- | `polling_stop` | `void` | 轮询停止 |
128
+ | 事件 | 参数 | 说明 |
129
+ | --------------- | -------------------------------------- | ---------- |
130
+ | `message` | `{ message: MsgContent, quoteMsgMap }` | 收到新消息 |
131
+ | `error` | `Error` | 发生错误 |
132
+ | `polling_start` | `void` | 轮询开始 |
133
+ | `polling_stop` | `void` | 轮询停止 |
109
134
 
110
135
  ### 函数式 API
111
136
 
112
- #### getUpdates
137
+ #### getUpdates(已废弃)
113
138
 
114
139
  ```typescript
115
140
  getUpdates(params: GetUpdatesParams): Promise<BotUpdate[]>
@@ -123,6 +148,28 @@ getUpdates(params: GetUpdatesParams): Promise<BotUpdate[]>
123
148
  | offset | number | 否 | 0 | 从该 seqId 之后获取 |
124
149
  | limit | number | 否 | 100 | 最大返回条数 |
125
150
 
151
+ #### getUpdatesV2(推荐)
152
+
153
+ ```typescript
154
+ getUpdatesV2(params: GetUpdatesV2Params): Promise<GetUpdatesV2Result>
155
+ ```
156
+
157
+ | 参数 | 类型 | 必填 | 默认值 | 说明 |
158
+ | ------- | ------ | ---- | ------ | ----------------------- |
159
+ | token | string | 是 | - | Bot Token |
160
+ | baseUrl | string | 否 | - | API 地址 |
161
+ | timeout | number | 否 | 0 | Long Polling 超时(秒) |
162
+ | limit | number | 否 | 100 | 最大返回条数 |
163
+
164
+ 返回值:
165
+
166
+ ```typescript
167
+ interface GetUpdatesV2Result {
168
+ msgs: { message: MsgContent }[]
169
+ quoteMsgMap: Record<string, MsgContent> // key: "convID:seqID"
170
+ }
171
+ ```
172
+
126
173
  #### sendMessage
127
174
 
128
175
  ```typescript
@@ -136,32 +183,118 @@ sendMessage(params: SendMessageParams): Promise<SendMessageResult>
136
183
  | sessionInfo | SessionInfo | 是 | 会话信息 |
137
184
  | msgContent | MsgContent | 是 | 消息内容 |
138
185
 
186
+ ### 媒体文件 API
187
+
188
+ #### getUploadURL
189
+
190
+ 获取单文件上传签名地址。
191
+
192
+ ```typescript
193
+ getUploadURL(params): Promise<UploadURLResult>
194
+ ```
195
+
196
+ | 参数 | 类型 | 必填 | 说明 |
197
+ | -------------- | ------ | ---- | ---------------- |
198
+ | token | string | 是 | Bot Token |
199
+ | originFileName | string | 是 | 原始文件名 |
200
+ | contentType | string | 是 | MIME 类型 |
201
+ | md5 | string | 是 | 格式:`md5_hash` |
202
+ | size | number | 是 | 文件大小(字节) |
203
+
204
+ #### getAccessURL
205
+
206
+ 获取文件下载地址。
207
+
208
+ ```typescript
209
+ // 方式1:完整参数
210
+ getAccessURL({ token, firstId, secondId, sessionType, seqId, fileId })
211
+
212
+ // 方式2:使用 sessionInfo
213
+ getAccessURL({ token, sessionInfo, seqId, fileId })
214
+ ```
215
+
216
+ #### uploadFile
217
+
218
+ 上传文件(自动选择单文件或分片上传)。
219
+
220
+ ```typescript
221
+ uploadFile(token, buffer, { fileName, contentType, onProgress? })
222
+ ```
223
+
224
+ #### sendMedia
225
+
226
+ 发送媒体消息(上传并发送)。
227
+
228
+ ```typescript
229
+ sendMedia(token, sessionInfo, { buffer, fileName, contentType, content?, onProgress? })
230
+ ```
231
+
232
+ ### 工具函数
233
+
234
+ #### getConvID
235
+
236
+ 生成会话 ID。
237
+
238
+ ```typescript
239
+ import { getConvID } from '@meet-im/meet-bot-jssdk'
240
+
241
+ const convID = getConvID(firstID, secondID, sessionType, companyID?)
242
+ ```
243
+
244
+ | 会话类型 | convID 格式 |
245
+ | -------- | -------------------------------------- |
246
+ | 私聊 (1) | `min:max` 或 `min:max:companyID` |
247
+ | 群聊 (3) | `firstID+secondID` 或追加 `+companyID` |
248
+ | 频道 | `firstID_secondID` 或追加 `_companyID` |
249
+
250
+ #### getQuoteMsgKey
251
+
252
+ 生成引用消息的 key。
253
+
254
+ ```typescript
255
+ import { getQuoteMsgKey } from '@meet-im/meet-bot-jssdk'
256
+
257
+ const key = getQuoteMsgKey(convID, seqID) // "convID:seqID"
258
+ ```
259
+
139
260
  ## 类型定义
140
261
 
141
262
  ```typescript
142
263
  type SessionType = 1 | 3 // 1: 私聊, 3: 群聊
143
264
 
144
265
  interface SessionInfo {
145
- firstID: number // 会话第一方 ID
146
- secondID: number // 会话第二方 ID(私聊为用户ID,群聊为群ID)
266
+ firstID: number
267
+ secondID: number
147
268
  sessionType: SessionType
148
269
  companyID?: number
149
270
  }
150
271
 
151
272
  type MsgType = 'NORMAL' | 'RECALL' | 'QUOTE'
152
273
 
274
+ interface AttachmentInfo {
275
+ fileID: string | number
276
+ fileName?: string
277
+ filePath?: string
278
+ fileSize?: number
279
+ mimeType?: string
280
+ fileUrl?: string
281
+ }
282
+
153
283
  interface ExtraInfo {
154
284
  msgType?: MsgType
285
+ attechmentInfo?: AttachmentInfo // 单附件
286
+ attechmentInfos?: AttachmentInfo[] // 多附件
155
287
  [key: string]: unknown
156
288
  }
157
289
 
158
290
  interface MsgContent {
159
- content: string // 消息文本内容
160
- seqId?: number // 消息序列号
161
- timestamp?: number // 时间戳(秒)
162
- fromUid?: number // 发送者 UID
163
- atIds?: number[] // @用户 ID 列表
164
- extraInfo?: ExtraInfo // 额外信息
291
+ content: string
292
+ seqId?: number
293
+ timestamp?: number
294
+ fromUid?: number
295
+ atIds?: number[]
296
+ quoteSeqID?: number // 引用消息 seqID
297
+ extraInfo?: ExtraInfo
165
298
  sessionInfo?: SessionInfo
166
299
  }
167
300
 
@@ -169,6 +302,11 @@ interface BotUpdate {
169
302
  message?: MsgContent
170
303
  }
171
304
 
305
+ interface GetUpdatesV2Result {
306
+ msgs: { message: MsgContent }[]
307
+ quoteMsgMap: Record<string, MsgContent>
308
+ }
309
+
172
310
  interface SendMessageResult {
173
311
  msgContent: MsgContent
174
312
  quoteMsg: unknown
package/dist/index.cjs CHANGED
@@ -128,6 +128,11 @@ var CHUNK_RULES = [
128
128
  { maxSize: Infinity, chunks: 50 }
129
129
  // > 100MB: 50 片
130
130
  ];
131
+ var SESSION_TYPE = {
132
+ PRIVATE: 1,
133
+ GROUP: 3,
134
+ CHANNEL: 4
135
+ };
131
136
  function getChunkNum(size) {
132
137
  for (const rule of CHUNK_RULES) {
133
138
  if (size < rule.maxSize) {
@@ -136,6 +141,31 @@ function getChunkNum(size) {
136
141
  }
137
142
  return 50;
138
143
  }
144
+ function getConvID(firstID, secondID, sessionType, companyID = 1) {
145
+ let convID;
146
+ if (sessionType === SESSION_TYPE.PRIVATE) {
147
+ const min = Math.min(firstID, secondID);
148
+ const max = Math.max(firstID, secondID);
149
+ convID = `${min}:${max}`;
150
+ if (companyID > 1) {
151
+ convID += `:${companyID}`;
152
+ }
153
+ } else if (sessionType === SESSION_TYPE.GROUP) {
154
+ convID = `${firstID}+${secondID}`;
155
+ if (companyID > 1) {
156
+ convID += `+${companyID}`;
157
+ }
158
+ } else {
159
+ convID = `${firstID}_${secondID}`;
160
+ if (companyID > 1) {
161
+ convID += `_${companyID}`;
162
+ }
163
+ }
164
+ return convID;
165
+ }
166
+ function getQuoteMsgKey(convID, seqID) {
167
+ return `${convID}:${seqID}`;
168
+ }
139
169
 
140
170
  // src/utils/logger.ts
141
171
  var Logger = class {
@@ -337,9 +367,8 @@ async function getAccessURL(params) {
337
367
  sessionType: queryParams.sessionType,
338
368
  seqId: queryParams.seqId,
339
369
  fileId: queryParams.fileId,
370
+ companyId: queryParams.companyId,
340
371
  "x-oss-process": queryParams["x-oss-process"],
341
- origin: queryParams.origin,
342
- bestDomain: queryParams.bestDomain,
343
372
  printResult: queryParams.printResult ?? "1"
344
373
  }
345
374
  });
@@ -534,6 +563,20 @@ async function getUpdates(params) {
534
563
  timeout: (timeout || 0) * 1e3 + HTTP.POLLING_TIMEOUT_BUFFER
535
564
  });
536
565
  }
566
+ async function getUpdatesV2(params) {
567
+ const { token, baseUrl, timeout = API.DEFAULT_TIMEOUT, limit = API.DEFAULT_LIMIT } = params;
568
+ return request({
569
+ token,
570
+ baseUrl,
571
+ method: "POST",
572
+ path: "getUpdatesV2",
573
+ body: {
574
+ timeout,
575
+ limit
576
+ },
577
+ timeout: (timeout || 0) * 1e3 + HTTP.POLLING_TIMEOUT_BUFFER
578
+ });
579
+ }
537
580
  async function sendMessage(params) {
538
581
  const { token, baseUrl, sessionInfo, msgContent } = params;
539
582
  const hasContent = msgContent.content && msgContent.content.trim() !== "";
@@ -561,6 +604,7 @@ var MeetBot = class {
561
604
  baseUrl;
562
605
  pollingLimit;
563
606
  longPollingTimeout;
607
+ useV2;
564
608
  eventHandlers = /* @__PURE__ */ new Map();
565
609
  polling = false;
566
610
  offset = 0;
@@ -579,6 +623,7 @@ var MeetBot = class {
579
623
  this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
580
624
  this.pollingLimit = config.pollingLimit ?? POLLING.DEFAULT_LIMIT;
581
625
  this.longPollingTimeout = config.longPollingTimeout ?? POLLING.DEFAULT_TIMEOUT;
626
+ this.useV2 = config.useV2 ?? false;
582
627
  logger.setLevel(config.logLevel ?? "silent");
583
628
  }
584
629
  on(event, handler) {
@@ -618,7 +663,7 @@ var MeetBot = class {
618
663
  this.abortController = new AbortController();
619
664
  const limit = options?.limit ?? this.pollingLimit;
620
665
  const timeout = options?.timeout ?? this.longPollingTimeout;
621
- logger.info(`\u5F00\u59CB\u957F\u8F6E\u8BE2\u6D88\u606F... (\u6761\u6570: ${limit}, \u8D85\u65F6: ${timeout}s)`);
666
+ logger.info(`\u5F00\u59CB\u957F\u8F6E\u8BE2\u6D88\u606F... (\u6761\u6570: ${limit}, \u8D85\u65F6: ${timeout}s, V2: ${this.useV2})`);
622
667
  this.emit("polling_start", void 0);
623
668
  const retryDelay = options?.retryDelay ?? POLLING.DEFAULT_RETRY_DELAY;
624
669
  const maxRetries = options?.maxRetries ?? POLLING.DEFAULT_MAX_RETRIES;
@@ -626,20 +671,35 @@ var MeetBot = class {
626
671
  let retryCount = 0;
627
672
  while (this.polling) {
628
673
  try {
629
- const updates = await getUpdates({
630
- token: this.token,
631
- baseUrl: this.baseUrl,
632
- timeout,
633
- offset: this.offset,
634
- limit
635
- });
636
- retryCount = 0;
637
- for (const update of updates) {
638
- if (update.message) {
639
- this.emit("message", update.message);
640
- this.offset = (update.message.seqId || 0) + 1;
674
+ if (this.useV2) {
675
+ const result = await getUpdatesV2({
676
+ token: this.token,
677
+ baseUrl: this.baseUrl,
678
+ timeout,
679
+ limit
680
+ });
681
+ retryCount = 0;
682
+ for (const msgUpdate of result.msgs) {
683
+ this.emit("message", { message: msgUpdate.message, quoteMsgMap: result.quoteMsgMap });
684
+ this.offset = (msgUpdate.message.seqId || 0) + 1;
641
685
  onOffsetUpdate?.(this.offset);
642
686
  }
687
+ } else {
688
+ const updates = await getUpdates({
689
+ token: this.token,
690
+ baseUrl: this.baseUrl,
691
+ timeout,
692
+ offset: this.offset,
693
+ limit
694
+ });
695
+ retryCount = 0;
696
+ for (const update of updates) {
697
+ if (update.message) {
698
+ this.emit("message", { message: update.message, quoteMsgMap: {} });
699
+ this.offset = (update.message.seqId || 0) + 1;
700
+ onOffsetUpdate?.(this.offset);
701
+ }
702
+ }
643
703
  }
644
704
  await this.sleep(POLLING.SUCCESS_DELAY);
645
705
  } catch (error) {
@@ -670,6 +730,13 @@ var MeetBot = class {
670
730
  ...options
671
731
  });
672
732
  }
733
+ async getUpdatesV2(options) {
734
+ return getUpdatesV2({
735
+ token: this.token,
736
+ baseUrl: this.baseUrl,
737
+ ...options
738
+ });
739
+ }
673
740
  async sendMessage(sessionInfo, msgContent) {
674
741
  return sendMessage({
675
742
  token: this.token,
@@ -712,10 +779,18 @@ var MeetBot = class {
712
779
  * 获取文件下载地址
713
780
  */
714
781
  async getAccessURL(params) {
782
+ const apiParams = "sessionInfo" in params ? {
783
+ firstId: params.sessionInfo.firstID,
784
+ secondId: params.sessionInfo.secondID,
785
+ sessionType: params.sessionInfo.sessionType,
786
+ seqId: params.seqId,
787
+ fileId: params.fileId,
788
+ printResult: params.printResult
789
+ } : params;
715
790
  return getAccessURL({
716
791
  token: this.token,
717
792
  baseUrl: this.baseUrl,
718
- ...params
793
+ ...apiParams
719
794
  });
720
795
  }
721
796
  /**
@@ -744,13 +819,17 @@ exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
744
819
  exports.HTTP = HTTP;
745
820
  exports.MeetBot = MeetBot;
746
821
  exports.POLLING = POLLING;
822
+ exports.SESSION_TYPE = SESSION_TYPE;
747
823
  exports.UPLOAD = UPLOAD;
748
824
  exports.completeMultipartUpload = completeMultipartUpload;
749
825
  exports.computeMD5 = computeMD5;
750
826
  exports.getAccessURL = getAccessURL;
751
827
  exports.getChunkNum = getChunkNum;
828
+ exports.getConvID = getConvID;
752
829
  exports.getMultiPartUploadURL = getMultiPartUploadURL;
830
+ exports.getQuoteMsgKey = getQuoteMsgKey;
753
831
  exports.getUpdates = getUpdates;
832
+ exports.getUpdatesV2 = getUpdatesV2;
754
833
  exports.getUploadURL = getUploadURL;
755
834
  exports.sendMediaMessage = sendMediaMessage;
756
835
  exports.sendMessage = sendMessage;