go-chat-sdk 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/dist/index.d.mts +500 -0
- package/dist/index.mjs +696 -0
- package/package.json +36 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
//#region types.d.ts
|
|
2
|
+
declare enum ChatType {
|
|
3
|
+
Single = "single",
|
|
4
|
+
Group = "group"
|
|
5
|
+
}
|
|
6
|
+
declare enum MessageType {
|
|
7
|
+
Text = "text",
|
|
8
|
+
Image = "image",
|
|
9
|
+
Video = "video",
|
|
10
|
+
File = "file"
|
|
11
|
+
}
|
|
12
|
+
interface Message {
|
|
13
|
+
/** 服务器生成的消息ID */
|
|
14
|
+
msg_id: string;
|
|
15
|
+
/** 客户端生成的消息ID(用于去重) */
|
|
16
|
+
client_msg_id: string;
|
|
17
|
+
/** 发送者ID */
|
|
18
|
+
sender_id: string;
|
|
19
|
+
/** 接收者ID(用户ID或群ID) */
|
|
20
|
+
receiver_id: string;
|
|
21
|
+
/** 聊天类型: single(单聊) / group(群聊) */
|
|
22
|
+
chat_type: ChatType;
|
|
23
|
+
/** 服务器时间戳(微秒级) */
|
|
24
|
+
server_time: number;
|
|
25
|
+
/** 回复的消息ID(可选) */
|
|
26
|
+
reply_to_msg_id?: string;
|
|
27
|
+
/** 消息类型: text/image/video/file */
|
|
28
|
+
msg_type: MessageType;
|
|
29
|
+
/** 消息内容负载 */
|
|
30
|
+
payload: unknown;
|
|
31
|
+
/** 扩展字段 */
|
|
32
|
+
ext?: Record<string, unknown>;
|
|
33
|
+
/** 创建时间 */
|
|
34
|
+
created_at?: string;
|
|
35
|
+
}
|
|
36
|
+
interface SendMessageRequest {
|
|
37
|
+
/** 客户端生成的唯一消息ID */
|
|
38
|
+
client_msg_id?: string;
|
|
39
|
+
/** 接收者ID */
|
|
40
|
+
receiver_id: string;
|
|
41
|
+
/** 聊天类型 */
|
|
42
|
+
chat_type: ChatType;
|
|
43
|
+
/** 消息类型 */
|
|
44
|
+
msg_type: MessageType;
|
|
45
|
+
/** 消息内容负载 */
|
|
46
|
+
payload: unknown;
|
|
47
|
+
/** 回复的消息ID(可选) */
|
|
48
|
+
reply_to_msg_id?: string;
|
|
49
|
+
/** 扩展字段(可选) */
|
|
50
|
+
ext?: Record<string, unknown>;
|
|
51
|
+
}
|
|
52
|
+
interface TextPayload {
|
|
53
|
+
text: string;
|
|
54
|
+
}
|
|
55
|
+
interface ImagePayload {
|
|
56
|
+
url: string;
|
|
57
|
+
width?: number;
|
|
58
|
+
height?: number;
|
|
59
|
+
size?: number;
|
|
60
|
+
}
|
|
61
|
+
interface VideoPayload {
|
|
62
|
+
url: string;
|
|
63
|
+
duration?: number;
|
|
64
|
+
width?: number;
|
|
65
|
+
height?: number;
|
|
66
|
+
size?: number;
|
|
67
|
+
thumbnail_url?: string;
|
|
68
|
+
}
|
|
69
|
+
interface FilePayload {
|
|
70
|
+
url: string;
|
|
71
|
+
name: string;
|
|
72
|
+
size: number;
|
|
73
|
+
mime_type?: string;
|
|
74
|
+
}
|
|
75
|
+
interface RegisterRequest {
|
|
76
|
+
username: string;
|
|
77
|
+
password: string;
|
|
78
|
+
}
|
|
79
|
+
interface LoginRequest {
|
|
80
|
+
username: string;
|
|
81
|
+
password: string;
|
|
82
|
+
}
|
|
83
|
+
interface UserResponse {
|
|
84
|
+
user_id: string;
|
|
85
|
+
username: string;
|
|
86
|
+
token: string;
|
|
87
|
+
}
|
|
88
|
+
interface BatchGenerateRequest {
|
|
89
|
+
count: number;
|
|
90
|
+
}
|
|
91
|
+
interface BatchUserResponse {
|
|
92
|
+
user_id: string;
|
|
93
|
+
username: string;
|
|
94
|
+
password: string;
|
|
95
|
+
token: string;
|
|
96
|
+
}
|
|
97
|
+
interface ChatSDKOptions {
|
|
98
|
+
/** API基础URL */
|
|
99
|
+
baseURL: string;
|
|
100
|
+
/** WebSocket网关URL */
|
|
101
|
+
gatewayURL: string;
|
|
102
|
+
/** 自动重连间隔(毫秒),默认3000ms */
|
|
103
|
+
reconnectInterval?: number;
|
|
104
|
+
/** 最大重连次数,默认10次 */
|
|
105
|
+
maxReconnectAttempts?: number;
|
|
106
|
+
/** 心跳间隔(毫秒),默认30000ms */
|
|
107
|
+
heartbeatInterval?: number;
|
|
108
|
+
/** 消息缓冲区大小,默认100 */
|
|
109
|
+
messageBufferSize?: number;
|
|
110
|
+
}
|
|
111
|
+
declare enum ConnectionState {
|
|
112
|
+
Disconnected = "disconnected",
|
|
113
|
+
Connecting = "connecting",
|
|
114
|
+
Connected = "connected",
|
|
115
|
+
Reconnecting = "reconnecting",
|
|
116
|
+
Error = "error"
|
|
117
|
+
}
|
|
118
|
+
declare enum ChatEventType {
|
|
119
|
+
MessageReceived = "message:received",
|
|
120
|
+
MessageSent = "message:sent",
|
|
121
|
+
ConnectionStateChange = "connection:state:change",
|
|
122
|
+
Error = "error",
|
|
123
|
+
Connect = "connect",
|
|
124
|
+
Disconnect = "disconnect"
|
|
125
|
+
}
|
|
126
|
+
interface MessageReceivedData {
|
|
127
|
+
message: Message;
|
|
128
|
+
}
|
|
129
|
+
interface MessageSentData {
|
|
130
|
+
client_msg_id: string;
|
|
131
|
+
server_msg_id?: string;
|
|
132
|
+
server_time?: number;
|
|
133
|
+
}
|
|
134
|
+
interface ConnectionStateChangeData {
|
|
135
|
+
state: ConnectionState;
|
|
136
|
+
previousState: ConnectionState;
|
|
137
|
+
}
|
|
138
|
+
interface ErrorData {
|
|
139
|
+
code: string;
|
|
140
|
+
message: string;
|
|
141
|
+
originalError?: Error;
|
|
142
|
+
}
|
|
143
|
+
interface ConnectData {
|
|
144
|
+
timestamp: number;
|
|
145
|
+
}
|
|
146
|
+
interface DisconnectData {
|
|
147
|
+
code?: number;
|
|
148
|
+
reason?: string;
|
|
149
|
+
}
|
|
150
|
+
interface ChatEventDataMap {
|
|
151
|
+
[ChatEventType.MessageReceived]: MessageReceivedData;
|
|
152
|
+
[ChatEventType.MessageSent]: MessageSentData;
|
|
153
|
+
[ChatEventType.ConnectionStateChange]: ConnectionStateChangeData;
|
|
154
|
+
[ChatEventType.Error]: ErrorData;
|
|
155
|
+
[ChatEventType.Connect]: ConnectData;
|
|
156
|
+
[ChatEventType.Disconnect]: DisconnectData;
|
|
157
|
+
}
|
|
158
|
+
type ChatEvent<T extends ChatEventType = ChatEventType> = {
|
|
159
|
+
type: T;
|
|
160
|
+
data: T extends keyof ChatEventDataMap ? ChatEventDataMap[T] : unknown;
|
|
161
|
+
timestamp: number;
|
|
162
|
+
};
|
|
163
|
+
type EventListener<T extends ChatEventType = ChatEventType> = (event: ChatEvent<T>) => void;
|
|
164
|
+
interface HistoryQueryParams {
|
|
165
|
+
/** 接收者ID */
|
|
166
|
+
receiver_id: string;
|
|
167
|
+
/** 聊天类型 */
|
|
168
|
+
chat_type: ChatType;
|
|
169
|
+
/** 查询此时间戳之前的消息(微秒级,默认为当前时间) */
|
|
170
|
+
before_server_time?: number;
|
|
171
|
+
/** 每页数量(默认20) */
|
|
172
|
+
page_size?: number;
|
|
173
|
+
}
|
|
174
|
+
interface HistoryMessagesResponse {
|
|
175
|
+
messages: Message[];
|
|
176
|
+
hasMore: boolean;
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region utils.d.ts
|
|
180
|
+
/**
|
|
181
|
+
* 事件发射器 - 用于SDK内部事件管理
|
|
182
|
+
*/
|
|
183
|
+
declare class EventEmitter {
|
|
184
|
+
private listeners;
|
|
185
|
+
/**
|
|
186
|
+
* 监听事件
|
|
187
|
+
*/
|
|
188
|
+
on<T extends ChatEventType>(event: T, listener: EventListener<T>): () => void;
|
|
189
|
+
/**
|
|
190
|
+
* 监听一次性事件
|
|
191
|
+
*/
|
|
192
|
+
once<T extends ChatEventType>(event: T, listener: EventListener<T>): void;
|
|
193
|
+
/**
|
|
194
|
+
* 取消监听
|
|
195
|
+
*/
|
|
196
|
+
off<T extends ChatEventType>(event: T, listener: EventListener<T>): void;
|
|
197
|
+
/**
|
|
198
|
+
* 触发事件
|
|
199
|
+
*/
|
|
200
|
+
emit<T extends ChatEventType>(event: T, data: ChatEvent<T>['data']): void;
|
|
201
|
+
/**
|
|
202
|
+
* 移除所有监听器
|
|
203
|
+
*/
|
|
204
|
+
removeAllListeners(event?: ChatEventType): void;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* 生成UUID v4
|
|
208
|
+
*/
|
|
209
|
+
declare function generateUUID(): string;
|
|
210
|
+
/**
|
|
211
|
+
* 检查字符串是否为有效的UUID
|
|
212
|
+
*/
|
|
213
|
+
declare function isValidUUID(str: string): boolean;
|
|
214
|
+
/**
|
|
215
|
+
* 延迟函数
|
|
216
|
+
*/
|
|
217
|
+
declare function delay(ms: number): Promise<void>;
|
|
218
|
+
/**
|
|
219
|
+
* 创建错误数据对象
|
|
220
|
+
*/
|
|
221
|
+
declare function createError(code: string, message: string, originalError?: Error): ErrorData;
|
|
222
|
+
/**
|
|
223
|
+
* 创建连接状态变更数据
|
|
224
|
+
*/
|
|
225
|
+
declare function createStateChange(state: ConnectionState, previousState: ConnectionState): ConnectionStateChangeData;
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region api.d.ts
|
|
228
|
+
/**
|
|
229
|
+
* API 客户端 - 处理所有 HTTP 请求
|
|
230
|
+
*/
|
|
231
|
+
declare class APIClient {
|
|
232
|
+
private baseURL;
|
|
233
|
+
private token;
|
|
234
|
+
constructor(baseURL: string);
|
|
235
|
+
/**
|
|
236
|
+
* 设置认证 Token
|
|
237
|
+
*/
|
|
238
|
+
setToken(token: string): void;
|
|
239
|
+
/**
|
|
240
|
+
* 清除认证 Token
|
|
241
|
+
*/
|
|
242
|
+
clearToken(): void;
|
|
243
|
+
/**
|
|
244
|
+
* 获取当前 Token
|
|
245
|
+
*/
|
|
246
|
+
getToken(): string | null;
|
|
247
|
+
/**
|
|
248
|
+
* 构建请求头
|
|
249
|
+
*/
|
|
250
|
+
private getHeaders;
|
|
251
|
+
/**
|
|
252
|
+
* 发送 HTTP 请求
|
|
253
|
+
*/
|
|
254
|
+
private request;
|
|
255
|
+
/**
|
|
256
|
+
* 用户注册
|
|
257
|
+
*/
|
|
258
|
+
register(req: RegisterRequest): Promise<UserResponse>;
|
|
259
|
+
/**
|
|
260
|
+
* 用户登录
|
|
261
|
+
*/
|
|
262
|
+
login(req: LoginRequest): Promise<UserResponse>;
|
|
263
|
+
/**
|
|
264
|
+
* 批量生成用户
|
|
265
|
+
*/
|
|
266
|
+
batchGenerate(req: BatchGenerateRequest): Promise<{
|
|
267
|
+
users: BatchUserResponse[];
|
|
268
|
+
}>;
|
|
269
|
+
/**
|
|
270
|
+
* 获取历史消息
|
|
271
|
+
* 注意:后端目前只提供了按 conversation 查询的接口
|
|
272
|
+
*/
|
|
273
|
+
getHistoryMessages(params: HistoryQueryParams): Promise<HistoryMessagesResponse>;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* API 错误类
|
|
277
|
+
*/
|
|
278
|
+
declare class APIError extends Error {
|
|
279
|
+
statusCode: number;
|
|
280
|
+
data: Record<string, unknown>;
|
|
281
|
+
constructor(statusCode: number, message: string, data?: Record<string, unknown>);
|
|
282
|
+
}
|
|
283
|
+
//#endregion
|
|
284
|
+
//#region websocket.d.ts
|
|
285
|
+
/**
|
|
286
|
+
* WebSocket 连接管理器
|
|
287
|
+
*/
|
|
288
|
+
declare class WebSocketManager {
|
|
289
|
+
private ws;
|
|
290
|
+
private options;
|
|
291
|
+
private emitter;
|
|
292
|
+
private currentState;
|
|
293
|
+
private reconnectAttempts;
|
|
294
|
+
private reconnectTimer;
|
|
295
|
+
private heartbeatTimer;
|
|
296
|
+
private messageQueue;
|
|
297
|
+
private token;
|
|
298
|
+
private intentionalClose;
|
|
299
|
+
constructor(options: ChatSDKOptions, emitter: EventEmitter);
|
|
300
|
+
/**
|
|
301
|
+
* 设置认证 Token
|
|
302
|
+
*/
|
|
303
|
+
setToken(token: string): void;
|
|
304
|
+
/**
|
|
305
|
+
* 清除认证 Token
|
|
306
|
+
*/
|
|
307
|
+
clearToken(): void;
|
|
308
|
+
/**
|
|
309
|
+
* 获取当前连接状态
|
|
310
|
+
*/
|
|
311
|
+
getState(): ConnectionState;
|
|
312
|
+
/**
|
|
313
|
+
* 是否已连接
|
|
314
|
+
*/
|
|
315
|
+
isConnected(): boolean;
|
|
316
|
+
/**
|
|
317
|
+
* 更新连接状态并触发事件
|
|
318
|
+
*/
|
|
319
|
+
private setState;
|
|
320
|
+
/**
|
|
321
|
+
* 连接到 WebSocket 网关
|
|
322
|
+
*/
|
|
323
|
+
connect(): Promise<void>;
|
|
324
|
+
/**
|
|
325
|
+
* 断开 WebSocket 连接
|
|
326
|
+
*/
|
|
327
|
+
disconnect(): void;
|
|
328
|
+
/**
|
|
329
|
+
* 发送消息
|
|
330
|
+
*/
|
|
331
|
+
sendMessage(req: SendMessageRequest): void;
|
|
332
|
+
/**
|
|
333
|
+
* 构建发送的消息对象
|
|
334
|
+
*/
|
|
335
|
+
private buildMessage;
|
|
336
|
+
/**
|
|
337
|
+
* 设置 WebSocket 事件处理器
|
|
338
|
+
*/
|
|
339
|
+
private setupWebSocketHandlers;
|
|
340
|
+
/**
|
|
341
|
+
* 处理接收到的消息
|
|
342
|
+
*/
|
|
343
|
+
private handleMessage;
|
|
344
|
+
/**
|
|
345
|
+
* 启动心跳
|
|
346
|
+
*/
|
|
347
|
+
private startHeartbeat;
|
|
348
|
+
/**
|
|
349
|
+
* 安排重连
|
|
350
|
+
*/
|
|
351
|
+
private scheduleReconnect;
|
|
352
|
+
/**
|
|
353
|
+
* 清空定时器
|
|
354
|
+
*/
|
|
355
|
+
private clearTimers;
|
|
356
|
+
/**
|
|
357
|
+
* 刷新消息队列(连接成功后发送缓存的消息)
|
|
358
|
+
*/
|
|
359
|
+
private flushMessageQueue;
|
|
360
|
+
}
|
|
361
|
+
//#endregion
|
|
362
|
+
//#region index.d.ts
|
|
363
|
+
/**
|
|
364
|
+
* ChatSDK - 类型安全的聊天 SDK
|
|
365
|
+
*
|
|
366
|
+
* 使用示例:
|
|
367
|
+
* ```typescript
|
|
368
|
+
* const sdk = new ChatSDK({
|
|
369
|
+
* baseURL: 'http://localhost:8080',
|
|
370
|
+
* gatewayURL: 'ws://localhost:8081/ws',
|
|
371
|
+
* });
|
|
372
|
+
*
|
|
373
|
+
* // 注册/登录
|
|
374
|
+
* await sdk.register({ username: 'test', password: '123456' });
|
|
375
|
+
* await sdk.login({ username: 'test', password: '123456' });
|
|
376
|
+
*
|
|
377
|
+
* // 连接 WebSocket
|
|
378
|
+
* await sdk.connect();
|
|
379
|
+
*
|
|
380
|
+
* // 监听消息
|
|
381
|
+
* sdk.on(ChatEventType.MessageReceived, (event) => {
|
|
382
|
+
* console.log('收到消息:', event.data.message);
|
|
383
|
+
* });
|
|
384
|
+
*
|
|
385
|
+
* // 发送文本消息
|
|
386
|
+
* sdk.sendTextMessage({
|
|
387
|
+
* receiver_id: 'xxx',
|
|
388
|
+
* chat_type: ChatType.Single,
|
|
389
|
+
* text: 'Hello!',
|
|
390
|
+
* });
|
|
391
|
+
* ```
|
|
392
|
+
*/
|
|
393
|
+
declare class ChatSDK {
|
|
394
|
+
private api;
|
|
395
|
+
private wsManager;
|
|
396
|
+
private emitter;
|
|
397
|
+
private options;
|
|
398
|
+
private currentUser;
|
|
399
|
+
constructor(options: ChatSDKOptions);
|
|
400
|
+
/**
|
|
401
|
+
* 监听事件 - 类型安全的事件监听
|
|
402
|
+
*/
|
|
403
|
+
on<T extends ChatEventType>(event: T, listener: EventListener<T>): () => void;
|
|
404
|
+
/**
|
|
405
|
+
* 监听一次性事件 - 类型安全的事件监听
|
|
406
|
+
*/
|
|
407
|
+
once<T extends ChatEventType>(event: T, listener: EventListener<T>): void;
|
|
408
|
+
/**
|
|
409
|
+
* 取消监听
|
|
410
|
+
*/
|
|
411
|
+
off<T extends ChatEventType>(event: T, listener: EventListener<T>): void;
|
|
412
|
+
/**
|
|
413
|
+
* 移除所有监听器
|
|
414
|
+
*/
|
|
415
|
+
removeAllListeners(event?: ChatEventType): void;
|
|
416
|
+
/**
|
|
417
|
+
* 用户注册
|
|
418
|
+
*/
|
|
419
|
+
register(req: RegisterRequest): Promise<UserResponse>;
|
|
420
|
+
/**
|
|
421
|
+
* 用户登录
|
|
422
|
+
*/
|
|
423
|
+
login(req: LoginRequest): Promise<UserResponse>;
|
|
424
|
+
/**
|
|
425
|
+
* 批量生成用户(测试/管理用途)
|
|
426
|
+
*/
|
|
427
|
+
batchGenerateUsers(req: BatchGenerateRequest): Promise<BatchUserResponse[]>;
|
|
428
|
+
/**
|
|
429
|
+
* 设置认证信息
|
|
430
|
+
*/
|
|
431
|
+
setAuth(user: UserResponse): void;
|
|
432
|
+
/**
|
|
433
|
+
* 清除认证信息
|
|
434
|
+
*/
|
|
435
|
+
clearAuth(): void;
|
|
436
|
+
/**
|
|
437
|
+
* 获取当前用户信息
|
|
438
|
+
*/
|
|
439
|
+
getCurrentUser(): UserResponse | null;
|
|
440
|
+
/**
|
|
441
|
+
* 检查是否已认证
|
|
442
|
+
*/
|
|
443
|
+
isAuthenticated(): boolean;
|
|
444
|
+
/**
|
|
445
|
+
* 连接到消息网关
|
|
446
|
+
*/
|
|
447
|
+
connect(): Promise<void>;
|
|
448
|
+
/**
|
|
449
|
+
* 断开消息网关连接
|
|
450
|
+
*/
|
|
451
|
+
disconnect(): void;
|
|
452
|
+
/**
|
|
453
|
+
* 获取连接状态
|
|
454
|
+
*/
|
|
455
|
+
getConnectionState(): ConnectionState;
|
|
456
|
+
/**
|
|
457
|
+
* 是否已连接
|
|
458
|
+
*/
|
|
459
|
+
isConnected(): boolean;
|
|
460
|
+
/**
|
|
461
|
+
* 发送原始消息
|
|
462
|
+
*/
|
|
463
|
+
sendMessage(req: SendMessageRequest): void;
|
|
464
|
+
/**
|
|
465
|
+
* 发送文本消息
|
|
466
|
+
*/
|
|
467
|
+
sendTextMessage(params: Omit<SendMessageRequest, 'client_msg_id' | 'msg_type' | 'payload'> & {
|
|
468
|
+
text: string;
|
|
469
|
+
}): void;
|
|
470
|
+
/**
|
|
471
|
+
* 发送图片消息
|
|
472
|
+
*/
|
|
473
|
+
sendImageMessage(params: Omit<SendMessageRequest, 'client_msg_id' | 'msg_type' | 'payload'> & ImagePayload): void;
|
|
474
|
+
/**
|
|
475
|
+
* 发送视频消息
|
|
476
|
+
*/
|
|
477
|
+
sendVideoMessage(params: Omit<SendMessageRequest, 'client_msg_id' | 'msg_type' | 'payload'> & VideoPayload): void;
|
|
478
|
+
/**
|
|
479
|
+
* 发送文件消息
|
|
480
|
+
*/
|
|
481
|
+
sendFileMessage(params: Omit<SendMessageRequest, 'client_msg_id' | 'msg_type' | 'payload'> & FilePayload): void;
|
|
482
|
+
/**
|
|
483
|
+
* 获取历史消息
|
|
484
|
+
*/
|
|
485
|
+
getHistoryMessages(params: HistoryQueryParams): Promise<HistoryMessagesResponse>;
|
|
486
|
+
/**
|
|
487
|
+
* 生成 UUID(用于 client_msg_id)
|
|
488
|
+
*/
|
|
489
|
+
generateMessageId(): string;
|
|
490
|
+
/**
|
|
491
|
+
* 验证 UUID 格式
|
|
492
|
+
*/
|
|
493
|
+
validateMessageId(id: string): boolean;
|
|
494
|
+
/**
|
|
495
|
+
* 设置事件转发
|
|
496
|
+
*/
|
|
497
|
+
private setupEventForwarding;
|
|
498
|
+
}
|
|
499
|
+
//#endregion
|
|
500
|
+
export { APIClient, APIError, BatchGenerateRequest, BatchUserResponse, ChatEvent, ChatEventDataMap, ChatEventType, ChatSDK, ChatSDK as default, ChatSDKOptions, ChatType, ConnectData, ConnectionState, ConnectionStateChangeData, DisconnectData, ErrorData, EventEmitter, EventListener, FilePayload, HistoryMessagesResponse, HistoryQueryParams, ImagePayload, LoginRequest, Message, MessageReceivedData, MessageSentData, MessageType, RegisterRequest, SendMessageRequest, TextPayload, UserResponse, VideoPayload, WebSocketManager, createError, createStateChange, delay, generateUUID, isValidUUID };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
//#region types.ts
|
|
2
|
+
let ChatType = /* @__PURE__ */ function(ChatType) {
|
|
3
|
+
ChatType["Single"] = "single";
|
|
4
|
+
ChatType["Group"] = "group";
|
|
5
|
+
return ChatType;
|
|
6
|
+
}({});
|
|
7
|
+
let MessageType = /* @__PURE__ */ function(MessageType) {
|
|
8
|
+
MessageType["Text"] = "text";
|
|
9
|
+
MessageType["Image"] = "image";
|
|
10
|
+
MessageType["Video"] = "video";
|
|
11
|
+
MessageType["File"] = "file";
|
|
12
|
+
return MessageType;
|
|
13
|
+
}({});
|
|
14
|
+
let ConnectionState = /* @__PURE__ */ function(ConnectionState) {
|
|
15
|
+
ConnectionState["Disconnected"] = "disconnected";
|
|
16
|
+
ConnectionState["Connecting"] = "connecting";
|
|
17
|
+
ConnectionState["Connected"] = "connected";
|
|
18
|
+
ConnectionState["Reconnecting"] = "reconnecting";
|
|
19
|
+
ConnectionState["Error"] = "error";
|
|
20
|
+
return ConnectionState;
|
|
21
|
+
}({});
|
|
22
|
+
let ChatEventType = /* @__PURE__ */ function(ChatEventType) {
|
|
23
|
+
ChatEventType["MessageReceived"] = "message:received";
|
|
24
|
+
ChatEventType["MessageSent"] = "message:sent";
|
|
25
|
+
ChatEventType["ConnectionStateChange"] = "connection:state:change";
|
|
26
|
+
ChatEventType["Error"] = "error";
|
|
27
|
+
ChatEventType["Connect"] = "connect";
|
|
28
|
+
ChatEventType["Disconnect"] = "disconnect";
|
|
29
|
+
return ChatEventType;
|
|
30
|
+
}({});
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region api.ts
|
|
33
|
+
/**
|
|
34
|
+
* API 客户端 - 处理所有 HTTP 请求
|
|
35
|
+
*/
|
|
36
|
+
var APIClient = class {
|
|
37
|
+
constructor(baseURL) {
|
|
38
|
+
this.token = null;
|
|
39
|
+
this.baseURL = baseURL.replace(/\/$/, "");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 设置认证 Token
|
|
43
|
+
*/
|
|
44
|
+
setToken(token) {
|
|
45
|
+
this.token = token;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 清除认证 Token
|
|
49
|
+
*/
|
|
50
|
+
clearToken() {
|
|
51
|
+
this.token = null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 获取当前 Token
|
|
55
|
+
*/
|
|
56
|
+
getToken() {
|
|
57
|
+
return this.token;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 构建请求头
|
|
61
|
+
*/
|
|
62
|
+
getHeaders() {
|
|
63
|
+
const headers = { "Content-Type": "application/json" };
|
|
64
|
+
if (this.token) headers["Authorization"] = `Bearer ${this.token}`;
|
|
65
|
+
return headers;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 发送 HTTP 请求
|
|
69
|
+
*/
|
|
70
|
+
async request(method, endpoint, body) {
|
|
71
|
+
const url = `${this.baseURL}${endpoint}`;
|
|
72
|
+
const options = {
|
|
73
|
+
method,
|
|
74
|
+
headers: this.getHeaders()
|
|
75
|
+
};
|
|
76
|
+
if (body !== void 0) options.body = JSON.stringify(body);
|
|
77
|
+
const response = await fetch(url, options);
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
const errorData = await response.json().catch(() => ({}));
|
|
80
|
+
throw new APIError(response.status, errorData.error || `HTTP ${response.status}: ${response.statusText}`, errorData);
|
|
81
|
+
}
|
|
82
|
+
if (response.status === 204) return;
|
|
83
|
+
return response.json();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 用户注册
|
|
87
|
+
*/
|
|
88
|
+
async register(req) {
|
|
89
|
+
return this.request("POST", "/api/v1/users/register", req);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 用户登录
|
|
93
|
+
*/
|
|
94
|
+
async login(req) {
|
|
95
|
+
const resp = await this.request("POST", "/api/v1/users/login", req);
|
|
96
|
+
this.setToken(resp.token);
|
|
97
|
+
return resp;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 批量生成用户
|
|
101
|
+
*/
|
|
102
|
+
async batchGenerate(req) {
|
|
103
|
+
return this.request("POST", "/api/v1/users/batch", req);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 获取历史消息
|
|
107
|
+
* 注意:后端目前只提供了按 conversation 查询的接口
|
|
108
|
+
*/
|
|
109
|
+
async getHistoryMessages(params) {
|
|
110
|
+
const queryParams = new URLSearchParams();
|
|
111
|
+
queryParams.append("receiver_id", params.receiver_id);
|
|
112
|
+
queryParams.append("chat_type", params.chat_type);
|
|
113
|
+
if (params.before_server_time !== void 0) queryParams.append("before_server_time", params.before_server_time.toString());
|
|
114
|
+
if (params.page_size !== void 0) queryParams.append("page_size", params.page_size.toString());
|
|
115
|
+
const messages = await this.request("GET", `/api/v1/messages/history?${queryParams.toString()}`);
|
|
116
|
+
return {
|
|
117
|
+
messages: messages || [],
|
|
118
|
+
hasMore: messages.length === (params.page_size || 20)
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* API 错误类
|
|
124
|
+
*/
|
|
125
|
+
var APIError = class extends Error {
|
|
126
|
+
constructor(statusCode, message, data = {}) {
|
|
127
|
+
super(message);
|
|
128
|
+
this.name = "APIError";
|
|
129
|
+
this.statusCode = statusCode;
|
|
130
|
+
this.data = data;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region utils.ts
|
|
135
|
+
/**
|
|
136
|
+
* 事件发射器 - 用于SDK内部事件管理
|
|
137
|
+
*/
|
|
138
|
+
var EventEmitter = class {
|
|
139
|
+
constructor() {
|
|
140
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* 监听事件
|
|
144
|
+
*/
|
|
145
|
+
on(event, listener) {
|
|
146
|
+
if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
147
|
+
this.listeners.get(event).add(listener);
|
|
148
|
+
return () => {
|
|
149
|
+
this.off(event, listener);
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* 监听一次性事件
|
|
154
|
+
*/
|
|
155
|
+
once(event, listener) {
|
|
156
|
+
const onceWrapper = (e) => {
|
|
157
|
+
this.off(event, onceWrapper);
|
|
158
|
+
listener(e);
|
|
159
|
+
};
|
|
160
|
+
this.on(event, onceWrapper);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* 取消监听
|
|
164
|
+
*/
|
|
165
|
+
off(event, listener) {
|
|
166
|
+
const set = this.listeners.get(event);
|
|
167
|
+
if (set) {
|
|
168
|
+
set.delete(listener);
|
|
169
|
+
if (set.size === 0) this.listeners.delete(event);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* 触发事件
|
|
174
|
+
*/
|
|
175
|
+
emit(event, data) {
|
|
176
|
+
const set = this.listeners.get(event);
|
|
177
|
+
if (set) {
|
|
178
|
+
const eventObj = {
|
|
179
|
+
type: event,
|
|
180
|
+
data,
|
|
181
|
+
timestamp: Date.now()
|
|
182
|
+
};
|
|
183
|
+
set.forEach((listener) => {
|
|
184
|
+
try {
|
|
185
|
+
listener(eventObj);
|
|
186
|
+
} catch (err) {
|
|
187
|
+
console.error(`Event listener error for ${event}:`, err);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* 移除所有监听器
|
|
194
|
+
*/
|
|
195
|
+
removeAllListeners(event) {
|
|
196
|
+
if (event) this.listeners.delete(event);
|
|
197
|
+
else this.listeners.clear();
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* 生成UUID v4
|
|
202
|
+
*/
|
|
203
|
+
function generateUUID() {
|
|
204
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
205
|
+
const r = Math.random() * 16 | 0;
|
|
206
|
+
return (c === "x" ? r : r & 3 | 8).toString(16);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* 检查字符串是否为有效的UUID
|
|
211
|
+
*/
|
|
212
|
+
function isValidUUID(str) {
|
|
213
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(str);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* 延迟函数
|
|
217
|
+
*/
|
|
218
|
+
function delay(ms) {
|
|
219
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* 创建错误数据对象
|
|
223
|
+
*/
|
|
224
|
+
function createError(code, message, originalError) {
|
|
225
|
+
return {
|
|
226
|
+
code,
|
|
227
|
+
message,
|
|
228
|
+
originalError
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* 创建连接状态变更数据
|
|
233
|
+
*/
|
|
234
|
+
function createStateChange(state, previousState) {
|
|
235
|
+
return {
|
|
236
|
+
state,
|
|
237
|
+
previousState
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
//#endregion
|
|
241
|
+
//#region websocket.ts
|
|
242
|
+
/**
|
|
243
|
+
* WebSocket 连接管理器
|
|
244
|
+
*/
|
|
245
|
+
var WebSocketManager = class {
|
|
246
|
+
constructor(options, emitter) {
|
|
247
|
+
this.ws = null;
|
|
248
|
+
this.currentState = "disconnected";
|
|
249
|
+
this.reconnectAttempts = 0;
|
|
250
|
+
this.reconnectTimer = null;
|
|
251
|
+
this.heartbeatTimer = null;
|
|
252
|
+
this.messageQueue = [];
|
|
253
|
+
this.token = null;
|
|
254
|
+
this.intentionalClose = false;
|
|
255
|
+
this.options = {
|
|
256
|
+
baseURL: options.baseURL,
|
|
257
|
+
gatewayURL: options.gatewayURL,
|
|
258
|
+
reconnectInterval: options.reconnectInterval ?? 3e3,
|
|
259
|
+
maxReconnectAttempts: options.maxReconnectAttempts ?? 10,
|
|
260
|
+
heartbeatInterval: options.heartbeatInterval ?? 3e4,
|
|
261
|
+
messageBufferSize: options.messageBufferSize ?? 100
|
|
262
|
+
};
|
|
263
|
+
this.emitter = emitter;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* 设置认证 Token
|
|
267
|
+
*/
|
|
268
|
+
setToken(token) {
|
|
269
|
+
this.token = token;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* 清除认证 Token
|
|
273
|
+
*/
|
|
274
|
+
clearToken() {
|
|
275
|
+
this.token = null;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* 获取当前连接状态
|
|
279
|
+
*/
|
|
280
|
+
getState() {
|
|
281
|
+
return this.currentState;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* 是否已连接
|
|
285
|
+
*/
|
|
286
|
+
isConnected() {
|
|
287
|
+
return this.currentState === "connected" && this.ws?.readyState === WebSocket.OPEN;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* 更新连接状态并触发事件
|
|
291
|
+
*/
|
|
292
|
+
setState(newState) {
|
|
293
|
+
const previousState = this.currentState;
|
|
294
|
+
this.currentState = newState;
|
|
295
|
+
this.emitter.emit("connection:state:change", createStateChange(newState, previousState));
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* 连接到 WebSocket 网关
|
|
299
|
+
*/
|
|
300
|
+
async connect() {
|
|
301
|
+
if (this.isConnected()) return;
|
|
302
|
+
if (!this.token) throw new Error("Token is required before connecting to WebSocket");
|
|
303
|
+
this.intentionalClose = false;
|
|
304
|
+
this.setState("connecting");
|
|
305
|
+
try {
|
|
306
|
+
const wsUrl = new URL(this.options.gatewayURL);
|
|
307
|
+
wsUrl.searchParams.append("token", this.token);
|
|
308
|
+
this.ws = new WebSocket(wsUrl.toString());
|
|
309
|
+
await this.setupWebSocketHandlers();
|
|
310
|
+
} catch (error) {
|
|
311
|
+
this.setState("error");
|
|
312
|
+
this.emitter.emit("error", createError("WS_CONNECT_FAILED", "Failed to connect to WebSocket", error instanceof Error ? error : void 0));
|
|
313
|
+
this.scheduleReconnect();
|
|
314
|
+
throw error;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* 断开 WebSocket 连接
|
|
319
|
+
*/
|
|
320
|
+
disconnect() {
|
|
321
|
+
this.intentionalClose = true;
|
|
322
|
+
this.clearTimers();
|
|
323
|
+
if (this.ws) {
|
|
324
|
+
if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) this.ws.close(1e3, "Client disconnect");
|
|
325
|
+
this.ws = null;
|
|
326
|
+
}
|
|
327
|
+
this.reconnectAttempts = 0;
|
|
328
|
+
this.setState("disconnected");
|
|
329
|
+
this.emitter.emit("disconnect", void 0);
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* 发送消息
|
|
333
|
+
*/
|
|
334
|
+
sendMessage(req) {
|
|
335
|
+
if (!this.isConnected()) {
|
|
336
|
+
this.messageQueue.push(req);
|
|
337
|
+
this.emitter.emit("error", createError("WS_NOT_CONNECTED", "WebSocket not connected, message queued"));
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const message = this.buildMessage(req);
|
|
341
|
+
this.ws.send(JSON.stringify(message));
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* 构建发送的消息对象
|
|
345
|
+
*/
|
|
346
|
+
buildMessage(req) {
|
|
347
|
+
return {
|
|
348
|
+
client_msg_id: req.client_msg_id,
|
|
349
|
+
receiver_id: req.receiver_id,
|
|
350
|
+
chat_type: req.chat_type,
|
|
351
|
+
msg_type: req.msg_type,
|
|
352
|
+
payload: req.payload,
|
|
353
|
+
...req.reply_to_msg_id && { reply_to_msg_id: req.reply_to_msg_id },
|
|
354
|
+
...req.ext && { ext: req.ext }
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* 设置 WebSocket 事件处理器
|
|
359
|
+
*/
|
|
360
|
+
setupWebSocketHandlers() {
|
|
361
|
+
return new Promise((resolve, reject) => {
|
|
362
|
+
if (!this.ws) {
|
|
363
|
+
reject(/* @__PURE__ */ new Error("WebSocket instance is null"));
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
this.ws.onopen = () => {
|
|
367
|
+
this.reconnectAttempts = 0;
|
|
368
|
+
this.setState("connected");
|
|
369
|
+
this.emitter.emit("connect", void 0);
|
|
370
|
+
this.startHeartbeat();
|
|
371
|
+
this.flushMessageQueue();
|
|
372
|
+
resolve();
|
|
373
|
+
};
|
|
374
|
+
this.ws.onmessage = (event) => {
|
|
375
|
+
this.handleMessage(event.data);
|
|
376
|
+
};
|
|
377
|
+
this.ws.onclose = (event) => {
|
|
378
|
+
this.clearTimers();
|
|
379
|
+
if (this.intentionalClose) {
|
|
380
|
+
this.setState("disconnected");
|
|
381
|
+
this.emitter.emit("disconnect", {
|
|
382
|
+
code: event.code,
|
|
383
|
+
reason: event.reason
|
|
384
|
+
});
|
|
385
|
+
} else {
|
|
386
|
+
this.setState("reconnecting");
|
|
387
|
+
this.scheduleReconnect();
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
this.ws.onerror = (error) => {
|
|
391
|
+
this.emitter.emit("error", createError("WS_ERROR", "WebSocket error occurred", error instanceof Error ? error : void 0));
|
|
392
|
+
reject(error);
|
|
393
|
+
};
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* 处理接收到的消息
|
|
398
|
+
*/
|
|
399
|
+
handleMessage(data) {
|
|
400
|
+
try {
|
|
401
|
+
const message = JSON.parse(data);
|
|
402
|
+
if (!message.msg_id || !message.sender_id) {
|
|
403
|
+
this.emitter.emit("error", createError("INVALID_MESSAGE", "Received invalid message format"));
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
this.emitter.emit("message:received", { message });
|
|
407
|
+
} catch (error) {
|
|
408
|
+
this.emitter.emit("error", createError("MESSAGE_PARSE_ERROR", data, error instanceof Error ? error : void 0));
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* 启动心跳
|
|
413
|
+
*/
|
|
414
|
+
startHeartbeat() {
|
|
415
|
+
this.heartbeatTimer = setInterval(() => {
|
|
416
|
+
if (this.isConnected()) this.ws.send(JSON.stringify({ type: "ping" }));
|
|
417
|
+
}, this.options.heartbeatInterval);
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* 安排重连
|
|
421
|
+
*/
|
|
422
|
+
scheduleReconnect() {
|
|
423
|
+
if (this.reconnectAttempts >= this.options.maxReconnectAttempts) {
|
|
424
|
+
this.setState("error");
|
|
425
|
+
this.emitter.emit("error", createError("MAX_RECONNECT_REACHED", "Maximum reconnection attempts reached"));
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
this.reconnectAttempts++;
|
|
429
|
+
this.reconnectTimer = setTimeout(() => {
|
|
430
|
+
this.connect().catch(() => {});
|
|
431
|
+
}, this.options.reconnectInterval * this.reconnectAttempts);
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* 清空定时器
|
|
435
|
+
*/
|
|
436
|
+
clearTimers() {
|
|
437
|
+
if (this.reconnectTimer) {
|
|
438
|
+
clearTimeout(this.reconnectTimer);
|
|
439
|
+
this.reconnectTimer = null;
|
|
440
|
+
}
|
|
441
|
+
if (this.heartbeatTimer) {
|
|
442
|
+
clearInterval(this.heartbeatTimer);
|
|
443
|
+
this.heartbeatTimer = null;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* 刷新消息队列(连接成功后发送缓存的消息)
|
|
448
|
+
*/
|
|
449
|
+
flushMessageQueue() {
|
|
450
|
+
while (this.messageQueue.length > 0) {
|
|
451
|
+
const req = this.messageQueue.shift();
|
|
452
|
+
if (req) this.sendMessage(req);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
//#endregion
|
|
457
|
+
//#region index.ts
|
|
458
|
+
/**
|
|
459
|
+
* ChatSDK - 类型安全的聊天 SDK
|
|
460
|
+
*
|
|
461
|
+
* 使用示例:
|
|
462
|
+
* ```typescript
|
|
463
|
+
* const sdk = new ChatSDK({
|
|
464
|
+
* baseURL: 'http://localhost:8080',
|
|
465
|
+
* gatewayURL: 'ws://localhost:8081/ws',
|
|
466
|
+
* });
|
|
467
|
+
*
|
|
468
|
+
* // 注册/登录
|
|
469
|
+
* await sdk.register({ username: 'test', password: '123456' });
|
|
470
|
+
* await sdk.login({ username: 'test', password: '123456' });
|
|
471
|
+
*
|
|
472
|
+
* // 连接 WebSocket
|
|
473
|
+
* await sdk.connect();
|
|
474
|
+
*
|
|
475
|
+
* // 监听消息
|
|
476
|
+
* sdk.on(ChatEventType.MessageReceived, (event) => {
|
|
477
|
+
* console.log('收到消息:', event.data.message);
|
|
478
|
+
* });
|
|
479
|
+
*
|
|
480
|
+
* // 发送文本消息
|
|
481
|
+
* sdk.sendTextMessage({
|
|
482
|
+
* receiver_id: 'xxx',
|
|
483
|
+
* chat_type: ChatType.Single,
|
|
484
|
+
* text: 'Hello!',
|
|
485
|
+
* });
|
|
486
|
+
* ```
|
|
487
|
+
*/
|
|
488
|
+
var ChatSDK = class {
|
|
489
|
+
constructor(options) {
|
|
490
|
+
this.currentUser = null;
|
|
491
|
+
this.options = {
|
|
492
|
+
baseURL: options.baseURL,
|
|
493
|
+
gatewayURL: options.gatewayURL,
|
|
494
|
+
reconnectInterval: options.reconnectInterval ?? 3e3,
|
|
495
|
+
maxReconnectAttempts: options.maxReconnectAttempts ?? 10,
|
|
496
|
+
heartbeatInterval: options.heartbeatInterval ?? 3e4,
|
|
497
|
+
messageBufferSize: options.messageBufferSize ?? 100
|
|
498
|
+
};
|
|
499
|
+
this.emitter = new EventEmitter();
|
|
500
|
+
this.api = new APIClient(this.options.baseURL);
|
|
501
|
+
this.wsManager = new WebSocketManager(this.options, this.emitter);
|
|
502
|
+
this.setupEventForwarding();
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* 监听事件 - 类型安全的事件监听
|
|
506
|
+
*/
|
|
507
|
+
on(event, listener) {
|
|
508
|
+
return this.emitter.on(event, listener);
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* 监听一次性事件 - 类型安全的事件监听
|
|
512
|
+
*/
|
|
513
|
+
once(event, listener) {
|
|
514
|
+
this.emitter.once(event, listener);
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* 取消监听
|
|
518
|
+
*/
|
|
519
|
+
off(event, listener) {
|
|
520
|
+
this.emitter.off(event, listener);
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* 移除所有监听器
|
|
524
|
+
*/
|
|
525
|
+
removeAllListeners(event) {
|
|
526
|
+
this.emitter.removeAllListeners(event);
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* 用户注册
|
|
530
|
+
*/
|
|
531
|
+
async register(req) {
|
|
532
|
+
const resp = await this.api.register(req);
|
|
533
|
+
this.setAuth(resp);
|
|
534
|
+
return resp;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* 用户登录
|
|
538
|
+
*/
|
|
539
|
+
async login(req) {
|
|
540
|
+
const resp = await this.api.login(req);
|
|
541
|
+
this.setAuth(resp);
|
|
542
|
+
return resp;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* 批量生成用户(测试/管理用途)
|
|
546
|
+
*/
|
|
547
|
+
async batchGenerateUsers(req) {
|
|
548
|
+
return (await this.api.batchGenerate(req)).users;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* 设置认证信息
|
|
552
|
+
*/
|
|
553
|
+
setAuth(user) {
|
|
554
|
+
this.currentUser = user;
|
|
555
|
+
this.api.setToken(user.token);
|
|
556
|
+
this.wsManager.setToken(user.token);
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* 清除认证信息
|
|
560
|
+
*/
|
|
561
|
+
clearAuth() {
|
|
562
|
+
this.currentUser = null;
|
|
563
|
+
this.api.clearToken();
|
|
564
|
+
this.wsManager.clearToken();
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* 获取当前用户信息
|
|
568
|
+
*/
|
|
569
|
+
getCurrentUser() {
|
|
570
|
+
return this.currentUser;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* 检查是否已认证
|
|
574
|
+
*/
|
|
575
|
+
isAuthenticated() {
|
|
576
|
+
return !!this.currentUser && !!this.api.getToken();
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* 连接到消息网关
|
|
580
|
+
*/
|
|
581
|
+
async connect() {
|
|
582
|
+
if (!this.isAuthenticated()) throw new Error("Must be authenticated before connecting");
|
|
583
|
+
return this.wsManager.connect();
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* 断开消息网关连接
|
|
587
|
+
*/
|
|
588
|
+
disconnect() {
|
|
589
|
+
this.wsManager.disconnect();
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* 获取连接状态
|
|
593
|
+
*/
|
|
594
|
+
getConnectionState() {
|
|
595
|
+
return this.wsManager.getState();
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* 是否已连接
|
|
599
|
+
*/
|
|
600
|
+
isConnected() {
|
|
601
|
+
return this.wsManager.isConnected();
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* 发送原始消息
|
|
605
|
+
*/
|
|
606
|
+
sendMessage(req) {
|
|
607
|
+
if (!req.client_msg_id) req.client_msg_id = generateUUID();
|
|
608
|
+
this.wsManager.sendMessage(req);
|
|
609
|
+
this.emitter.emit("message:sent", { client_msg_id: req.client_msg_id });
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* 发送文本消息
|
|
613
|
+
*/
|
|
614
|
+
sendTextMessage(params) {
|
|
615
|
+
const { text, ...rest } = params;
|
|
616
|
+
this.sendMessage({
|
|
617
|
+
...rest,
|
|
618
|
+
msg_type: "text",
|
|
619
|
+
payload: { text }
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* 发送图片消息
|
|
624
|
+
*/
|
|
625
|
+
sendImageMessage(params) {
|
|
626
|
+
const { url, width, height, size, ...rest } = params;
|
|
627
|
+
this.sendMessage({
|
|
628
|
+
...rest,
|
|
629
|
+
msg_type: "image",
|
|
630
|
+
payload: {
|
|
631
|
+
url,
|
|
632
|
+
width,
|
|
633
|
+
height,
|
|
634
|
+
size
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* 发送视频消息
|
|
640
|
+
*/
|
|
641
|
+
sendVideoMessage(params) {
|
|
642
|
+
const { url, duration, width, height, size, thumbnail_url, ...rest } = params;
|
|
643
|
+
this.sendMessage({
|
|
644
|
+
...rest,
|
|
645
|
+
msg_type: "video",
|
|
646
|
+
payload: {
|
|
647
|
+
url,
|
|
648
|
+
duration,
|
|
649
|
+
width,
|
|
650
|
+
height,
|
|
651
|
+
size,
|
|
652
|
+
thumbnail_url
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* 发送文件消息
|
|
658
|
+
*/
|
|
659
|
+
sendFileMessage(params) {
|
|
660
|
+
const { url, name, size, mime_type, ...rest } = params;
|
|
661
|
+
this.sendMessage({
|
|
662
|
+
...rest,
|
|
663
|
+
msg_type: "file",
|
|
664
|
+
payload: {
|
|
665
|
+
url,
|
|
666
|
+
name,
|
|
667
|
+
size,
|
|
668
|
+
mime_type
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* 获取历史消息
|
|
674
|
+
*/
|
|
675
|
+
async getHistoryMessages(params) {
|
|
676
|
+
return this.api.getHistoryMessages(params);
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* 生成 UUID(用于 client_msg_id)
|
|
680
|
+
*/
|
|
681
|
+
generateMessageId() {
|
|
682
|
+
return generateUUID();
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* 验证 UUID 格式
|
|
686
|
+
*/
|
|
687
|
+
validateMessageId(id) {
|
|
688
|
+
return isValidUUID(id);
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* 设置事件转发
|
|
692
|
+
*/
|
|
693
|
+
setupEventForwarding() {}
|
|
694
|
+
};
|
|
695
|
+
//#endregion
|
|
696
|
+
export { APIClient, APIError, ChatEventType, ChatSDK, ChatSDK as default, ChatType, ConnectionState, EventEmitter, MessageType, WebSocketManager, createError, createStateChange, delay, generateUUID, isValidUUID };
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "go-chat-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Type-safe Chat SDK for Go-Chat IM service",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.mts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"chat",
|
|
13
|
+
"sdk",
|
|
14
|
+
"websocket",
|
|
15
|
+
"im",
|
|
16
|
+
"instant-messaging",
|
|
17
|
+
"typescript"
|
|
18
|
+
],
|
|
19
|
+
"author": "sanbei101",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^25.6.0",
|
|
23
|
+
"@types/ws": "^8.18.1",
|
|
24
|
+
"tsdown": "^0.21.9",
|
|
25
|
+
"typescript": "^6.0.3",
|
|
26
|
+
"vitest": "^4.1.5",
|
|
27
|
+
"ws": "^8.20.0"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=24"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsdown",
|
|
34
|
+
"test": "vitest run"
|
|
35
|
+
}
|
|
36
|
+
}
|