joytalk 0.0.1

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 ADDED
@@ -0,0 +1,640 @@
1
+ # 客服 SDK
2
+
3
+ 一个功能完善的客服 SDK,用于在项目中快速集成客服功能。
4
+
5
+ ## 特性
6
+
7
+ - ✅ TypeScript 支持
8
+ - ✅ 自动重连机制
9
+ - ✅ 心跳机制
10
+ - ✅ 事件驱动架构
11
+ - ✅ 支持多种消息类型(文本、图片、视频、文件、FAQ)
12
+ - ✅ 完整的类型定义
13
+ - ✅ 支持 CommonJS 和 ES Module
14
+ - ✅ 使用 Vite 构建,快速高效
15
+ - ✅ 文件上传支持
16
+ - ✅ 视频缩略图生成
17
+
18
+ ## 安装
19
+
20
+ ```bash
21
+ npm install joytalk
22
+ # 或
23
+ yarn add joytalk
24
+ # 或
25
+ pnpm add joytalk
26
+ ```
27
+
28
+ ## 快速开始
29
+
30
+ ### 基本使用
31
+
32
+ ```typescript
33
+ import JoyTalk, { EventType, ChatType, MessageCode } from 'joytalk';
34
+
35
+ // 创建 SDK 实例
36
+ const sdk = new JoyTalk({
37
+ token: 'your-token',
38
+ autoReconnect: true,
39
+ reconnectInterval: 3000,
40
+ maxReconnectAttempts: 5,
41
+ heartbeatTimeout: 10000,
42
+ });
43
+
44
+ // 监听连接事件
45
+ sdk.on(EventType.CONNECTED, () => {
46
+ console.log('已连接到服务器');
47
+ });
48
+
49
+ // 监听消息事件
50
+ sdk.on(EventType.MESSAGE, (message) => {
51
+ console.log('收到消息:', message);
52
+
53
+ // 根据消息类型处理
54
+ switch (message.code) {
55
+ case MessageCode.SEND_SUCCESS:
56
+ console.log('消息发送成功:', message.data);
57
+ break;
58
+ case MessageCode.MATCH_SUCCESS:
59
+ console.log('匹配成功:', message.data);
60
+ break;
61
+ // ... 其他消息类型
62
+ }
63
+ });
64
+
65
+ // 监听错误事件
66
+ sdk.on(EventType.ERROR, (error) => {
67
+ console.error('发生错误:', error);
68
+ });
69
+
70
+ // 监听状态变化
71
+ sdk.on(EventType.STATUS_CHANGE, (status) => {
72
+ console.log('连接状态变化:', status);
73
+ });
74
+
75
+ // 连接到服务器(需要传入客户组ID)
76
+ await sdk.connect('customer-group-id');
77
+
78
+ // 发送文本消息
79
+ await sdk.sendText('你好,我需要帮助');
80
+
81
+ // 发送图片消息
82
+ const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
83
+ const file = fileInput.files?.[0];
84
+ if (file) {
85
+ await sdk.sendFile({
86
+ file,
87
+ chatType: ChatType.IMAGE,
88
+ });
89
+ }
90
+
91
+ // 发送视频消息(自动生成缩略图)
92
+ const videoFile = fileInput.files?.[0];
93
+ if (videoFile) {
94
+ const thumbnailFile = await sdk.getVideoThumbnail(videoFile);
95
+ await sdk.sendFile({
96
+ file: videoFile,
97
+ thumbnailFile,
98
+ chatType: ChatType.VIDEO,
99
+ });
100
+ }
101
+
102
+ // 获取用户信息
103
+ const userInfo = await sdk.getUserInfo();
104
+ console.log('用户信息:', userInfo);
105
+
106
+ // 获取咨询列表
107
+ const consultationList = await sdk.getConsultationList(entranceId);
108
+ console.log('咨询列表:', consultationList);
109
+ ```
110
+
111
+ ### 在 Vue 项目中使用
112
+
113
+ ```vue
114
+ <template>
115
+ <div>
116
+ <input type="file" @change="handleFileChange" />
117
+ <button @click="sendText">发送文本</button>
118
+ </div>
119
+ </template>
120
+
121
+ <script setup lang="ts">
122
+ import { onMounted, onUnmounted } from 'vue';
123
+ import JoyTalk, { EventType, ChatType } from 'joytalk';
124
+
125
+ const sdk = new JoyTalk({
126
+ token: 'your-token',
127
+ });
128
+
129
+ onMounted(() => {
130
+ sdk.on(EventType.MESSAGE, (message) => {
131
+ console.log('收到消息:', message);
132
+ });
133
+
134
+ sdk.connect('customer-group-id');
135
+ });
136
+
137
+ onUnmounted(() => {
138
+ sdk.disconnect();
139
+ });
140
+
141
+ const sendText = async () => {
142
+ await sdk.sendText('Hello');
143
+ };
144
+
145
+ const handleFileChange = async (e: Event) => {
146
+ const file = (e.target as HTMLInputElement).files?.[0];
147
+ if (file) {
148
+ await sdk.sendFile({
149
+ file,
150
+ chatType: ChatType.IMAGE,
151
+ });
152
+ }
153
+ };
154
+ </script>
155
+ ```
156
+
157
+ ### CDN 引入方式
158
+
159
+ 通过 CDN 方式引入 SDK,适合在 HTML 页面中直接使用。
160
+
161
+ #### 简化示例
162
+
163
+ ```html
164
+ <!DOCTYPE html>
165
+ <html lang="zh-CN">
166
+ <head>
167
+ <meta charset="UTF-8">
168
+ <title>客服 SDK 简化示例</title>
169
+ <script src="https://your-cdn.com/joytalk/dist/index.umd.js"></script>
170
+ </head>
171
+ <body>
172
+ <script>
173
+ const JoyTalk = window.JoyTalk.default;
174
+ const { EventType, ChatType } = window.JoyTalk;
175
+
176
+ const sdk = new JoyTalk({ token: 'your-token' });
177
+
178
+ sdk.on(EventType.CONNECTED, () => {
179
+ console.log('连接成功');
180
+ });
181
+
182
+ sdk.on(EventType.MESSAGE, (message) => {
183
+ console.log('收到消息:', message);
184
+ });
185
+
186
+ // 连接并发送消息
187
+ sdk.connect('customer-group-id').then(() => {
188
+ sdk.sendText('你好');
189
+ });
190
+ </script>
191
+ </body>
192
+ </html>
193
+ ```
194
+
195
+ #### 完整示例
196
+
197
+ ```html
198
+ <!DOCTYPE html>
199
+ <html lang="zh-CN">
200
+ <head>
201
+ <meta charset="UTF-8">
202
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
203
+ <title>客服 SDK 示例</title>
204
+ <!-- 引入 SDK -->
205
+ <script src="https://your-cdn.com/joytalk/dist/index.umd.js"></script>
206
+ </head>
207
+ <body>
208
+ <div id="app">
209
+ <button id="connectBtn">连接</button>
210
+ <button id="sendBtn">发送消息</button>
211
+ <input type="file" id="fileInput" accept="image/*,video/*" />
212
+ </div>
213
+
214
+ <script>
215
+ // 从全局变量中获取 SDK 类和枚举
216
+ // 注意:JoyTalk 是默认导出,其他枚举和类型是命名导出
217
+ const JoyTalk = window.JoyTalk.default;
218
+ const { EventType, ChatType, MessageCode, ConnectionStatus, MessageType } = window.JoyTalk;
219
+
220
+ // 创建 SDK 实例
221
+ const sdk = new JoyTalk({
222
+ token: 'your-token',
223
+ autoReconnect: true,
224
+ reconnectInterval: 3000,
225
+ maxReconnectAttempts: 5,
226
+ heartbeatTimeout: 10000,
227
+ });
228
+
229
+ // 监听连接事件
230
+ sdk.on(EventType.CONNECTED, () => {
231
+ console.log('已连接到服务器');
232
+ });
233
+
234
+ // 监听消息事件
235
+ sdk.on(EventType.MESSAGE, (message) => {
236
+ console.log('收到消息:', message);
237
+
238
+ // 根据消息类型处理
239
+ switch (message.code) {
240
+ case MessageCode.SEND_SUCCESS:
241
+ console.log('消息发送成功:', message.data);
242
+ break;
243
+ case MessageCode.MATCH_SUCCESS:
244
+ console.log('匹配成功:', message.data);
245
+ break;
246
+ // ... 其他消息类型
247
+ }
248
+ });
249
+
250
+ // 监听错误事件
251
+ sdk.on(EventType.ERROR, (error) => {
252
+ console.error('发生错误:', error);
253
+ });
254
+
255
+ // 监听状态变化
256
+ sdk.on(EventType.STATUS_CHANGE, (status) => {
257
+ console.log('连接状态变化:', status);
258
+ });
259
+
260
+ // 连接按钮
261
+ document.getElementById('connectBtn').addEventListener('click', async () => {
262
+ try {
263
+ await sdk.connect('customer-group-id');
264
+ console.log('连接成功');
265
+ } catch (error) {
266
+ console.error('连接失败:', error);
267
+ }
268
+ });
269
+
270
+ // 发送消息按钮
271
+ document.getElementById('sendBtn').addEventListener('click', async () => {
272
+ try {
273
+ await sdk.sendText('你好,我需要帮助');
274
+ } catch (error) {
275
+ console.error('发送失败:', error);
276
+ }
277
+ });
278
+
279
+ // 文件上传
280
+ document.getElementById('fileInput').addEventListener('change', async (e) => {
281
+ const file = e.target.files[0];
282
+ if (!file) return;
283
+
284
+ try {
285
+ if (file.type.startsWith('image/')) {
286
+ // 发送图片
287
+ await sdk.sendFile({
288
+ file: file,
289
+ chatType: ChatType.IMAGE,
290
+ });
291
+ } else if (file.type.startsWith('video/')) {
292
+ // 发送视频(自动生成缩略图)
293
+ const thumbnailFile = await sdk.getVideoThumbnail(file);
294
+ await sdk.sendFile({
295
+ file: file,
296
+ thumbnailFile: thumbnailFile,
297
+ chatType: ChatType.VIDEO,
298
+ });
299
+ } else {
300
+ // 发送文件
301
+ await sdk.sendFile({
302
+ file: file,
303
+ chatType: ChatType.FILE,
304
+ });
305
+ }
306
+ console.log('文件发送成功');
307
+ } catch (error) {
308
+ console.error('文件发送失败:', error);
309
+ }
310
+ });
311
+
312
+ // 获取用户信息示例
313
+ async function loadUserInfo() {
314
+ try {
315
+ const userInfo = await sdk.getUserInfo();
316
+ console.log('用户信息:', userInfo);
317
+ // 可以在页面上显示用户信息
318
+ // document.getElementById('userName').textContent = userInfo.nickname;
319
+ } catch (error) {
320
+ console.error('获取用户信息失败:', error);
321
+ }
322
+ }
323
+
324
+ // 获取咨询列表示例
325
+ async function loadConsultationList(entranceId) {
326
+ try {
327
+ const result = await sdk.getConsultationList(entranceId);
328
+ console.log('咨询列表:', result.data.list);
329
+ // 可以在页面上显示咨询列表
330
+ } catch (error) {
331
+ console.error('获取咨询列表失败:', error);
332
+ }
333
+ }
334
+
335
+ // 页面加载完成后可以调用
336
+ // loadUserInfo();
337
+ // loadConsultationList(123);
338
+ </script>
339
+ </body>
340
+ </html>
341
+ ```
342
+
343
+ **CDN 地址说明:**
344
+ - 将 `https://your-cdn.com/joytalk/dist/index.umd.js` 替换为实际的 CDN 地址
345
+ - 如果使用本地构建文件,可以使用相对路径:`<script src="./dist/index.umd.js"></script>`
346
+ - 建议使用版本号锁定,如:`https://cdn.example.com/joytalk@0.0.1/dist/index.umd.js`
347
+
348
+ **注意事项:**
349
+ - CDN 引入后,SDK 会挂载到 `window.JoyTalk` 全局对象上
350
+ - 默认导出(`default`)是 `JoyTalk` 类,通过 `window.JoyTalk.default` 访问
351
+ - 所有枚举和类型都可以从 `window.JoyTalk` 中获取,如 `EventType`、`ChatType`、`MessageCode`、`ConnectionStatus`、`MessageType` 等
352
+ - 确保 CDN 地址可访问,并且版本正确
353
+ - 如果页面中已经使用了模块化构建工具(如 Webpack、Vite),建议使用 NPM 安装方式而不是 CDN
354
+
355
+ ### 配置选项
356
+
357
+ ```typescript
358
+ interface SDKConfig {
359
+ /** 认证令牌(必需) */
360
+ token: string;
361
+ /** 自动重连(默认: true) */
362
+ autoReconnect?: boolean;
363
+ /** 重连间隔(毫秒,默认: 3000) */
364
+ reconnectInterval?: number;
365
+ /** 最大重连次数(默认: 5) */
366
+ maxReconnectAttempts?: number;
367
+ /** 心跳超时时间(毫秒,默认: 10000) */
368
+ heartbeatTimeout?: number;
369
+ }
370
+ ```
371
+
372
+ ### 事件类型
373
+
374
+ - `EventType.CONNECTED` - 连接成功
375
+ - `EventType.DISCONNECTED` - 连接断开
376
+ - `EventType.ERROR` - 发生错误
377
+ - `EventType.MESSAGE` - 收到消息
378
+ - `EventType.STATUS_CHANGE` - 连接状态变化
379
+ - `EventType.HEARTBEAT_TIMEOUT` - 心跳超时
380
+
381
+ ### 消息类型
382
+
383
+ - `ChatType.TEXT` - 文本消息
384
+ - `ChatType.IMAGE` - 图片消息
385
+ - `ChatType.VIDEO` - 视频消息
386
+ - `ChatType.FILE` - 文件消息
387
+ - `ChatType.FAQ` - FAQ 消息
388
+
389
+ ### 连接状态
390
+
391
+ - `ConnectionStatus.DISCONNECTED` - 未连接
392
+ - `ConnectionStatus.CONNECTING` - 连接中
393
+ - `ConnectionStatus.CONNECTED` - 已连接
394
+ - `ConnectionStatus.RECONNECTING` - 重连中
395
+ - `ConnectionStatus.ERROR` - 错误
396
+
397
+ ## API 方法
398
+
399
+ ### 连接管理
400
+
401
+ #### `connect(customerGroupId: string): Promise<void>`
402
+ 连接到服务器
403
+
404
+ **参数:**
405
+ - `customerGroupId` - 客户组ID
406
+
407
+ **示例:**
408
+ ```typescript
409
+ await sdk.connect('customer-group-id');
410
+ ```
411
+
412
+ #### `disconnect(): void`
413
+ 断开连接
414
+
415
+ **示例:**
416
+ ```typescript
417
+ sdk.disconnect();
418
+ ```
419
+
420
+ #### `getStatus(): ConnectionStatus`
421
+ 获取当前连接状态
422
+
423
+ **返回值:** `ConnectionStatus` 枚举值
424
+
425
+ #### `isConnected(): boolean`
426
+ 检查是否已连接
427
+
428
+ **返回值:** `boolean`
429
+
430
+ ### 消息发送
431
+
432
+ #### `sendText(content: string): Promise<boolean>`
433
+ 发送文本消息
434
+
435
+ **参数:**
436
+ - `content` - 消息内容
437
+
438
+ **示例:**
439
+ ```typescript
440
+ await sdk.sendText('你好');
441
+ ```
442
+
443
+ #### `sendFile(params): Promise<string>`
444
+ 发送文件消息(图片、视频、文件)
445
+
446
+ **参数:**
447
+ ```typescript
448
+ {
449
+ file: File; // 文件对象
450
+ thumbnailFile?: File; // 缩略图文件(可选,用于视频)
451
+ chatType: ChatType.IMAGE | ChatType.VIDEO | ChatType.FILE;
452
+ }
453
+ ```
454
+
455
+ **返回值:** `Promise<string>` - 返回文件信息的 JSON 字符串
456
+
457
+ **示例:**
458
+ ```typescript
459
+ // 发送图片
460
+ await sdk.sendFile({
461
+ file: imageFile,
462
+ chatType: ChatType.IMAGE,
463
+ });
464
+
465
+ // 发送视频(带缩略图)
466
+ const thumbnailFile = await sdk.getVideoThumbnail(videoFile);
467
+ await sdk.sendFile({
468
+ file: videoFile,
469
+ thumbnailFile,
470
+ chatType: ChatType.VIDEO,
471
+ });
472
+
473
+ // 发送文件
474
+ await sdk.sendFile({
475
+ file: documentFile,
476
+ chatType: ChatType.FILE,
477
+ });
478
+ ```
479
+
480
+ #### `sendFaq(faqItem: FaqItem): void`
481
+ 发送 FAQ 消息
482
+
483
+ **参数:**
484
+ - `faqItem` - FAQ 项对象
485
+
486
+ **示例:**
487
+ ```typescript
488
+ sdk.sendFaq({
489
+ faqId: 1,
490
+ question: '常见问题',
491
+ item: [...],
492
+ isMulti: false,
493
+ });
494
+ ```
495
+
496
+ ### 文件处理
497
+
498
+ #### `getVideoThumbnail(videoFile: File): Promise<File>`
499
+ 获取视频文件的首帧作为缩略图
500
+
501
+ **参数:**
502
+ - `videoFile` - 视频文件对象
503
+
504
+ **返回值:** `Promise<File>` - 返回首帧图片的 File 对象
505
+
506
+ **示例:**
507
+ ```typescript
508
+ const thumbnailFile = await sdk.getVideoThumbnail(videoFile);
509
+ ```
510
+
511
+ ### 用户信息
512
+
513
+ #### `getUserInfo(): Promise<UserInfo>`
514
+ 获取用户信息
515
+
516
+ **返回值:** `Promise<UserInfo>` - 用户信息对象
517
+
518
+ **示例:**
519
+ ```typescript
520
+ const userInfo = await sdk.getUserInfo();
521
+ console.log(userInfo.nickname, userInfo.avatar);
522
+ ```
523
+
524
+ ### API 调用
525
+
526
+ #### `getConsultationList(entranceId: number)`
527
+ 获取咨询列表
528
+
529
+ **参数:**
530
+ - `entranceId` - 入口ID
531
+
532
+ **返回值:** `Promise<ConsultationListResponse>`
533
+
534
+ **示例:**
535
+ ```typescript
536
+ const result = await sdk.getConsultationList(123);
537
+ console.log(result.data.list);
538
+ ```
539
+
540
+ ### 事件监听
541
+
542
+ #### `on(event: EventType, callback: EventCallback): void`
543
+ 监听事件
544
+
545
+ **示例:**
546
+ ```typescript
547
+ sdk.on(EventType.MESSAGE, (message) => {
548
+ console.log('收到消息:', message);
549
+ });
550
+ ```
551
+
552
+ #### `off(event: EventType, callback: EventCallback): void`
553
+ 取消监听事件
554
+
555
+ **示例:**
556
+ ```typescript
557
+ const handler = (message) => console.log(message);
558
+ sdk.on(EventType.MESSAGE, handler);
559
+ sdk.off(EventType.MESSAGE, handler);
560
+ ```
561
+
562
+ ### 公共属性
563
+
564
+ #### `OBS_URL: string`
565
+ OBS 存储地址(只读)
566
+
567
+ #### `userInfo: UserInfo | null`
568
+ 当前用户信息(只读)
569
+
570
+ ## 类型定义
571
+
572
+ SDK 提供了完整的 TypeScript 类型定义,包括:
573
+
574
+ - `MessageData` - 消息数据
575
+ - `TextMessageData` - 文本消息数据
576
+ - `FileMessageData` - 文件消息数据
577
+ - `FaqMessageData` - FAQ 消息数据
578
+ - `SessionData` - 会话数据
579
+ - `UserInfo` - 用户信息
580
+ - `ChatType` - 聊天类型枚举
581
+ - `MessageType` - 消息类型枚举
582
+ - `EventType` - 事件类型枚举
583
+ - `ConnectionStatus` - 连接状态枚举
584
+ - `MessageCode` - 消息代码枚举
585
+
586
+ ## 消息处理示例
587
+
588
+ ```typescript
589
+ import JoyTalk, { EventType, MessageCode, ChatType } from 'joytalk';
590
+
591
+ const sdk = new JoyTalk({ token: 'your-token' });
592
+
593
+ sdk.on(EventType.MESSAGE, (message) => {
594
+ switch (message.code) {
595
+ case MessageCode.SEND_SUCCESS:
596
+ // 消息发送成功
597
+ const messageData = message.data;
598
+ if (messageData.contentType === ChatType.TEXT) {
599
+ console.log('文本消息:', messageData.content);
600
+ } else if (messageData.contentType === ChatType.IMAGE) {
601
+ console.log('图片消息:', messageData.content.fileUrl);
602
+ }
603
+ break;
604
+
605
+ case MessageCode.MATCH_SUCCESS:
606
+ // 匹配成功
607
+ const sessionData = message.data;
608
+ console.log('会话ID:', sessionData.sessionId);
609
+ break;
610
+
611
+ case MessageCode.CONNECT_SUCCESS:
612
+ console.log('连接成功');
613
+ break;
614
+
615
+ // ... 其他消息类型
616
+ }
617
+ });
618
+ ```
619
+
620
+ ## 开发
621
+
622
+ ```bash
623
+ # 安装依赖
624
+ npm install
625
+ # 或
626
+ pnpm install
627
+
628
+ # 构建
629
+ npm run build
630
+
631
+ # 开发模式(监听文件变化)
632
+ npm run dev
633
+
634
+ # 类型检查
635
+ npm run type-check
636
+ ```
637
+
638
+ ## 许可证
639
+
640
+ ISC
@@ -0,0 +1,24 @@
1
+ import { ObsAuthInfo, ConsultationListResponse, RequestResponse, UserAuthParams, AnonymousAuthParams, FaqListResponse, UserInfo } from '../types';
2
+ export declare const getObsAuthInfo: (params: {
3
+ ext: string;
4
+ }) => Promise<RequestResponse<ObsAuthInfo>>;
5
+ export declare const uploadFile: (formData: FormData) => Promise<any>;
6
+ /**
7
+ * 获取咨询列表
8
+ * @returns Promise<ConsultationListResponse>
9
+ */
10
+ export declare const fetchConsultationList: (params: {
11
+ entranceId: number;
12
+ }) => Promise<RequestResponse<ConsultationListResponse>>;
13
+ /**
14
+ * 登录用户认证
15
+ */
16
+ export declare const userAuth: (params: UserAuthParams) => Promise<RequestResponse<any>>;
17
+ /**
18
+ * 匿名用户认证
19
+ */
20
+ export declare const anonymousAuth: (params: AnonymousAuthParams) => Promise<RequestResponse<any>>;
21
+ export declare const fetchFaqList: (params: {
22
+ id: string;
23
+ }) => Promise<RequestResponse<FaqListResponse>>;
24
+ export declare const fetchUserInfo: () => Promise<RequestResponse<UserInfo>>;
@@ -0,0 +1,2 @@
1
+ declare const request: import('axios').AxiosInstance;
2
+ export default request;