tgo-widget-miniprogram 1.0.0 → 1.1.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/miniprogram_dist/core/chatStore.js +1494 -591
- package/miniprogram_dist/services/wukongim.js +1118 -170
- package/miniprogram_dist/utils/jsonRender.js +18538 -109
- package/miniprogram_dist/utils/markdown.js +1214 -21
- package/package.json +7 -8
|
@@ -1,30 +1,965 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
var
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
var
|
|
13
|
-
var
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
3
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
// node_modules/easyjssdk/dist/cjs/index.js
|
|
7
|
+
var require_cjs = __commonJS({
|
|
8
|
+
"node_modules/easyjssdk/dist/cjs/index.js"(exports2) {
|
|
9
|
+
"use strict";
|
|
10
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
11
|
+
exports2.WKIMDeviceFlag = exports2.WKIMEvent = exports2.WKIMChannelType = exports2.WKIM = exports2.DeviceFlag = exports2.ReasonCode = exports2.Event = exports2.ChannelType = exports2.currentPlatform = exports2.PlatformType = void 0;
|
|
12
|
+
var WS_CONNECTING = 0;
|
|
13
|
+
var WS_OPEN = 1;
|
|
14
|
+
var WS_CLOSING = 2;
|
|
15
|
+
var WS_CLOSED = 3;
|
|
16
|
+
var PlatformType;
|
|
17
|
+
(function(PlatformType2) {
|
|
18
|
+
PlatformType2["Browser"] = "browser";
|
|
19
|
+
PlatformType2["NodeJS"] = "nodejs";
|
|
20
|
+
PlatformType2["WeChat"] = "wechat";
|
|
21
|
+
PlatformType2["Alipay"] = "alipay";
|
|
22
|
+
PlatformType2["UniApp"] = "uniapp";
|
|
23
|
+
})(PlatformType || (exports2.PlatformType = PlatformType = {}));
|
|
24
|
+
var WeChatWebSocketAdapter = class {
|
|
25
|
+
constructor(url) {
|
|
26
|
+
this.socketTask = null;
|
|
27
|
+
this._readyState = WS_CONNECTING;
|
|
28
|
+
this.onopen = null;
|
|
29
|
+
this.onmessage = null;
|
|
30
|
+
this.onerror = null;
|
|
31
|
+
this.onclose = null;
|
|
32
|
+
if (typeof wx === "undefined") {
|
|
33
|
+
throw new Error("WeChat Mini Program environment not detected");
|
|
34
|
+
}
|
|
35
|
+
this.socketTask = wx.connectSocket({
|
|
36
|
+
url,
|
|
37
|
+
success: () => {
|
|
38
|
+
console.log("WeChat WebSocket connecting...");
|
|
39
|
+
},
|
|
40
|
+
fail: (err) => {
|
|
41
|
+
console.error("WeChat WebSocket connection failed:", err);
|
|
42
|
+
this._readyState = WS_CLOSED;
|
|
43
|
+
if (this.onerror) {
|
|
44
|
+
this.onerror({ message: err.errMsg || "Connection failed" });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
this.socketTask.onOpen((res) => {
|
|
49
|
+
this._readyState = WS_OPEN;
|
|
50
|
+
if (this.onopen) {
|
|
51
|
+
this.onopen(res);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
this.socketTask.onMessage((res) => {
|
|
55
|
+
if (this.onmessage) {
|
|
56
|
+
const data = res.data instanceof ArrayBuffer ? new TextDecoder().decode(res.data) : res.data;
|
|
57
|
+
this.onmessage({ data });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
this.socketTask.onError((res) => {
|
|
61
|
+
console.error("WeChat WebSocket error:", res);
|
|
62
|
+
if (this.onerror) {
|
|
63
|
+
this.onerror({ message: res.errMsg || "WebSocket error" });
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
this.socketTask.onClose((res) => {
|
|
67
|
+
this._readyState = WS_CLOSED;
|
|
68
|
+
if (this.onclose) {
|
|
69
|
+
this.onclose({ code: res.code || 1e3, reason: res.reason || "" });
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
get readyState() {
|
|
74
|
+
return this._readyState;
|
|
75
|
+
}
|
|
76
|
+
send(data) {
|
|
77
|
+
if (this._readyState !== WS_OPEN || !this.socketTask) {
|
|
78
|
+
throw new Error("WebSocket is not open");
|
|
79
|
+
}
|
|
80
|
+
this.socketTask.send({
|
|
81
|
+
data,
|
|
82
|
+
fail: (err) => {
|
|
83
|
+
console.error("WeChat WebSocket send failed:", err);
|
|
84
|
+
if (this.onerror) {
|
|
85
|
+
this.onerror({ message: "Send failed" });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
close(code, reason) {
|
|
91
|
+
if (this._readyState === WS_CLOSED || this._readyState === WS_CLOSING) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
this._readyState = WS_CLOSING;
|
|
95
|
+
if (this.socketTask) {
|
|
96
|
+
this.socketTask.close({
|
|
97
|
+
code: code || 1e3,
|
|
98
|
+
reason: reason || "",
|
|
99
|
+
fail: (err) => {
|
|
100
|
+
console.error("WeChat WebSocket close failed:", err);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
var AlipayWebSocketAdapter = class {
|
|
107
|
+
constructor(url) {
|
|
108
|
+
this._readyState = WS_CONNECTING;
|
|
109
|
+
this.boundOnOpen = null;
|
|
110
|
+
this.boundOnMessage = null;
|
|
111
|
+
this.boundOnError = null;
|
|
112
|
+
this.boundOnClose = null;
|
|
113
|
+
this.onopen = null;
|
|
114
|
+
this.onmessage = null;
|
|
115
|
+
this.onerror = null;
|
|
116
|
+
this.onclose = null;
|
|
117
|
+
if (typeof my === "undefined") {
|
|
118
|
+
throw new Error("Alipay Mini Program environment not detected");
|
|
119
|
+
}
|
|
120
|
+
this.boundOnOpen = (res) => {
|
|
121
|
+
this._readyState = WS_OPEN;
|
|
122
|
+
if (this.onopen) {
|
|
123
|
+
this.onopen(res);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
this.boundOnMessage = (res) => {
|
|
127
|
+
if (this.onmessage) {
|
|
128
|
+
const data = res.data instanceof ArrayBuffer ? new TextDecoder().decode(res.data) : res.data;
|
|
129
|
+
this.onmessage({ data });
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
this.boundOnError = (res) => {
|
|
133
|
+
console.error("Alipay WebSocket error:", res);
|
|
134
|
+
if (this.onerror) {
|
|
135
|
+
this.onerror({ message: res.errorMessage || "WebSocket error" });
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
this.boundOnClose = (res) => {
|
|
139
|
+
this._readyState = WS_CLOSED;
|
|
140
|
+
this.cleanup();
|
|
141
|
+
if (this.onclose) {
|
|
142
|
+
this.onclose({ code: res.code || 1e3, reason: res.reason || "" });
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
my.onSocketOpen(this.boundOnOpen);
|
|
146
|
+
my.onSocketMessage(this.boundOnMessage);
|
|
147
|
+
my.onSocketError(this.boundOnError);
|
|
148
|
+
my.onSocketClose(this.boundOnClose);
|
|
149
|
+
my.connectSocket({
|
|
150
|
+
url,
|
|
151
|
+
success: () => {
|
|
152
|
+
console.log("Alipay WebSocket connecting...");
|
|
153
|
+
},
|
|
154
|
+
fail: (err) => {
|
|
155
|
+
console.error("Alipay WebSocket connection failed:", err);
|
|
156
|
+
this._readyState = WS_CLOSED;
|
|
157
|
+
this.cleanup();
|
|
158
|
+
if (this.onerror) {
|
|
159
|
+
this.onerror({ message: err.errorMessage || "Connection failed" });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
get readyState() {
|
|
165
|
+
return this._readyState;
|
|
166
|
+
}
|
|
167
|
+
send(data) {
|
|
168
|
+
if (typeof my === "undefined") {
|
|
169
|
+
throw new Error("Alipay Mini Program environment not detected");
|
|
170
|
+
}
|
|
171
|
+
if (this._readyState !== WS_OPEN) {
|
|
172
|
+
throw new Error("WebSocket is not open");
|
|
173
|
+
}
|
|
174
|
+
my.sendSocketMessage({
|
|
175
|
+
data,
|
|
176
|
+
fail: (err) => {
|
|
177
|
+
console.error("Alipay WebSocket send failed:", err);
|
|
178
|
+
if (this.onerror) {
|
|
179
|
+
this.onerror({ message: "Send failed" });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
close(code, reason) {
|
|
185
|
+
if (typeof my === "undefined") {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (this._readyState === WS_CLOSED || this._readyState === WS_CLOSING) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
this._readyState = WS_CLOSING;
|
|
192
|
+
my.closeSocket({
|
|
193
|
+
code: code || 1e3,
|
|
194
|
+
reason: reason || "",
|
|
195
|
+
fail: (err) => {
|
|
196
|
+
console.error("Alipay WebSocket close failed:", err);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
cleanup() {
|
|
201
|
+
if (typeof my !== "undefined") {
|
|
202
|
+
my.offSocketOpen(this.boundOnOpen);
|
|
203
|
+
my.offSocketMessage(this.boundOnMessage);
|
|
204
|
+
my.offSocketError(this.boundOnError);
|
|
205
|
+
my.offSocketClose(this.boundOnClose);
|
|
206
|
+
this.boundOnOpen = null;
|
|
207
|
+
this.boundOnMessage = null;
|
|
208
|
+
this.boundOnError = null;
|
|
209
|
+
this.boundOnClose = null;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
var UniAppWebSocketAdapter = class {
|
|
214
|
+
constructor(url) {
|
|
215
|
+
this.socketTask = null;
|
|
216
|
+
this._readyState = WS_CONNECTING;
|
|
217
|
+
this.onopen = null;
|
|
218
|
+
this.onmessage = null;
|
|
219
|
+
this.onerror = null;
|
|
220
|
+
this.onclose = null;
|
|
221
|
+
if (typeof uni === "undefined") {
|
|
222
|
+
throw new Error("UniApp environment not detected");
|
|
223
|
+
}
|
|
224
|
+
this.socketTask = uni.connectSocket({
|
|
225
|
+
url,
|
|
226
|
+
success: () => {
|
|
227
|
+
console.log("UniApp WebSocket connecting...");
|
|
228
|
+
},
|
|
229
|
+
fail: (err) => {
|
|
230
|
+
console.error("UniApp WebSocket connection failed:", err);
|
|
231
|
+
this._readyState = WS_CLOSED;
|
|
232
|
+
if (this.onerror) {
|
|
233
|
+
this.onerror({ message: err.errMsg || "Connection failed" });
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
this.socketTask.onOpen((res) => {
|
|
238
|
+
this._readyState = WS_OPEN;
|
|
239
|
+
if (this.onopen) {
|
|
240
|
+
this.onopen(res);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
this.socketTask.onMessage((res) => {
|
|
244
|
+
if (this.onmessage) {
|
|
245
|
+
const data = res.data instanceof ArrayBuffer ? new TextDecoder().decode(res.data) : res.data;
|
|
246
|
+
this.onmessage({ data });
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
this.socketTask.onError((res) => {
|
|
250
|
+
console.error("UniApp WebSocket error:", res);
|
|
251
|
+
if (this.onerror) {
|
|
252
|
+
this.onerror({ message: res.errMsg || "WebSocket error" });
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
this.socketTask.onClose((res) => {
|
|
256
|
+
this._readyState = WS_CLOSED;
|
|
257
|
+
if (this.onclose) {
|
|
258
|
+
this.onclose({ code: res.code || 1e3, reason: res.reason || "" });
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
get readyState() {
|
|
263
|
+
return this._readyState;
|
|
264
|
+
}
|
|
265
|
+
send(data) {
|
|
266
|
+
if (this._readyState !== WS_OPEN || !this.socketTask) {
|
|
267
|
+
throw new Error("WebSocket is not open");
|
|
268
|
+
}
|
|
269
|
+
this.socketTask.send({
|
|
270
|
+
data,
|
|
271
|
+
fail: (err) => {
|
|
272
|
+
console.error("UniApp WebSocket send failed:", err);
|
|
273
|
+
if (this.onerror) {
|
|
274
|
+
this.onerror({ message: "Send failed" });
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
close(code, reason) {
|
|
280
|
+
if (this._readyState === WS_CLOSED || this._readyState === WS_CLOSING) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
this._readyState = WS_CLOSING;
|
|
284
|
+
if (this.socketTask) {
|
|
285
|
+
this.socketTask.close({
|
|
286
|
+
code: code || 1e3,
|
|
287
|
+
reason: reason || "",
|
|
288
|
+
fail: (err) => {
|
|
289
|
+
console.error("UniApp WebSocket close failed:", err);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
function detectPlatform() {
|
|
296
|
+
if (typeof uni !== "undefined" && typeof uni.connectSocket === "function") {
|
|
297
|
+
return PlatformType.UniApp;
|
|
298
|
+
}
|
|
299
|
+
if (typeof wx !== "undefined" && typeof wx.connectSocket === "function") {
|
|
300
|
+
return PlatformType.WeChat;
|
|
301
|
+
}
|
|
302
|
+
if (typeof my !== "undefined" && typeof my.connectSocket === "function") {
|
|
303
|
+
return PlatformType.Alipay;
|
|
304
|
+
}
|
|
305
|
+
if (typeof WebSocket !== "undefined") {
|
|
306
|
+
return PlatformType.Browser;
|
|
307
|
+
}
|
|
308
|
+
return PlatformType.NodeJS;
|
|
309
|
+
}
|
|
310
|
+
var currentPlatform = detectPlatform();
|
|
311
|
+
exports2.currentPlatform = currentPlatform;
|
|
312
|
+
function createWebSocket(url) {
|
|
313
|
+
switch (currentPlatform) {
|
|
314
|
+
case PlatformType.UniApp:
|
|
315
|
+
return new UniAppWebSocketAdapter(url);
|
|
316
|
+
case PlatformType.WeChat:
|
|
317
|
+
return new WeChatWebSocketAdapter(url);
|
|
318
|
+
case PlatformType.Alipay:
|
|
319
|
+
return new AlipayWebSocketAdapter(url);
|
|
320
|
+
case PlatformType.Browser:
|
|
321
|
+
return new WebSocket(url);
|
|
322
|
+
case PlatformType.NodeJS:
|
|
323
|
+
default:
|
|
324
|
+
try {
|
|
325
|
+
const dynamicRequire = new Function("mod", "return require(mod)");
|
|
326
|
+
const Ws = dynamicRequire("ws");
|
|
327
|
+
const WsImpl = Ws.WebSocket || Ws;
|
|
328
|
+
return new WsImpl(url);
|
|
329
|
+
} catch (e) {
|
|
330
|
+
throw new Error("WebSocket is not available in this environment. Install 'ws' package for Node.js.");
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
var ChannelType;
|
|
335
|
+
(function(ChannelType2) {
|
|
336
|
+
ChannelType2[ChannelType2["Person"] = 1] = "Person";
|
|
337
|
+
ChannelType2[ChannelType2["Group"] = 2] = "Group";
|
|
338
|
+
ChannelType2[ChannelType2["CustomerService"] = 3] = "CustomerService";
|
|
339
|
+
ChannelType2[ChannelType2["Community"] = 4] = "Community";
|
|
340
|
+
ChannelType2[ChannelType2["CommunityTopic"] = 5] = "CommunityTopic";
|
|
341
|
+
ChannelType2[ChannelType2["Info"] = 6] = "Info";
|
|
342
|
+
ChannelType2[ChannelType2["Data"] = 7] = "Data";
|
|
343
|
+
ChannelType2[ChannelType2["Temp"] = 8] = "Temp";
|
|
344
|
+
ChannelType2[ChannelType2["Live"] = 9] = "Live";
|
|
345
|
+
ChannelType2[ChannelType2["Visitors"] = 10] = "Visitors";
|
|
346
|
+
})(ChannelType || (exports2.WKIMChannelType = exports2.ChannelType = ChannelType = {}));
|
|
347
|
+
var Event;
|
|
348
|
+
(function(Event2) {
|
|
349
|
+
Event2["Connect"] = "connect";
|
|
350
|
+
Event2["Disconnect"] = "disconnect";
|
|
351
|
+
Event2["Message"] = "message";
|
|
352
|
+
Event2["Error"] = "error";
|
|
353
|
+
Event2["SendAck"] = "sendack";
|
|
354
|
+
Event2["Reconnecting"] = "reconnecting";
|
|
355
|
+
Event2["CustomEvent"] = "customevent";
|
|
356
|
+
})(Event || (exports2.WKIMEvent = exports2.Event = Event = {}));
|
|
357
|
+
var ReasonCode;
|
|
358
|
+
(function(ReasonCode2) {
|
|
359
|
+
ReasonCode2[ReasonCode2["Unknown"] = 0] = "Unknown";
|
|
360
|
+
ReasonCode2[ReasonCode2["Success"] = 1] = "Success";
|
|
361
|
+
ReasonCode2[ReasonCode2["AuthFail"] = 2] = "AuthFail";
|
|
362
|
+
ReasonCode2[ReasonCode2["SubscriberNotExist"] = 3] = "SubscriberNotExist";
|
|
363
|
+
ReasonCode2[ReasonCode2["InBlacklist"] = 4] = "InBlacklist";
|
|
364
|
+
ReasonCode2[ReasonCode2["ChannelNotExist"] = 5] = "ChannelNotExist";
|
|
365
|
+
ReasonCode2[ReasonCode2["UserNotOnNode"] = 6] = "UserNotOnNode";
|
|
366
|
+
ReasonCode2[ReasonCode2["SenderOffline"] = 7] = "SenderOffline";
|
|
367
|
+
ReasonCode2[ReasonCode2["MsgKeyError"] = 8] = "MsgKeyError";
|
|
368
|
+
ReasonCode2[ReasonCode2["PayloadDecodeError"] = 9] = "PayloadDecodeError";
|
|
369
|
+
ReasonCode2[ReasonCode2["ForwardSendPacketError"] = 10] = "ForwardSendPacketError";
|
|
370
|
+
ReasonCode2[ReasonCode2["NotAllowSend"] = 11] = "NotAllowSend";
|
|
371
|
+
ReasonCode2[ReasonCode2["ConnectKick"] = 12] = "ConnectKick";
|
|
372
|
+
ReasonCode2[ReasonCode2["NotInWhitelist"] = 13] = "NotInWhitelist";
|
|
373
|
+
ReasonCode2[ReasonCode2["QueryTokenError"] = 14] = "QueryTokenError";
|
|
374
|
+
ReasonCode2[ReasonCode2["SystemError"] = 15] = "SystemError";
|
|
375
|
+
ReasonCode2[ReasonCode2["ChannelIDError"] = 16] = "ChannelIDError";
|
|
376
|
+
ReasonCode2[ReasonCode2["NodeMatchError"] = 17] = "NodeMatchError";
|
|
377
|
+
ReasonCode2[ReasonCode2["NodeNotMatch"] = 18] = "NodeNotMatch";
|
|
378
|
+
ReasonCode2[ReasonCode2["Ban"] = 19] = "Ban";
|
|
379
|
+
ReasonCode2[ReasonCode2["NotSupportHeader"] = 20] = "NotSupportHeader";
|
|
380
|
+
ReasonCode2[ReasonCode2["ClientKeyIsEmpty"] = 21] = "ClientKeyIsEmpty";
|
|
381
|
+
ReasonCode2[ReasonCode2["RateLimit"] = 22] = "RateLimit";
|
|
382
|
+
ReasonCode2[ReasonCode2["NotSupportChannelType"] = 23] = "NotSupportChannelType";
|
|
383
|
+
ReasonCode2[ReasonCode2["Disband"] = 24] = "Disband";
|
|
384
|
+
ReasonCode2[ReasonCode2["SendBan"] = 25] = "SendBan";
|
|
385
|
+
})(ReasonCode || (exports2.ReasonCode = ReasonCode = {}));
|
|
386
|
+
var DeviceFlag;
|
|
387
|
+
(function(DeviceFlag2) {
|
|
388
|
+
DeviceFlag2[DeviceFlag2["App"] = 0] = "App";
|
|
389
|
+
DeviceFlag2[DeviceFlag2["Web"] = 1] = "Web";
|
|
390
|
+
DeviceFlag2[DeviceFlag2["Desktop"] = 2] = "Desktop";
|
|
391
|
+
})(DeviceFlag || (exports2.WKIMDeviceFlag = exports2.DeviceFlag = DeviceFlag = {}));
|
|
392
|
+
var WKIM = class _WKIM {
|
|
393
|
+
constructor(url, auth) {
|
|
394
|
+
this.ws = null;
|
|
395
|
+
this.isConnected = false;
|
|
396
|
+
this.connectionPromise = null;
|
|
397
|
+
this.pingInterval = null;
|
|
398
|
+
this.pingTimeout = null;
|
|
399
|
+
this.PING_INTERVAL_MS = 25 * 1e3;
|
|
400
|
+
this.PONG_TIMEOUT_MS = 10 * 1e3;
|
|
401
|
+
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
402
|
+
this.eventListeners = /* @__PURE__ */ new Map();
|
|
403
|
+
this.reconnectAttempts = 0;
|
|
404
|
+
this.maxReconnectAttempts = 5;
|
|
405
|
+
this.initialReconnectDelay = 1e3;
|
|
406
|
+
this.isReconnecting = false;
|
|
407
|
+
this.manualDisconnect = false;
|
|
408
|
+
this.beforeUnloadHandler = null;
|
|
409
|
+
this.url = url;
|
|
410
|
+
this.auth = auth || {};
|
|
411
|
+
this.sessionId = this.generateUUID();
|
|
412
|
+
if (!this.auth.deviceId || this.auth.deviceId === "") {
|
|
413
|
+
this.auth.deviceId = `web_${this.sessionId.slice(0, 8)}_${Date.now()}`;
|
|
414
|
+
}
|
|
415
|
+
Object.values(Event).forEach((event) => this.eventListeners.set(event, []));
|
|
416
|
+
this.setupBeforeUnloadHandler();
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Initializes the WKIM instance.
|
|
420
|
+
* @param url WebSocket server URL (e.g., "ws://localhost:5100")
|
|
421
|
+
* @param auth Authentication options { uid, token, ... }
|
|
422
|
+
* @param options Configuration options { singleton: boolean }
|
|
423
|
+
* @returns A WKIM instance
|
|
424
|
+
*/
|
|
425
|
+
static init(url, auth, options = {}) {
|
|
426
|
+
if (!url || !auth || !auth.uid || !auth.token) {
|
|
427
|
+
throw new Error("URL, uid, and token are required for initialization.");
|
|
428
|
+
}
|
|
429
|
+
if (options.singleton && _WKIM.globalInstance) {
|
|
430
|
+
console.log("Destroying previous global instance...");
|
|
431
|
+
_WKIM.globalInstance.destroy();
|
|
432
|
+
}
|
|
433
|
+
const instance = new _WKIM(url, auth);
|
|
434
|
+
if (options.singleton !== false) {
|
|
435
|
+
_WKIM.globalInstance = instance;
|
|
436
|
+
}
|
|
437
|
+
return instance;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Establishes connection and authenticates with the server.
|
|
441
|
+
* Returns a Promise that resolves on successful connection/authentication,
|
|
442
|
+
* or rejects on failure.
|
|
443
|
+
*/
|
|
444
|
+
connect() {
|
|
445
|
+
return new Promise((resolve, reject) => {
|
|
446
|
+
var _a;
|
|
447
|
+
if (this.isConnected || ((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WS_CONNECTING) {
|
|
448
|
+
console.warn("Connection already established or in progress.");
|
|
449
|
+
if (this.isConnected) {
|
|
450
|
+
resolve();
|
|
451
|
+
} else if (this.connectionPromise) {
|
|
452
|
+
this.connectionPromise.resolve = resolve;
|
|
453
|
+
this.connectionPromise.reject = reject;
|
|
454
|
+
} else {
|
|
455
|
+
reject(new Error("Already connecting, but no connection promise found."));
|
|
456
|
+
}
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
this.manualDisconnect = false;
|
|
460
|
+
this.connectionPromise = { resolve, reject };
|
|
461
|
+
try {
|
|
462
|
+
console.log(`Connecting to ${this.url}... (Platform: ${currentPlatform})`);
|
|
463
|
+
this.ws = createWebSocket(this.url);
|
|
464
|
+
this.ws.onopen = () => {
|
|
465
|
+
console.log("WebSocket connection opened. Authenticating...");
|
|
466
|
+
this.sendConnectRequest();
|
|
467
|
+
};
|
|
468
|
+
this.ws.onmessage = (event) => {
|
|
469
|
+
this.handleMessage(event.data);
|
|
470
|
+
};
|
|
471
|
+
this.ws.onerror = (event) => {
|
|
472
|
+
const errorMessage = event.message || (event.error ? event.error.message : "WebSocket error");
|
|
473
|
+
console.error("WebSocket error:", errorMessage, event);
|
|
474
|
+
this.emit(Event.Error, event.error || new Error(errorMessage));
|
|
475
|
+
};
|
|
476
|
+
this.ws.onclose = (event) => {
|
|
477
|
+
const wasConnected = this.isConnected;
|
|
478
|
+
console.log(`WebSocket connection closed. Code: ${event.code}, Reason: ${event.reason}`);
|
|
479
|
+
if (this.connectionPromise && !this.isConnected) {
|
|
480
|
+
this.connectionPromise.reject(new Error(`Connection closed before authentication (Code: ${event.code})`));
|
|
481
|
+
}
|
|
482
|
+
this.cleanupConnection();
|
|
483
|
+
this.emit(Event.Disconnect, { code: event.code, reason: event.reason });
|
|
484
|
+
if (wasConnected && !this.manualDisconnect) {
|
|
485
|
+
this.tryReconnect();
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
} catch (error) {
|
|
489
|
+
console.error("Failed to create WebSocket:", error);
|
|
490
|
+
this.emit(Event.Error, error);
|
|
491
|
+
if (this.connectionPromise) {
|
|
492
|
+
this.connectionPromise.reject(error);
|
|
493
|
+
this.connectionPromise = null;
|
|
494
|
+
}
|
|
495
|
+
this.cleanupConnection();
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Disconnects from the server.
|
|
501
|
+
*/
|
|
502
|
+
disconnect() {
|
|
503
|
+
console.log("Manual disconnect initiated.");
|
|
504
|
+
this.manualDisconnect = true;
|
|
505
|
+
this.isReconnecting = false;
|
|
506
|
+
this.cleanupBeforeUnloadHandler();
|
|
507
|
+
this.handleDisconnect(true, "Manual disconnection");
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Completely destroys the SDK instance, cleaning up all resources.
|
|
511
|
+
* Call this when you no longer need the SDK instance.
|
|
512
|
+
*/
|
|
513
|
+
destroy() {
|
|
514
|
+
console.log("Destroying SDK instance...");
|
|
515
|
+
this.disconnect();
|
|
516
|
+
this.eventListeners.clear();
|
|
517
|
+
this.pendingRequests.clear();
|
|
518
|
+
if (_WKIM.globalInstance === this) {
|
|
519
|
+
_WKIM.globalInstance = null;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Sends a message to a specific channel.
|
|
524
|
+
* @param channelId Target channel ID
|
|
525
|
+
* @param channelType Target channel type (e.g., WKIM.ChannelType.Person)
|
|
526
|
+
* @param payload Message payload (must be a JSON-serializable object)
|
|
527
|
+
* @param options Optional: { clientMsgNo, header, setting, msgKey, expire, topic }
|
|
528
|
+
* @returns Promise resolving with { messageId, messageSeq } on server ack, or rejecting on error.
|
|
529
|
+
*/
|
|
530
|
+
send(channelId, channelType, payload, options = {}) {
|
|
531
|
+
if (!this.isConnected || !this.ws || this.ws.readyState !== WS_OPEN) {
|
|
532
|
+
return Promise.reject(new Error("Not connected. Call connect() first."));
|
|
533
|
+
}
|
|
534
|
+
if (typeof payload !== "object" || payload === null) {
|
|
535
|
+
return Promise.reject(new Error("Payload must be a non-null object."));
|
|
536
|
+
}
|
|
537
|
+
const header = options.header || {};
|
|
538
|
+
header.redDot = true;
|
|
539
|
+
const clientMsgNo = options.clientMsgNo || this.generateUUID();
|
|
540
|
+
const params = {
|
|
541
|
+
clientMsgNo,
|
|
542
|
+
channelId,
|
|
543
|
+
channelType,
|
|
544
|
+
payload: this._encodePayloadToBase64(payload),
|
|
545
|
+
header,
|
|
546
|
+
topic: options.topic,
|
|
547
|
+
setting: options.setting
|
|
548
|
+
};
|
|
549
|
+
return this.sendRequest("send", params);
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Registers an event listener.
|
|
553
|
+
* @param eventName The event to listen for (e.g., WKIM.Event.Message)
|
|
554
|
+
* @param callback The function to call when the event occurs
|
|
555
|
+
*/
|
|
556
|
+
on(eventName, callback) {
|
|
557
|
+
var _a;
|
|
558
|
+
if (this.eventListeners.has(eventName)) {
|
|
559
|
+
(_a = this.eventListeners.get(eventName)) === null || _a === void 0 ? void 0 : _a.push(callback);
|
|
560
|
+
} else {
|
|
561
|
+
console.warn(`Attempted to register listener for unknown event: ${eventName}`);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Removes an event listener.
|
|
566
|
+
* @param eventName The event to stop listening for
|
|
567
|
+
* @param callback The specific callback function to remove
|
|
568
|
+
*/
|
|
569
|
+
off(eventName, callback) {
|
|
570
|
+
if (this.eventListeners.has(eventName)) {
|
|
571
|
+
const listeners = this.eventListeners.get(eventName);
|
|
572
|
+
if (listeners) {
|
|
573
|
+
const index = listeners.indexOf(callback);
|
|
574
|
+
if (index > -1) {
|
|
575
|
+
listeners.splice(index, 1);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
// --- Private Methods ---
|
|
581
|
+
emit(eventName, ...args) {
|
|
582
|
+
const listeners = this.eventListeners.get(eventName);
|
|
583
|
+
if (listeners) {
|
|
584
|
+
listeners.forEach((callback) => {
|
|
585
|
+
try {
|
|
586
|
+
callback(...args);
|
|
587
|
+
} catch (error) {
|
|
588
|
+
console.error(`Error in event listener for ${eventName}:`, error);
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
generateUUID() {
|
|
594
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
|
595
|
+
const r = Math.random() * 16 | 0, v = c === "x" ? r : r & 3 | 8;
|
|
596
|
+
return v.toString(16);
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
sendConnectRequest() {
|
|
600
|
+
var _a;
|
|
601
|
+
const params = {
|
|
602
|
+
uid: this.auth.uid,
|
|
603
|
+
token: this.auth.token,
|
|
604
|
+
deviceId: this.auth.deviceId,
|
|
605
|
+
deviceFlag: (_a = this.auth.deviceFlag) !== null && _a !== void 0 ? _a : DeviceFlag.Web,
|
|
606
|
+
// Default to WEB
|
|
607
|
+
clientTimestamp: Date.now()
|
|
608
|
+
// Add version, clientKey if needed
|
|
609
|
+
};
|
|
610
|
+
this.sendRequest("connect", params, 5e3).then((result) => {
|
|
611
|
+
console.log("Authentication successful:", result);
|
|
612
|
+
this.isConnected = true;
|
|
613
|
+
this.reconnectAttempts = 0;
|
|
614
|
+
this.isReconnecting = false;
|
|
615
|
+
this.manualDisconnect = false;
|
|
616
|
+
this.startPing();
|
|
617
|
+
this.emit(Event.Connect, result);
|
|
618
|
+
if (this.connectionPromise) {
|
|
619
|
+
this.connectionPromise.resolve();
|
|
620
|
+
this.connectionPromise = null;
|
|
621
|
+
}
|
|
622
|
+
}).catch((error) => {
|
|
623
|
+
console.error("Authentication failed:", error);
|
|
624
|
+
this.emit(Event.Error, new Error(`Authentication failed: ${error.message || JSON.stringify(error)}`));
|
|
625
|
+
if (this.connectionPromise) {
|
|
626
|
+
this.connectionPromise.reject(error);
|
|
627
|
+
this.connectionPromise = null;
|
|
628
|
+
}
|
|
629
|
+
this.handleDisconnect(false, "Authentication failed");
|
|
630
|
+
this.cleanupConnection();
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
sendRequest(method, params, timeoutMs = 15e3) {
|
|
634
|
+
return new Promise((resolve, reject) => {
|
|
635
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) {
|
|
636
|
+
return reject(new Error("WebSocket is not open."));
|
|
637
|
+
}
|
|
638
|
+
const requestId = this.generateUUID();
|
|
639
|
+
const request = {
|
|
640
|
+
method,
|
|
641
|
+
params,
|
|
642
|
+
id: requestId
|
|
643
|
+
};
|
|
644
|
+
const timeoutTimer = setTimeout(() => {
|
|
645
|
+
this.pendingRequests.delete(requestId);
|
|
646
|
+
reject(new Error(`Request timeout for method ${method} (id: ${requestId})`));
|
|
647
|
+
}, timeoutMs);
|
|
648
|
+
this.pendingRequests.set(requestId, { resolve, reject, timeoutTimer });
|
|
649
|
+
try {
|
|
650
|
+
console.debug(`--> Sending request (id: ${requestId}):`, JSON.stringify(request));
|
|
651
|
+
this.ws.send(JSON.stringify(request));
|
|
652
|
+
} catch (error) {
|
|
653
|
+
clearTimeout(timeoutTimer);
|
|
654
|
+
this.pendingRequests.delete(requestId);
|
|
655
|
+
console.error(`Error sending request (id: ${requestId}):`, error);
|
|
656
|
+
reject(error);
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
sendNotification(method, params) {
|
|
661
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) {
|
|
662
|
+
console.error("Cannot send notification, WebSocket is not open.");
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const notification = {
|
|
666
|
+
method,
|
|
667
|
+
params
|
|
668
|
+
};
|
|
669
|
+
console.debug(`--> Sending notification:`, JSON.stringify(notification));
|
|
670
|
+
try {
|
|
671
|
+
this.ws.send(JSON.stringify(notification));
|
|
672
|
+
} catch (error) {
|
|
673
|
+
console.error(`Error sending notification (${method}):`, error);
|
|
674
|
+
this.emit(Event.Error, new Error(`Failed to send notification ${method}: ${error}`));
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
handleMessage(data) {
|
|
678
|
+
console.debug("<-- Received raw:", data);
|
|
679
|
+
let message;
|
|
680
|
+
try {
|
|
681
|
+
message = JSON.parse(data.toString());
|
|
682
|
+
} catch (error) {
|
|
683
|
+
console.error("Failed to parse incoming message:", error, data);
|
|
684
|
+
this.emit(Event.Error, new Error(`Failed to parse message: ${error}`));
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
if ("id" in message) {
|
|
688
|
+
this.handleResponse(message);
|
|
689
|
+
} else if ("method" in message) {
|
|
690
|
+
this.handleNotification(message);
|
|
691
|
+
} else {
|
|
692
|
+
console.warn("Received unknown message format:", message);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
handleResponse(response) {
|
|
696
|
+
console.debug(`<-- Handling response (id: ${response.id}):`, response);
|
|
697
|
+
const pending = this.pendingRequests.get(response.id);
|
|
698
|
+
if (pending) {
|
|
699
|
+
clearTimeout(pending.timeoutTimer);
|
|
700
|
+
this.pendingRequests.delete(response.id);
|
|
701
|
+
if (response.error) {
|
|
702
|
+
pending.reject(response.error);
|
|
703
|
+
} else {
|
|
704
|
+
pending.resolve(response.result);
|
|
705
|
+
}
|
|
706
|
+
} else {
|
|
707
|
+
console.warn(`Received response for unknown request ID: ${response.id}`);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
handleNotification(notification) {
|
|
711
|
+
var _a, _b;
|
|
712
|
+
console.debug(`<-- Handling notification (${notification.method}):`, notification.params);
|
|
713
|
+
switch (notification.method) {
|
|
714
|
+
case "recv":
|
|
715
|
+
const messageData = notification.params;
|
|
716
|
+
if (typeof messageData.payload === "string") {
|
|
717
|
+
try {
|
|
718
|
+
messageData.payload = JSON.parse(this._decodeBase64ToStr(messageData.payload));
|
|
719
|
+
} catch (e) {
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
this.emit(Event.Message, messageData);
|
|
723
|
+
this.sendRecvAck(messageData.header, messageData.messageId, messageData.messageSeq);
|
|
724
|
+
break;
|
|
725
|
+
case "pong":
|
|
726
|
+
this.handlePong();
|
|
727
|
+
break;
|
|
728
|
+
case "disconnect":
|
|
729
|
+
console.warn("Server initiated disconnect:", notification.params);
|
|
730
|
+
this.emit(Event.Disconnect, notification.params);
|
|
731
|
+
this.handleDisconnect(false, `Server disconnected: ${((_a = notification.params) === null || _a === void 0 ? void 0 : _a.reason) || ((_b = notification.params) === null || _b === void 0 ? void 0 : _b.reasonCode)}`);
|
|
732
|
+
break;
|
|
733
|
+
case "event":
|
|
734
|
+
this.handleEventNotification(notification.params);
|
|
735
|
+
break;
|
|
736
|
+
// Handle other notifications if needed
|
|
737
|
+
default:
|
|
738
|
+
console.warn(`Received unhandled notification method: ${notification.method}`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Encode payload object to base64 string.
|
|
743
|
+
* WuKongIM server expects payload as []byte (base64 in JSON).
|
|
744
|
+
*/
|
|
745
|
+
_encodePayloadToBase64(payload) {
|
|
746
|
+
const json = JSON.stringify(payload);
|
|
747
|
+
if (typeof TextEncoder !== "undefined" && typeof btoa !== "undefined") {
|
|
748
|
+
const bytes = new TextEncoder().encode(json);
|
|
749
|
+
let binary = "";
|
|
750
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
751
|
+
binary += String.fromCharCode(bytes[i]);
|
|
752
|
+
}
|
|
753
|
+
return btoa(binary);
|
|
754
|
+
}
|
|
755
|
+
if (typeof Buffer !== "undefined") {
|
|
756
|
+
return Buffer.from(json, "utf-8").toString("base64");
|
|
757
|
+
}
|
|
758
|
+
if (typeof wx !== "undefined" && (wx === null || wx === void 0 ? void 0 : wx.arrayBufferToBase64)) {
|
|
759
|
+
const encoder = new TextEncoder();
|
|
760
|
+
const bytes = encoder.encode(json);
|
|
761
|
+
return wx.arrayBufferToBase64(bytes.buffer);
|
|
762
|
+
}
|
|
763
|
+
throw new Error("No base64 encoding method available");
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Decode base64 string to UTF-8 string.
|
|
767
|
+
*/
|
|
768
|
+
_decodeBase64ToStr(base64Str) {
|
|
769
|
+
if (typeof atob !== "undefined" && typeof TextDecoder !== "undefined") {
|
|
770
|
+
const binary = atob(base64Str);
|
|
771
|
+
const bytes = new Uint8Array(binary.length);
|
|
772
|
+
for (let i = 0; i < binary.length; i++) {
|
|
773
|
+
bytes[i] = binary.charCodeAt(i);
|
|
774
|
+
}
|
|
775
|
+
return new TextDecoder().decode(bytes);
|
|
776
|
+
}
|
|
777
|
+
if (typeof Buffer !== "undefined") {
|
|
778
|
+
return Buffer.from(base64Str, "base64").toString("utf-8");
|
|
779
|
+
}
|
|
780
|
+
if (typeof wx !== "undefined" && (wx === null || wx === void 0 ? void 0 : wx.base64ToArrayBuffer)) {
|
|
781
|
+
const buf = wx.base64ToArrayBuffer(base64Str);
|
|
782
|
+
return new TextDecoder().decode(new Uint8Array(buf));
|
|
783
|
+
}
|
|
784
|
+
throw new Error("No base64 decoding method available");
|
|
785
|
+
}
|
|
786
|
+
sendRecvAck(header, messageId, messageSeq) {
|
|
787
|
+
const params = { header, messageId, messageSeq };
|
|
788
|
+
this.sendNotification("recvack", params);
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Handles incoming event notifications from the server
|
|
792
|
+
* @param params Event notification parameters
|
|
793
|
+
*/
|
|
794
|
+
handleEventNotification(params) {
|
|
795
|
+
try {
|
|
796
|
+
const eventData = {
|
|
797
|
+
header: params.header,
|
|
798
|
+
id: params.id,
|
|
799
|
+
type: params.type,
|
|
800
|
+
timestamp: params.timestamp,
|
|
801
|
+
data: params.data
|
|
802
|
+
};
|
|
803
|
+
if (!eventData.id || !eventData.type) {
|
|
804
|
+
console.error("Invalid event notification: missing required fields", params);
|
|
805
|
+
this.emit(Event.Error, new Error("Invalid event notification: missing required fields"));
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
if (typeof eventData.data === "string") {
|
|
809
|
+
try {
|
|
810
|
+
eventData.data = JSON.parse(eventData.data);
|
|
811
|
+
} catch (e) {
|
|
812
|
+
console.debug("Event data is not JSON, keeping as string");
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
console.log(`Event notification received: type=${eventData.type}, id=${eventData.id}`);
|
|
816
|
+
this.emit(Event.CustomEvent, eventData);
|
|
817
|
+
} catch (error) {
|
|
818
|
+
console.error("Error handling event notification:", error);
|
|
819
|
+
this.emit(Event.Error, new Error(`Failed to handle event notification: ${error}`));
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
startPing() {
|
|
823
|
+
this.stopPing();
|
|
824
|
+
this.pingInterval = setInterval(() => {
|
|
825
|
+
if (this.ws && this.ws.readyState === WS_OPEN) {
|
|
826
|
+
this.sendRequest("ping", {}, this.PONG_TIMEOUT_MS).then(this.handlePong.bind(this)).catch((err) => {
|
|
827
|
+
console.error("Ping failed or timed out:", err);
|
|
828
|
+
this.emit(Event.Error, new Error(`Ping timeout: ${(err === null || err === void 0 ? void 0 : err.message) || err}`));
|
|
829
|
+
if (!this.manualDisconnect) {
|
|
830
|
+
this.handleDisconnect(false, "Ping timeout");
|
|
831
|
+
this.tryReconnect();
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
} else {
|
|
835
|
+
this.stopPing();
|
|
836
|
+
}
|
|
837
|
+
}, this.PING_INTERVAL_MS);
|
|
838
|
+
console.log(`Ping interval started (${this.PING_INTERVAL_MS}ms).`);
|
|
839
|
+
}
|
|
840
|
+
stopPing() {
|
|
841
|
+
if (this.pingInterval) {
|
|
842
|
+
clearInterval(this.pingInterval);
|
|
843
|
+
this.pingInterval = null;
|
|
844
|
+
console.log("Ping interval stopped.");
|
|
845
|
+
}
|
|
846
|
+
if (this.pingTimeout) {
|
|
847
|
+
clearTimeout(this.pingTimeout);
|
|
848
|
+
this.pingTimeout = null;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
handlePong() {
|
|
852
|
+
}
|
|
853
|
+
handleDisconnect(graceful, reason) {
|
|
854
|
+
console.log(`Handling disconnect. Graceful: ${graceful}, Reason: ${reason}`);
|
|
855
|
+
if (this.ws) {
|
|
856
|
+
this.stopPing();
|
|
857
|
+
if (graceful && this.ws.readyState === WS_OPEN) {
|
|
858
|
+
this.ws.close(1e3, "Client disconnected");
|
|
859
|
+
} else if (this.ws.readyState === WS_CONNECTING || this.ws.readyState === WS_OPEN) {
|
|
860
|
+
this.ws.close(3001, reason.substring(0, 123));
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
this.cleanupConnection();
|
|
864
|
+
}
|
|
865
|
+
cleanupConnection() {
|
|
866
|
+
console.log("Cleaning up connection resources.");
|
|
867
|
+
this.isConnected = false;
|
|
868
|
+
this.stopPing();
|
|
869
|
+
this.pendingRequests.forEach((pending) => {
|
|
870
|
+
clearTimeout(pending.timeoutTimer);
|
|
871
|
+
pending.reject(new Error("Connection closed"));
|
|
872
|
+
});
|
|
873
|
+
this.pendingRequests.clear();
|
|
874
|
+
if (this.connectionPromise) {
|
|
875
|
+
if (!this.isReconnecting && !this.isConnected) {
|
|
876
|
+
this.connectionPromise.reject(new Error("Connection closed during operation"));
|
|
877
|
+
}
|
|
878
|
+
this.connectionPromise = null;
|
|
879
|
+
}
|
|
880
|
+
if (this.ws) {
|
|
881
|
+
this.ws.onopen = null;
|
|
882
|
+
this.ws.onmessage = null;
|
|
883
|
+
this.ws.onerror = null;
|
|
884
|
+
this.ws.onclose = null;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
// --- Reconnection Methods ---
|
|
888
|
+
setupBeforeUnloadHandler() {
|
|
889
|
+
if (typeof window !== "undefined") {
|
|
890
|
+
this.beforeUnloadHandler = () => {
|
|
891
|
+
console.log("Page unloading, closing WebSocket connection...");
|
|
892
|
+
this.manualDisconnect = true;
|
|
893
|
+
this.isReconnecting = false;
|
|
894
|
+
if (this.ws && this.ws.readyState === WS_OPEN) {
|
|
895
|
+
this.ws.close(1e3, "Page unloaded");
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
window.addEventListener("beforeunload", this.beforeUnloadHandler);
|
|
899
|
+
window.addEventListener("pagehide", this.beforeUnloadHandler);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
cleanupBeforeUnloadHandler() {
|
|
903
|
+
if (typeof window !== "undefined" && this.beforeUnloadHandler) {
|
|
904
|
+
window.removeEventListener("beforeunload", this.beforeUnloadHandler);
|
|
905
|
+
window.removeEventListener("pagehide", this.beforeUnloadHandler);
|
|
906
|
+
this.beforeUnloadHandler = null;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
tryReconnect() {
|
|
910
|
+
if (this.isReconnecting || this.manualDisconnect) {
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
this.isReconnecting = true;
|
|
914
|
+
this.scheduleReconnect();
|
|
915
|
+
}
|
|
916
|
+
scheduleReconnect() {
|
|
917
|
+
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
918
|
+
console.error("Max reconnect attempts reached. Giving up.");
|
|
919
|
+
this.isReconnecting = false;
|
|
920
|
+
this.reconnectAttempts = 0;
|
|
921
|
+
this.emit(Event.Error, new Error("Reconnection failed."));
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
const delay = this.initialReconnectDelay * Math.pow(2, this.reconnectAttempts);
|
|
925
|
+
this.reconnectAttempts++;
|
|
926
|
+
console.log(`Will attempt to reconnect in ${delay / 1e3}s (Attempt ${this.reconnectAttempts}).`);
|
|
927
|
+
this.emit(Event.Reconnecting, { attempt: this.reconnectAttempts, delay });
|
|
928
|
+
setTimeout(() => {
|
|
929
|
+
if (!this.isReconnecting) {
|
|
930
|
+
console.log("Reconnection aborted.");
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
this.connect().catch(() => {
|
|
934
|
+
if (this.isReconnecting) {
|
|
935
|
+
this.scheduleReconnect();
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
}, delay);
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
exports2.WKIM = WKIM;
|
|
942
|
+
WKIM.globalInstance = null;
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
// miniprogram_dist/core/chatStore.js
|
|
947
|
+
var IMService = require("../services/wukongim");
|
|
948
|
+
var visitorService = require("../services/visitor");
|
|
949
|
+
var historyService = require("../services/messageHistory");
|
|
950
|
+
var chatService = require("../services/chat");
|
|
951
|
+
var uploadService = require("../services/upload");
|
|
952
|
+
var systemInfoAdapter = require("../adapters/systemInfo");
|
|
953
|
+
var types = require("./types");
|
|
954
|
+
var platformStore = require("./platformStore");
|
|
955
|
+
var uidUtil = require("../utils/uid");
|
|
956
|
+
var jsonRenderUtils = require("../utils/jsonRender");
|
|
957
|
+
var offMsg = null;
|
|
958
|
+
var offStatus = null;
|
|
959
|
+
var offCustom = null;
|
|
960
|
+
var streamTimer = null;
|
|
961
|
+
var STREAM_TIMEOUT_MS = 6e4;
|
|
962
|
+
var activeParsers = {};
|
|
28
963
|
function ChatStore() {
|
|
29
964
|
this._state = {
|
|
30
965
|
messages: [],
|
|
@@ -37,329 +972,314 @@ function ChatStore() {
|
|
|
37
972
|
historyError: null,
|
|
38
973
|
earliestSeq: null,
|
|
39
974
|
// identity
|
|
40
|
-
apiBase:
|
|
41
|
-
platformApiKey:
|
|
42
|
-
myUid:
|
|
43
|
-
channelId:
|
|
975
|
+
apiBase: "",
|
|
976
|
+
platformApiKey: "",
|
|
977
|
+
myUid: "",
|
|
978
|
+
channelId: "",
|
|
44
979
|
channelType: 251,
|
|
45
980
|
// streaming
|
|
46
981
|
isStreaming: false,
|
|
47
982
|
streamCanceling: false,
|
|
48
|
-
streamingClientMsgNo:
|
|
49
|
-
}
|
|
50
|
-
this._listeners = []
|
|
51
|
-
this._throttleTimer = null
|
|
52
|
-
this._pendingState = null
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
ChatStore.prototype.getState = function () {
|
|
56
|
-
return this._state
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
ChatStore.prototype._setState = function (partial) {
|
|
60
|
-
Object.assign(this._state, partial)
|
|
61
|
-
var state = this._state
|
|
62
|
-
this._listeners.forEach(function (fn) { try { fn(state) } catch (e) {} })
|
|
983
|
+
streamingClientMsgNo: ""
|
|
984
|
+
};
|
|
985
|
+
this._listeners = [];
|
|
986
|
+
this._throttleTimer = null;
|
|
987
|
+
this._pendingState = null;
|
|
63
988
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
989
|
+
ChatStore.prototype.getState = function() {
|
|
990
|
+
return this._state;
|
|
991
|
+
};
|
|
992
|
+
ChatStore.prototype._setState = function(partial) {
|
|
993
|
+
Object.assign(this._state, partial);
|
|
994
|
+
var state = this._state;
|
|
995
|
+
this._listeners.forEach(function(fn) {
|
|
996
|
+
try {
|
|
997
|
+
fn(state);
|
|
998
|
+
} catch (e) {
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
};
|
|
1002
|
+
ChatStore.prototype._setStateThrottled = function(partial) {
|
|
1003
|
+
var self = this;
|
|
1004
|
+
Object.assign(this._state, partial);
|
|
75
1005
|
if (!this._throttleTimer) {
|
|
76
|
-
this._throttleTimer = setTimeout(function
|
|
77
|
-
self._throttleTimer = null
|
|
78
|
-
var state = self._state
|
|
79
|
-
self._listeners.forEach(function
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
var self = this
|
|
87
|
-
return function () {
|
|
88
|
-
var idx = self._listeners.indexOf(fn)
|
|
89
|
-
if (idx >= 0) self._listeners.splice(idx, 1)
|
|
1006
|
+
this._throttleTimer = setTimeout(function() {
|
|
1007
|
+
self._throttleTimer = null;
|
|
1008
|
+
var state = self._state;
|
|
1009
|
+
self._listeners.forEach(function(fn) {
|
|
1010
|
+
try {
|
|
1011
|
+
fn(state);
|
|
1012
|
+
} catch (e) {
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
}, 50);
|
|
90
1016
|
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
var
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
var
|
|
108
|
-
var
|
|
1017
|
+
};
|
|
1018
|
+
ChatStore.prototype.subscribe = function(fn) {
|
|
1019
|
+
this._listeners.push(fn);
|
|
1020
|
+
var self = this;
|
|
1021
|
+
return function() {
|
|
1022
|
+
var idx = self._listeners.indexOf(fn);
|
|
1023
|
+
if (idx >= 0) self._listeners.splice(idx, 1);
|
|
1024
|
+
};
|
|
1025
|
+
};
|
|
1026
|
+
ChatStore.prototype.initIM = function(cfg) {
|
|
1027
|
+
if (!cfg || !cfg.apiBase || !cfg.platformApiKey) return Promise.resolve();
|
|
1028
|
+
var self = this;
|
|
1029
|
+
var st = this._state;
|
|
1030
|
+
if (st.initializing || IMService.isReady()) return Promise.resolve();
|
|
1031
|
+
this._setState({ initializing: true, error: null, apiBase: cfg.apiBase, platformApiKey: cfg.platformApiKey });
|
|
1032
|
+
var apiBase = cfg.apiBase;
|
|
1033
|
+
var platformApiKey = cfg.platformApiKey;
|
|
1034
|
+
var cached = visitorService.loadCachedVisitor(apiBase, platformApiKey);
|
|
1035
|
+
var visitorPromise;
|
|
109
1036
|
if (cached) {
|
|
110
|
-
visitorPromise = Promise.resolve(cached)
|
|
1037
|
+
visitorPromise = Promise.resolve(cached);
|
|
111
1038
|
} else {
|
|
112
|
-
var sysInfo = systemInfoAdapter.collectSystemInfo()
|
|
1039
|
+
var sysInfo = systemInfoAdapter.collectSystemInfo();
|
|
113
1040
|
visitorPromise = visitorService.registerVisitor({
|
|
114
|
-
apiBase
|
|
115
|
-
platformApiKey
|
|
1041
|
+
apiBase,
|
|
1042
|
+
platformApiKey,
|
|
116
1043
|
extra: sysInfo ? { system_info: sysInfo } : {}
|
|
117
|
-
}).then(function
|
|
118
|
-
visitorService.saveCachedVisitor(apiBase, platformApiKey, res)
|
|
119
|
-
return visitorService.loadCachedVisitor(apiBase, platformApiKey)
|
|
120
|
-
})
|
|
1044
|
+
}).then(function(res) {
|
|
1045
|
+
visitorService.saveCachedVisitor(apiBase, platformApiKey, res);
|
|
1046
|
+
return visitorService.loadCachedVisitor(apiBase, platformApiKey);
|
|
1047
|
+
});
|
|
121
1048
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
var
|
|
125
|
-
var
|
|
126
|
-
var
|
|
127
|
-
var token = visitor.im_token
|
|
128
|
-
|
|
1049
|
+
return visitorPromise.then(function(visitor) {
|
|
1050
|
+
var uid = String(visitor.visitor_id || "");
|
|
1051
|
+
var uidForIM = uid.endsWith("-vtr") ? uid : uid + "-vtr";
|
|
1052
|
+
var target = visitor.channel_id;
|
|
1053
|
+
var token = visitor.im_token;
|
|
129
1054
|
self._setState({
|
|
130
1055
|
myUid: uidForIM,
|
|
131
1056
|
channelId: target,
|
|
132
1057
|
channelType: visitor.channel_type || 251
|
|
133
|
-
})
|
|
134
|
-
|
|
1058
|
+
});
|
|
135
1059
|
if (!token) {
|
|
136
|
-
throw new Error(
|
|
1060
|
+
throw new Error("[Chat] Missing im_token from visitor registration");
|
|
137
1061
|
}
|
|
138
|
-
|
|
139
1062
|
return IMService.init({
|
|
140
|
-
apiBase
|
|
1063
|
+
apiBase,
|
|
141
1064
|
uid: uidForIM,
|
|
142
|
-
token
|
|
143
|
-
target
|
|
144
|
-
channelType:
|
|
145
|
-
})
|
|
146
|
-
}).then(function
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
self._setState({
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (offMsg) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
1065
|
+
token,
|
|
1066
|
+
target,
|
|
1067
|
+
channelType: "group"
|
|
1068
|
+
});
|
|
1069
|
+
}).then(function() {
|
|
1070
|
+
self._bindIMEvents();
|
|
1071
|
+
return IMService.connect();
|
|
1072
|
+
}).then(function() {
|
|
1073
|
+
return self.loadInitialHistory(20);
|
|
1074
|
+
}).catch(function(e) {
|
|
1075
|
+
var errMsg = e && e.message ? e.message : String(e);
|
|
1076
|
+
console.error("[Chat] IM initialization failed:", errMsg);
|
|
1077
|
+
self._setState({ error: errMsg, online: false });
|
|
1078
|
+
}).then(function() {
|
|
1079
|
+
self._setState({ initializing: false });
|
|
1080
|
+
});
|
|
1081
|
+
};
|
|
1082
|
+
ChatStore.prototype._bindIMEvents = function() {
|
|
1083
|
+
var self = this;
|
|
1084
|
+
var uidForIM = this._state.myUid;
|
|
1085
|
+
if (offStatus) {
|
|
1086
|
+
try {
|
|
1087
|
+
offStatus();
|
|
1088
|
+
} catch (e) {
|
|
1089
|
+
}
|
|
1090
|
+
;
|
|
1091
|
+
offStatus = null;
|
|
1092
|
+
}
|
|
1093
|
+
offStatus = IMService.onStatus(function(s) {
|
|
1094
|
+
self._setState({ online: s === "connected" });
|
|
1095
|
+
});
|
|
1096
|
+
if (offMsg) {
|
|
1097
|
+
try {
|
|
1098
|
+
offMsg();
|
|
1099
|
+
} catch (e) {
|
|
1100
|
+
}
|
|
1101
|
+
;
|
|
1102
|
+
offMsg = null;
|
|
1103
|
+
}
|
|
1104
|
+
offMsg = IMService.onMessage(function(m) {
|
|
1105
|
+
if (!m.fromUid || m.fromUid === uidForIM) return;
|
|
177
1106
|
var chat = {
|
|
178
1107
|
id: String(m.messageId),
|
|
179
|
-
role:
|
|
1108
|
+
role: "agent",
|
|
180
1109
|
payload: types.toPayloadFromAny(m && m.payload),
|
|
181
|
-
time: new Date(m.timestamp *
|
|
182
|
-
messageSeq: typeof m.messageSeq ===
|
|
1110
|
+
time: new Date(m.timestamp * 1e3),
|
|
1111
|
+
messageSeq: typeof m.messageSeq === "number" ? m.messageSeq : void 0,
|
|
183
1112
|
clientMsgNo: m.clientMsgNo,
|
|
184
1113
|
fromUid: m.fromUid,
|
|
185
1114
|
channelId: m.channelId,
|
|
186
1115
|
channelType: m.channelType
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
var currentMessages = self._state.messages
|
|
1116
|
+
};
|
|
1117
|
+
var currentMessages = self._state.messages;
|
|
190
1118
|
for (var i = 0; i < currentMessages.length; i++) {
|
|
191
|
-
if (currentMessages[i].id === chat.id) return
|
|
1119
|
+
if (currentMessages[i].id === chat.id) return;
|
|
192
1120
|
}
|
|
193
|
-
|
|
194
|
-
// Merge into streaming placeholder if exists
|
|
195
1121
|
if (chat.clientMsgNo) {
|
|
196
|
-
var idx = -1
|
|
1122
|
+
var idx = -1;
|
|
197
1123
|
for (var j = 0; j < currentMessages.length; j++) {
|
|
198
1124
|
if (currentMessages[j].clientMsgNo && currentMessages[j].clientMsgNo === chat.clientMsgNo) {
|
|
199
|
-
idx = j
|
|
200
|
-
break
|
|
1125
|
+
idx = j;
|
|
1126
|
+
break;
|
|
201
1127
|
}
|
|
202
1128
|
}
|
|
203
1129
|
if (idx >= 0) {
|
|
204
|
-
var next = currentMessages.slice()
|
|
205
|
-
next[idx] = Object.assign({}, currentMessages[idx], chat, { streamData:
|
|
206
|
-
self._setState({ messages: next })
|
|
207
|
-
return
|
|
1130
|
+
var next = currentMessages.slice();
|
|
1131
|
+
next[idx] = Object.assign({}, currentMessages[idx], chat, { streamData: void 0 });
|
|
1132
|
+
self._setState({ messages: next });
|
|
1133
|
+
return;
|
|
208
1134
|
}
|
|
209
1135
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
// Custom stream events
|
|
215
|
-
if (offCustom) { try { offCustom() } catch (e) {} ; offCustom = null }
|
|
216
|
-
offCustom = IMService.onCustom(function (ev) {
|
|
1136
|
+
self._setState({ messages: currentMessages.concat([chat]) });
|
|
1137
|
+
});
|
|
1138
|
+
if (offCustom) {
|
|
217
1139
|
try {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
1140
|
+
offCustom();
|
|
1141
|
+
} catch (e) {
|
|
1142
|
+
}
|
|
1143
|
+
;
|
|
1144
|
+
offCustom = null;
|
|
1145
|
+
}
|
|
1146
|
+
offCustom = IMService.onCustom(function(ev) {
|
|
1147
|
+
try {
|
|
1148
|
+
if (!ev) return;
|
|
1149
|
+
var customEvent = ev;
|
|
1150
|
+
var eventData = null;
|
|
1151
|
+
if (customEvent.dataJson && typeof customEvent.dataJson === "object") {
|
|
1152
|
+
eventData = customEvent.dataJson;
|
|
1153
|
+
} else if (typeof customEvent.data === "string") {
|
|
1154
|
+
try {
|
|
1155
|
+
eventData = JSON.parse(customEvent.data);
|
|
1156
|
+
} catch (e) {
|
|
1157
|
+
}
|
|
1158
|
+
} else if (customEvent.data && typeof customEvent.data === "object") {
|
|
1159
|
+
eventData = customEvent.data;
|
|
229
1160
|
}
|
|
230
|
-
|
|
231
|
-
var
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (!clientMsgNo) return
|
|
237
|
-
var payload = eventData && eventData.payload
|
|
238
|
-
var delta = payload && payload.delta
|
|
1161
|
+
var newEventType = eventData && eventData.event_type || customEvent.type || "";
|
|
1162
|
+
var clientMsgNo = eventData && eventData.client_msg_no ? String(eventData.client_msg_no) : customEvent.id ? String(customEvent.id) : "";
|
|
1163
|
+
if (newEventType === "stream.delta") {
|
|
1164
|
+
if (!clientMsgNo) return;
|
|
1165
|
+
var payload = eventData && eventData.payload;
|
|
1166
|
+
var delta = payload && payload.delta;
|
|
239
1167
|
if (delta) {
|
|
240
|
-
var parser = activeParsers[clientMsgNo]
|
|
1168
|
+
var parser = activeParsers[clientMsgNo];
|
|
241
1169
|
if (!parser) {
|
|
242
1170
|
parser = jsonRenderUtils.createMixedStreamParser({
|
|
243
|
-
onText: function
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
1171
|
+
onText: function(t) {
|
|
1172
|
+
self.appendMixedPart(clientMsgNo, { type: "text", text: t + "\n" });
|
|
1173
|
+
},
|
|
1174
|
+
onPatch: function(p) {
|
|
1175
|
+
self.appendMixedPart(clientMsgNo, { type: "data-spec", data: { type: "patch", patch: p } });
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1178
|
+
activeParsers[clientMsgNo] = parser;
|
|
247
1179
|
}
|
|
248
|
-
parser.push(String(delta).replace(/([^\n])```spec/g,
|
|
1180
|
+
parser.push(String(delta).replace(/([^\n])```spec/g, "$1\n```spec"));
|
|
249
1181
|
}
|
|
250
|
-
return
|
|
1182
|
+
return;
|
|
251
1183
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
1184
|
+
if (newEventType === "stream.close") {
|
|
1185
|
+
if (!clientMsgNo) return;
|
|
1186
|
+
var closeParser = activeParsers[clientMsgNo];
|
|
1187
|
+
if (closeParser) {
|
|
1188
|
+
closeParser.flush();
|
|
1189
|
+
delete activeParsers[clientMsgNo];
|
|
1190
|
+
}
|
|
1191
|
+
var errMsg = eventData && eventData.payload && eventData.payload.end_reason > 0 ? "\u6D41\u5F02\u5E38\u7ED3\u675F" : void 0;
|
|
1192
|
+
self.finalizeStreamMessage(clientMsgNo, errMsg);
|
|
1193
|
+
self.markStreamingEnd();
|
|
1194
|
+
return;
|
|
261
1195
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
1196
|
+
if (newEventType === "stream.error") {
|
|
1197
|
+
if (!clientMsgNo) return;
|
|
1198
|
+
var errParser = activeParsers[clientMsgNo];
|
|
1199
|
+
if (errParser) {
|
|
1200
|
+
errParser.flush();
|
|
1201
|
+
delete activeParsers[clientMsgNo];
|
|
1202
|
+
}
|
|
1203
|
+
var errMessage = eventData && eventData.payload && eventData.payload.error || "\u672A\u77E5\u9519\u8BEF";
|
|
1204
|
+
self.finalizeStreamMessage(clientMsgNo, errMessage);
|
|
1205
|
+
self.markStreamingEnd();
|
|
1206
|
+
return;
|
|
271
1207
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
1208
|
+
if (newEventType === "stream.cancel") {
|
|
1209
|
+
if (!clientMsgNo) return;
|
|
1210
|
+
var cancelParser = activeParsers[clientMsgNo];
|
|
1211
|
+
if (cancelParser) {
|
|
1212
|
+
cancelParser.flush();
|
|
1213
|
+
delete activeParsers[clientMsgNo];
|
|
1214
|
+
}
|
|
1215
|
+
self.finalizeStreamMessage(clientMsgNo);
|
|
1216
|
+
self.markStreamingEnd();
|
|
1217
|
+
return;
|
|
280
1218
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
return
|
|
1219
|
+
if (newEventType === "stream.finish") {
|
|
1220
|
+
return;
|
|
284
1221
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if (startId) self.markStreamingStart(startId)
|
|
290
|
-
return
|
|
1222
|
+
if (customEvent.type === "___TextMessageStart") {
|
|
1223
|
+
var startId = customEvent.id ? String(customEvent.id) : "";
|
|
1224
|
+
if (startId) self.markStreamingStart(startId);
|
|
1225
|
+
return;
|
|
291
1226
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
return
|
|
1227
|
+
if (customEvent.type === "___TextMessageContent") {
|
|
1228
|
+
var contentId = customEvent.id ? String(customEvent.id) : "";
|
|
1229
|
+
if (!contentId) return;
|
|
1230
|
+
var chunk = typeof customEvent.data === "string" ? customEvent.data : customEvent.data != null ? String(customEvent.data) : "";
|
|
1231
|
+
if (chunk) self.appendStreamData(contentId, chunk);
|
|
1232
|
+
return;
|
|
299
1233
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
self.
|
|
306
|
-
|
|
307
|
-
return
|
|
1234
|
+
if (customEvent.type === "___TextMessageEnd") {
|
|
1235
|
+
var endId = customEvent.id ? String(customEvent.id) : "";
|
|
1236
|
+
if (!endId) return;
|
|
1237
|
+
var endError = customEvent.data ? String(customEvent.data) : void 0;
|
|
1238
|
+
self.finalizeStreamMessage(endId, endError);
|
|
1239
|
+
self.markStreamingEnd();
|
|
1240
|
+
return;
|
|
308
1241
|
}
|
|
309
1242
|
} catch (err) {
|
|
310
|
-
console.error(
|
|
1243
|
+
console.error("[Chat] Custom event handler error:", err);
|
|
311
1244
|
}
|
|
312
|
-
})
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
var
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
var self = this
|
|
322
|
-
var clientMsgNo = uidUtil.generateClientMsgNo('cmn')
|
|
323
|
-
var id = 'u-' + Date.now()
|
|
1245
|
+
});
|
|
1246
|
+
};
|
|
1247
|
+
ChatStore.prototype.sendMessage = function(text) {
|
|
1248
|
+
var v = (text || "").trim();
|
|
1249
|
+
if (!v) return Promise.resolve();
|
|
1250
|
+
var self = this;
|
|
1251
|
+
var clientMsgNo = uidUtil.generateClientMsgNo("cmn");
|
|
1252
|
+
var id = "u-" + Date.now();
|
|
324
1253
|
var msg = {
|
|
325
|
-
id
|
|
326
|
-
role:
|
|
1254
|
+
id,
|
|
1255
|
+
role: "user",
|
|
327
1256
|
payload: { type: 1, content: v },
|
|
328
|
-
time: new Date(),
|
|
329
|
-
status:
|
|
330
|
-
clientMsgNo
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
this._setState({ messages: this._state.messages.concat([msg]) })
|
|
335
|
-
|
|
336
|
-
var st = this._state
|
|
1257
|
+
time: /* @__PURE__ */ new Date(),
|
|
1258
|
+
status: "sending",
|
|
1259
|
+
clientMsgNo
|
|
1260
|
+
};
|
|
1261
|
+
this._setState({ messages: this._state.messages.concat([msg]) });
|
|
1262
|
+
var st = this._state;
|
|
337
1263
|
if (!st.apiBase || !st.platformApiKey || !st.myUid) {
|
|
338
|
-
this._updateMessage(id, { status:
|
|
339
|
-
return Promise.resolve()
|
|
1264
|
+
this._updateMessage(id, { status: void 0, errorMessage: "Not initialized" });
|
|
1265
|
+
return Promise.resolve();
|
|
340
1266
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
var cancelPromise = st.isStreaming ? this.cancelStreaming('auto_cancel_on_new_send') : Promise.resolve()
|
|
344
|
-
|
|
345
|
-
return cancelPromise.then(function () {
|
|
346
|
-
// Wait for IM ready
|
|
1267
|
+
var cancelPromise = st.isStreaming ? this.cancelStreaming("auto_cancel_on_new_send") : Promise.resolve();
|
|
1268
|
+
return cancelPromise.then(function() {
|
|
347
1269
|
if (!IMService.isReady()) {
|
|
348
|
-
return self._waitForIMReady(
|
|
1270
|
+
return self._waitForIMReady(1e4);
|
|
349
1271
|
}
|
|
350
|
-
}).then(function
|
|
1272
|
+
}).then(function() {
|
|
351
1273
|
if (!IMService.isReady()) {
|
|
352
|
-
throw new Error(
|
|
1274
|
+
throw new Error("IM service is not ready");
|
|
353
1275
|
}
|
|
354
|
-
return IMService.sendText(v, { clientMsgNo
|
|
355
|
-
}).then(function
|
|
356
|
-
var ReasonCode =
|
|
1276
|
+
return IMService.sendText(v, { clientMsgNo });
|
|
1277
|
+
}).then(function(result) {
|
|
1278
|
+
var ReasonCode = require_cjs().ReasonCode;
|
|
357
1279
|
if (result.reasonCode !== ReasonCode.Success) {
|
|
358
|
-
self._updateMessage(id, { status:
|
|
359
|
-
return
|
|
1280
|
+
self._updateMessage(id, { status: void 0, reasonCode: result.reasonCode });
|
|
1281
|
+
return;
|
|
360
1282
|
}
|
|
361
|
-
|
|
362
|
-
// Call chat completion API
|
|
363
1283
|
return chatService.sendChatCompletion({
|
|
364
1284
|
apiBase: self._state.apiBase,
|
|
365
1285
|
platformApiKey: self._state.platformApiKey,
|
|
@@ -367,99 +1287,87 @@ ChatStore.prototype.sendMessage = function (text) {
|
|
|
367
1287
|
fromUid: self._state.myUid,
|
|
368
1288
|
channelId: self._state.channelId,
|
|
369
1289
|
channelType: self._state.channelType
|
|
370
|
-
}).then(function
|
|
371
|
-
self._updateMessage(id, { status:
|
|
372
|
-
})
|
|
373
|
-
}).catch(function
|
|
374
|
-
console.error(
|
|
375
|
-
self.markStreamingEnd()
|
|
376
|
-
var ReasonCode =
|
|
377
|
-
self._updateMessage(id, { status:
|
|
378
|
-
self._setState({ error: e && e.message ? e.message : String(e) })
|
|
379
|
-
})
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
resolve()
|
|
1290
|
+
}).then(function() {
|
|
1291
|
+
self._updateMessage(id, { status: void 0, reasonCode: result.reasonCode });
|
|
1292
|
+
});
|
|
1293
|
+
}).catch(function(e) {
|
|
1294
|
+
console.error("[Chat] Send failed:", e);
|
|
1295
|
+
self.markStreamingEnd();
|
|
1296
|
+
var ReasonCode = require_cjs().ReasonCode;
|
|
1297
|
+
self._updateMessage(id, { status: void 0, reasonCode: ReasonCode.Unknown });
|
|
1298
|
+
self._setState({ error: e && e.message ? e.message : String(e) });
|
|
1299
|
+
});
|
|
1300
|
+
};
|
|
1301
|
+
ChatStore.prototype._waitForIMReady = function(timeoutMs) {
|
|
1302
|
+
var start = Date.now();
|
|
1303
|
+
return new Promise(function(resolve) {
|
|
1304
|
+
var check = function() {
|
|
1305
|
+
if (IMService.isReady() || Date.now() - start >= timeoutMs) {
|
|
1306
|
+
resolve();
|
|
388
1307
|
} else {
|
|
389
|
-
setTimeout(check, 120)
|
|
1308
|
+
setTimeout(check, 120);
|
|
390
1309
|
}
|
|
391
|
-
}
|
|
392
|
-
check()
|
|
393
|
-
})
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
})
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
ChatStore.prototype.uploadImage = function (tempFilePath) {
|
|
407
|
-
var self = this
|
|
408
|
-
var st = this._state
|
|
1310
|
+
};
|
|
1311
|
+
check();
|
|
1312
|
+
});
|
|
1313
|
+
};
|
|
1314
|
+
ChatStore.prototype._updateMessage = function(id, partial) {
|
|
1315
|
+
var messages = this._state.messages.map(function(m) {
|
|
1316
|
+
if (m.id === id) return Object.assign({}, m, partial);
|
|
1317
|
+
return m;
|
|
1318
|
+
});
|
|
1319
|
+
this._setState({ messages });
|
|
1320
|
+
};
|
|
1321
|
+
ChatStore.prototype.uploadImage = function(tempFilePath) {
|
|
1322
|
+
var self = this;
|
|
1323
|
+
var st = this._state;
|
|
409
1324
|
if (!st.apiBase || !st.channelId) {
|
|
410
|
-
this._setState({ error:
|
|
411
|
-
return Promise.resolve()
|
|
1325
|
+
this._setState({ error: "[Upload] Not initialized" });
|
|
1326
|
+
return Promise.resolve();
|
|
412
1327
|
}
|
|
413
|
-
|
|
414
|
-
var
|
|
415
|
-
var id = 'u-up-' + Date.now() + '-' + Math.random().toString(36).slice(2, 6)
|
|
1328
|
+
var clientMsgNo = uidUtil.generateClientMsgNo("um");
|
|
1329
|
+
var id = "u-up-" + Date.now() + "-" + Math.random().toString(36).slice(2, 6);
|
|
416
1330
|
var placeholder = {
|
|
417
|
-
id
|
|
418
|
-
role:
|
|
419
|
-
payload: { type: 1, content:
|
|
420
|
-
time: new Date(),
|
|
421
|
-
status:
|
|
1331
|
+
id,
|
|
1332
|
+
role: "user",
|
|
1333
|
+
payload: { type: 1, content: "\u56FE\u7247\u4E0A\u4F20\u4E2D\u2026" },
|
|
1334
|
+
time: /* @__PURE__ */ new Date(),
|
|
1335
|
+
status: "uploading",
|
|
422
1336
|
uploadProgress: 0,
|
|
423
|
-
clientMsgNo
|
|
424
|
-
}
|
|
425
|
-
this._setState({ messages: this._state.messages.concat([placeholder]) })
|
|
426
|
-
|
|
427
|
-
return uploadService.getImageInfo(tempFilePath).then(function (dims) {
|
|
1337
|
+
clientMsgNo
|
|
1338
|
+
};
|
|
1339
|
+
this._setState({ messages: this._state.messages.concat([placeholder]) });
|
|
1340
|
+
return uploadService.getImageInfo(tempFilePath).then(function(dims) {
|
|
428
1341
|
return uploadService.uploadChatFile({
|
|
429
1342
|
apiBase: st.apiBase,
|
|
430
1343
|
apiKey: st.platformApiKey,
|
|
431
1344
|
channelId: st.channelId,
|
|
432
1345
|
channelType: st.channelType,
|
|
433
1346
|
filePath: tempFilePath,
|
|
434
|
-
onProgress: function
|
|
435
|
-
self._updateMessage(id, { uploadProgress: p })
|
|
436
|
-
}
|
|
437
|
-
}).then(function
|
|
438
|
-
var w = dims ? Math.max(1, dims.width) : 1
|
|
439
|
-
var h = dims ? Math.max(1, dims.height) : 1
|
|
440
|
-
var payload = { type: 2, url: res.file_url, width: w, height: h }
|
|
441
|
-
self._updateMessage(id, { payload
|
|
442
|
-
return IMService.sendPayload(payload, { clientMsgNo
|
|
443
|
-
}).then(function
|
|
444
|
-
self._updateMessage(id, { status:
|
|
445
|
-
})
|
|
446
|
-
}).catch(function
|
|
447
|
-
console.error(
|
|
448
|
-
self._updateMessage(id, { status:
|
|
449
|
-
})
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
if (!st.channelId || !st.channelType) return Promise.resolve()
|
|
459
|
-
if (st.historyLoading) return Promise.resolve()
|
|
460
|
-
|
|
461
|
-
this._setState({ historyLoading: true, historyError: null })
|
|
462
|
-
|
|
1347
|
+
onProgress: function(p) {
|
|
1348
|
+
self._updateMessage(id, { uploadProgress: p });
|
|
1349
|
+
}
|
|
1350
|
+
}).then(function(res) {
|
|
1351
|
+
var w = dims ? Math.max(1, dims.width) : 1;
|
|
1352
|
+
var h = dims ? Math.max(1, dims.height) : 1;
|
|
1353
|
+
var payload = { type: 2, url: res.file_url, width: w, height: h };
|
|
1354
|
+
self._updateMessage(id, { payload, status: "sending", uploadProgress: void 0 });
|
|
1355
|
+
return IMService.sendPayload(payload, { clientMsgNo });
|
|
1356
|
+
}).then(function(result) {
|
|
1357
|
+
self._updateMessage(id, { status: void 0, reasonCode: result && result.reasonCode });
|
|
1358
|
+
});
|
|
1359
|
+
}).catch(function(err) {
|
|
1360
|
+
console.error("[Chat] Upload failed:", err);
|
|
1361
|
+
self._updateMessage(id, { status: void 0, uploadError: err && err.message ? err.message : "\u4E0A\u4F20\u5931\u8D25" });
|
|
1362
|
+
});
|
|
1363
|
+
};
|
|
1364
|
+
ChatStore.prototype.loadInitialHistory = function(limit) {
|
|
1365
|
+
limit = limit || 20;
|
|
1366
|
+
var self = this;
|
|
1367
|
+
var st = this._state;
|
|
1368
|
+
if (!st.channelId || !st.channelType) return Promise.resolve();
|
|
1369
|
+
if (st.historyLoading) return Promise.resolve();
|
|
1370
|
+
this._setState({ historyLoading: true, historyError: null });
|
|
463
1371
|
return historyService.syncVisitorMessages({
|
|
464
1372
|
apiBase: st.apiBase,
|
|
465
1373
|
platformApiKey: st.platformApiKey,
|
|
@@ -467,58 +1375,50 @@ ChatStore.prototype.loadInitialHistory = function (limit) {
|
|
|
467
1375
|
channelType: st.channelType,
|
|
468
1376
|
startSeq: 0,
|
|
469
1377
|
endSeq: 0,
|
|
470
|
-
limit
|
|
1378
|
+
limit,
|
|
471
1379
|
pullMode: 1
|
|
472
|
-
}).then(function
|
|
473
|
-
var myUid = self._state.myUid
|
|
474
|
-
var list = res.messages.slice().sort(function
|
|
475
|
-
return (a.message_seq || 0) - (b.message_seq || 0)
|
|
476
|
-
}).map(function
|
|
477
|
-
return types.mapHistoryToChatMessage(m, myUid)
|
|
478
|
-
})
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
})
|
|
492
|
-
|
|
493
|
-
var earliest = self._state.earliestSeq
|
|
494
|
-
mergedHead.forEach(function (m) {
|
|
1380
|
+
}).then(function(res) {
|
|
1381
|
+
var myUid = self._state.myUid;
|
|
1382
|
+
var list = res.messages.slice().sort(function(a, b) {
|
|
1383
|
+
return (a.message_seq || 0) - (b.message_seq || 0);
|
|
1384
|
+
}).map(function(m) {
|
|
1385
|
+
return types.mapHistoryToChatMessage(m, myUid);
|
|
1386
|
+
});
|
|
1387
|
+
var existingSeqs = {};
|
|
1388
|
+
var existingIds = {};
|
|
1389
|
+
self._state.messages.forEach(function(m) {
|
|
1390
|
+
if (typeof m.messageSeq === "number") existingSeqs[m.messageSeq] = true;
|
|
1391
|
+
existingIds[m.id] = true;
|
|
1392
|
+
});
|
|
1393
|
+
var mergedHead = list.filter(function(m) {
|
|
1394
|
+
if (m.messageSeq != null) return !existingSeqs[m.messageSeq];
|
|
1395
|
+
return !existingIds[m.id];
|
|
1396
|
+
});
|
|
1397
|
+
var earliest = self._state.earliestSeq;
|
|
1398
|
+
mergedHead.forEach(function(m) {
|
|
495
1399
|
if (m.messageSeq != null) {
|
|
496
|
-
if (earliest === null || m.messageSeq < earliest) earliest = m.messageSeq
|
|
1400
|
+
if (earliest === null || m.messageSeq < earliest) earliest = m.messageSeq;
|
|
497
1401
|
}
|
|
498
|
-
})
|
|
499
|
-
|
|
1402
|
+
});
|
|
500
1403
|
self._setState({
|
|
501
1404
|
messages: mergedHead.concat(self._state.messages),
|
|
502
1405
|
earliestSeq: earliest,
|
|
503
1406
|
historyHasMore: res.more === 1
|
|
504
|
-
})
|
|
505
|
-
}).catch(function
|
|
506
|
-
self._setState({ historyError: e && e.message ? e.message : String(e) })
|
|
507
|
-
}).then(function
|
|
508
|
-
self._setState({ historyLoading: false })
|
|
509
|
-
})
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
var
|
|
515
|
-
|
|
516
|
-
if (
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
var start = st.earliestSeq || 0
|
|
520
|
-
this._setState({ historyLoading: true, historyError: null })
|
|
521
|
-
|
|
1407
|
+
});
|
|
1408
|
+
}).catch(function(e) {
|
|
1409
|
+
self._setState({ historyError: e && e.message ? e.message : String(e) });
|
|
1410
|
+
}).then(function() {
|
|
1411
|
+
self._setState({ historyLoading: false });
|
|
1412
|
+
});
|
|
1413
|
+
};
|
|
1414
|
+
ChatStore.prototype.loadMoreHistory = function(limit) {
|
|
1415
|
+
limit = limit || 20;
|
|
1416
|
+
var self = this;
|
|
1417
|
+
var st = this._state;
|
|
1418
|
+
if (!st.channelId || !st.channelType) return Promise.resolve();
|
|
1419
|
+
if (st.historyLoading) return Promise.resolve();
|
|
1420
|
+
var start = st.earliestSeq || 0;
|
|
1421
|
+
this._setState({ historyLoading: true, historyError: null });
|
|
522
1422
|
return historyService.syncVisitorMessages({
|
|
523
1423
|
apiBase: st.apiBase,
|
|
524
1424
|
platformApiKey: st.platformApiKey,
|
|
@@ -526,233 +1426,236 @@ ChatStore.prototype.loadMoreHistory = function (limit) {
|
|
|
526
1426
|
channelType: st.channelType,
|
|
527
1427
|
startSeq: start,
|
|
528
1428
|
endSeq: 0,
|
|
529
|
-
limit
|
|
1429
|
+
limit,
|
|
530
1430
|
pullMode: 0
|
|
531
|
-
}).then(function
|
|
532
|
-
var myUid = self._state.myUid
|
|
533
|
-
var listAsc = res.messages.slice().sort(function
|
|
534
|
-
return (a.message_seq || 0) - (b.message_seq || 0)
|
|
535
|
-
}).map(function
|
|
536
|
-
return types.mapHistoryToChatMessage(m, myUid)
|
|
537
|
-
})
|
|
538
|
-
|
|
539
|
-
var
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
var earliest = self._state.earliestSeq
|
|
552
|
-
prepend.forEach(function (m) {
|
|
1431
|
+
}).then(function(res) {
|
|
1432
|
+
var myUid = self._state.myUid;
|
|
1433
|
+
var listAsc = res.messages.slice().sort(function(a, b) {
|
|
1434
|
+
return (a.message_seq || 0) - (b.message_seq || 0);
|
|
1435
|
+
}).map(function(m) {
|
|
1436
|
+
return types.mapHistoryToChatMessage(m, myUid);
|
|
1437
|
+
});
|
|
1438
|
+
var existingSeqs = {};
|
|
1439
|
+
var existingIds = {};
|
|
1440
|
+
self._state.messages.forEach(function(m) {
|
|
1441
|
+
if (typeof m.messageSeq === "number") existingSeqs[m.messageSeq] = true;
|
|
1442
|
+
existingIds[m.id] = true;
|
|
1443
|
+
});
|
|
1444
|
+
var prepend = listAsc.filter(function(m) {
|
|
1445
|
+
if (m.messageSeq != null) return !existingSeqs[m.messageSeq];
|
|
1446
|
+
return !existingIds[m.id];
|
|
1447
|
+
});
|
|
1448
|
+
var earliest = self._state.earliestSeq;
|
|
1449
|
+
prepend.forEach(function(m) {
|
|
553
1450
|
if (m.messageSeq != null) {
|
|
554
|
-
if (earliest === null || m.messageSeq < earliest) earliest = m.messageSeq
|
|
1451
|
+
if (earliest === null || m.messageSeq < earliest) earliest = m.messageSeq;
|
|
555
1452
|
}
|
|
556
|
-
})
|
|
557
|
-
|
|
1453
|
+
});
|
|
558
1454
|
self._setState({
|
|
559
1455
|
messages: prepend.concat(self._state.messages),
|
|
560
1456
|
earliestSeq: earliest,
|
|
561
1457
|
historyHasMore: res.more === 1
|
|
562
|
-
})
|
|
563
|
-
}).catch(function
|
|
564
|
-
self._setState({ historyError: e && e.message ? e.message : String(e) })
|
|
565
|
-
}).then(function
|
|
566
|
-
self._setState({ historyLoading: false })
|
|
567
|
-
})
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
1458
|
+
});
|
|
1459
|
+
}).catch(function(e) {
|
|
1460
|
+
self._setState({ historyError: e && e.message ? e.message : String(e) });
|
|
1461
|
+
}).then(function() {
|
|
1462
|
+
self._setState({ historyLoading: false });
|
|
1463
|
+
});
|
|
1464
|
+
};
|
|
1465
|
+
ChatStore.prototype.markStreamingStart = function(clientMsgNo) {
|
|
1466
|
+
if (!clientMsgNo) return;
|
|
1467
|
+
if (streamTimer) {
|
|
1468
|
+
try {
|
|
1469
|
+
clearTimeout(streamTimer);
|
|
1470
|
+
} catch (e) {
|
|
1471
|
+
}
|
|
1472
|
+
;
|
|
1473
|
+
streamTimer = null;
|
|
1474
|
+
}
|
|
1475
|
+
var self = this;
|
|
1476
|
+
this._setState({ isStreaming: true, streamCanceling: false, streamingClientMsgNo: clientMsgNo });
|
|
1477
|
+
streamTimer = setTimeout(function() {
|
|
1478
|
+
var s = self._state;
|
|
579
1479
|
if (s.isStreaming && s.streamingClientMsgNo === clientMsgNo) {
|
|
580
|
-
self._setState({ isStreaming: false, streamingClientMsgNo:
|
|
1480
|
+
self._setState({ isStreaming: false, streamingClientMsgNo: "", streamCanceling: false });
|
|
581
1481
|
}
|
|
582
|
-
}, STREAM_TIMEOUT_MS)
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
1482
|
+
}, STREAM_TIMEOUT_MS);
|
|
1483
|
+
};
|
|
1484
|
+
ChatStore.prototype.markStreamingEnd = function() {
|
|
1485
|
+
if (streamTimer) {
|
|
1486
|
+
try {
|
|
1487
|
+
clearTimeout(streamTimer);
|
|
1488
|
+
} catch (e) {
|
|
1489
|
+
}
|
|
1490
|
+
;
|
|
1491
|
+
streamTimer = null;
|
|
1492
|
+
}
|
|
1493
|
+
this._setState({ isStreaming: false, streamCanceling: false, streamingClientMsgNo: "" });
|
|
1494
|
+
};
|
|
1495
|
+
ChatStore.prototype.cancelStreaming = function(reason) {
|
|
1496
|
+
var self = this;
|
|
1497
|
+
var st = this._state;
|
|
1498
|
+
if (st.streamCanceling) return Promise.resolve();
|
|
1499
|
+
this._setState({ streamCanceling: true });
|
|
597
1500
|
if (!st.apiBase || !st.streamingClientMsgNo || !st.platformApiKey) {
|
|
598
|
-
this.markStreamingEnd()
|
|
599
|
-
return Promise.resolve()
|
|
1501
|
+
this.markStreamingEnd();
|
|
1502
|
+
return Promise.resolve();
|
|
600
1503
|
}
|
|
601
|
-
|
|
602
1504
|
return chatService.cancelStreaming({
|
|
603
1505
|
apiBase: st.apiBase,
|
|
604
1506
|
platformApiKey: st.platformApiKey,
|
|
605
1507
|
clientMsgNo: st.streamingClientMsgNo,
|
|
606
|
-
reason: reason ||
|
|
607
|
-
}).catch(function
|
|
608
|
-
console.warn(
|
|
609
|
-
}).then(function
|
|
610
|
-
self.markStreamingEnd()
|
|
611
|
-
})
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
var
|
|
618
|
-
var found = false
|
|
619
|
-
var updated = messages.map(function (m) {
|
|
1508
|
+
reason: reason || "user_cancel"
|
|
1509
|
+
}).catch(function(e) {
|
|
1510
|
+
console.warn("[Chat] Cancel streaming error:", e);
|
|
1511
|
+
}).then(function() {
|
|
1512
|
+
self.markStreamingEnd();
|
|
1513
|
+
});
|
|
1514
|
+
};
|
|
1515
|
+
ChatStore.prototype.appendStreamData = function(clientMsgNo, data) {
|
|
1516
|
+
if (!clientMsgNo || !data) return;
|
|
1517
|
+
var messages = this._state.messages;
|
|
1518
|
+
var found = false;
|
|
1519
|
+
var updated = messages.map(function(m) {
|
|
620
1520
|
if (m.clientMsgNo && m.clientMsgNo === clientMsgNo) {
|
|
621
|
-
found = true
|
|
622
|
-
return Object.assign({}, m, { streamData: (m.streamData ||
|
|
1521
|
+
found = true;
|
|
1522
|
+
return Object.assign({}, m, { streamData: (m.streamData || "") + data });
|
|
623
1523
|
}
|
|
624
|
-
return m
|
|
625
|
-
})
|
|
626
|
-
|
|
1524
|
+
return m;
|
|
1525
|
+
});
|
|
627
1526
|
if (!found) {
|
|
628
1527
|
var placeholder = {
|
|
629
|
-
id:
|
|
630
|
-
role:
|
|
631
|
-
payload: { type: 1, content:
|
|
632
|
-
time: new Date(),
|
|
633
|
-
clientMsgNo
|
|
1528
|
+
id: "stream-" + clientMsgNo,
|
|
1529
|
+
role: "agent",
|
|
1530
|
+
payload: { type: 1, content: "" },
|
|
1531
|
+
time: /* @__PURE__ */ new Date(),
|
|
1532
|
+
clientMsgNo,
|
|
634
1533
|
streamData: data
|
|
635
|
-
}
|
|
636
|
-
updated = messages.concat([placeholder])
|
|
1534
|
+
};
|
|
1535
|
+
updated = messages.concat([placeholder]);
|
|
637
1536
|
}
|
|
638
|
-
|
|
639
|
-
this._setStateThrottled({ messages: updated })
|
|
640
|
-
|
|
1537
|
+
this._setStateThrottled({ messages: updated });
|
|
641
1538
|
if (!this._state.isStreaming) {
|
|
642
|
-
this.markStreamingStart(clientMsgNo)
|
|
1539
|
+
this.markStreamingStart(clientMsgNo);
|
|
643
1540
|
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
var
|
|
650
|
-
var found = false
|
|
651
|
-
var updated = messages.map(function (m) {
|
|
1541
|
+
};
|
|
1542
|
+
ChatStore.prototype.appendMixedPart = function(clientMsgNo, part) {
|
|
1543
|
+
if (!clientMsgNo) return;
|
|
1544
|
+
var messages = this._state.messages;
|
|
1545
|
+
var found = false;
|
|
1546
|
+
var updated = messages.map(function(m) {
|
|
652
1547
|
if (m.clientMsgNo && m.clientMsgNo === clientMsgNo) {
|
|
653
|
-
found = true
|
|
654
|
-
var parts = (m.uiParts || []).slice()
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
var textContent = ''
|
|
1548
|
+
found = true;
|
|
1549
|
+
var parts = (m.uiParts || []).slice();
|
|
1550
|
+
if (part.type === "text" && parts.length > 0) {
|
|
1551
|
+
var last = parts[parts.length - 1];
|
|
1552
|
+
if (last.type === "text") {
|
|
1553
|
+
parts[parts.length - 1] = { type: "text", text: (last.text || "") + (part.text || "") };
|
|
1554
|
+
var textContent = "";
|
|
661
1555
|
for (var i = 0; i < parts.length; i++) {
|
|
662
|
-
if (parts[i].type ===
|
|
1556
|
+
if (parts[i].type === "text") textContent += parts[i].text || "";
|
|
663
1557
|
}
|
|
664
|
-
return Object.assign({}, m, { uiParts: parts, streamData: textContent })
|
|
1558
|
+
return Object.assign({}, m, { uiParts: parts, streamData: textContent });
|
|
665
1559
|
}
|
|
666
1560
|
}
|
|
667
|
-
parts.push(part)
|
|
668
|
-
var tc =
|
|
1561
|
+
parts.push(part);
|
|
1562
|
+
var tc = "";
|
|
669
1563
|
for (var j = 0; j < parts.length; j++) {
|
|
670
|
-
if (parts[j].type ===
|
|
1564
|
+
if (parts[j].type === "text") tc += parts[j].text || "";
|
|
671
1565
|
}
|
|
672
|
-
return Object.assign({}, m, { uiParts: parts, streamData: tc })
|
|
1566
|
+
return Object.assign({}, m, { uiParts: parts, streamData: tc });
|
|
673
1567
|
}
|
|
674
|
-
return m
|
|
675
|
-
})
|
|
676
|
-
|
|
1568
|
+
return m;
|
|
1569
|
+
});
|
|
677
1570
|
if (!found) {
|
|
678
|
-
var initParts = [part]
|
|
1571
|
+
var initParts = [part];
|
|
679
1572
|
var placeholder = {
|
|
680
|
-
id:
|
|
681
|
-
role:
|
|
682
|
-
payload: { type: 1, content:
|
|
683
|
-
time: new Date(),
|
|
684
|
-
clientMsgNo
|
|
1573
|
+
id: "stream-" + clientMsgNo,
|
|
1574
|
+
role: "agent",
|
|
1575
|
+
payload: { type: 1, content: "" },
|
|
1576
|
+
time: /* @__PURE__ */ new Date(),
|
|
1577
|
+
clientMsgNo,
|
|
685
1578
|
uiParts: initParts,
|
|
686
|
-
streamData: part.type ===
|
|
687
|
-
}
|
|
688
|
-
updated = messages.concat([placeholder])
|
|
1579
|
+
streamData: part.type === "text" ? part.text || "" : ""
|
|
1580
|
+
};
|
|
1581
|
+
updated = messages.concat([placeholder]);
|
|
689
1582
|
}
|
|
690
|
-
|
|
691
|
-
this._setStateThrottled({ messages: updated })
|
|
692
|
-
|
|
1583
|
+
this._setStateThrottled({ messages: updated });
|
|
693
1584
|
if (!this._state.isStreaming) {
|
|
694
|
-
this.markStreamingStart(clientMsgNo)
|
|
1585
|
+
this.markStreamingStart(clientMsgNo);
|
|
695
1586
|
}
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
var messages = this._state.messages.map(function (m) {
|
|
1587
|
+
};
|
|
1588
|
+
ChatStore.prototype.finalizeStreamMessage = function(clientMsgNo, errorMessage) {
|
|
1589
|
+
if (!clientMsgNo) return;
|
|
1590
|
+
var messages = this._state.messages.map(function(m) {
|
|
702
1591
|
if (m.clientMsgNo && m.clientMsgNo === clientMsgNo) {
|
|
703
|
-
var finalPayload = m.streamData
|
|
704
|
-
? { type: 1, content: m.streamData }
|
|
705
|
-
: m.payload
|
|
1592
|
+
var finalPayload = m.streamData ? { type: 1, content: m.streamData } : m.payload;
|
|
706
1593
|
var result = Object.assign({}, m, {
|
|
707
1594
|
payload: finalPayload,
|
|
708
|
-
streamData:
|
|
709
|
-
errorMessage: errorMessage ||
|
|
710
|
-
})
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
return result
|
|
1595
|
+
streamData: void 0,
|
|
1596
|
+
errorMessage: errorMessage || void 0
|
|
1597
|
+
});
|
|
1598
|
+
if (m.uiParts) result.uiParts = m.uiParts;
|
|
1599
|
+
return result;
|
|
714
1600
|
}
|
|
715
|
-
return m
|
|
716
|
-
})
|
|
717
|
-
|
|
718
|
-
this._setState({ messages: messages })
|
|
719
|
-
|
|
1601
|
+
return m;
|
|
1602
|
+
});
|
|
1603
|
+
this._setState({ messages });
|
|
720
1604
|
if (this._state.streamingClientMsgNo === clientMsgNo) {
|
|
721
|
-
this.markStreamingEnd()
|
|
1605
|
+
this.markStreamingEnd();
|
|
722
1606
|
}
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
var messages = this._state.messages
|
|
730
|
-
var idx = -1
|
|
1607
|
+
};
|
|
1608
|
+
ChatStore.prototype.ensureWelcomeMessage = function(text) {
|
|
1609
|
+
var t = (text || "").trim();
|
|
1610
|
+
if (!t) return;
|
|
1611
|
+
var messages = this._state.messages;
|
|
1612
|
+
var idx = -1;
|
|
731
1613
|
for (var i = 0; i < messages.length; i++) {
|
|
732
|
-
if (messages[i].id ===
|
|
1614
|
+
if (messages[i].id === "welcome") {
|
|
1615
|
+
idx = i;
|
|
1616
|
+
break;
|
|
1617
|
+
}
|
|
733
1618
|
}
|
|
734
|
-
|
|
735
1619
|
if (idx >= 0) {
|
|
736
|
-
var next = messages.slice()
|
|
737
|
-
next[idx] = Object.assign({}, messages[idx], { payload: { type: 1, content: t } })
|
|
738
|
-
this._setState({ messages: next })
|
|
1620
|
+
var next = messages.slice();
|
|
1621
|
+
next[idx] = Object.assign({}, messages[idx], { payload: { type: 1, content: t } });
|
|
1622
|
+
this._setState({ messages: next });
|
|
739
1623
|
} else {
|
|
740
1624
|
var welcome = {
|
|
741
|
-
id:
|
|
742
|
-
role:
|
|
1625
|
+
id: "welcome",
|
|
1626
|
+
role: "agent",
|
|
743
1627
|
payload: { type: 1, content: t },
|
|
744
|
-
time: new Date()
|
|
1628
|
+
time: /* @__PURE__ */ new Date()
|
|
1629
|
+
};
|
|
1630
|
+
this._setState({ messages: [welcome].concat(messages) });
|
|
1631
|
+
}
|
|
1632
|
+
};
|
|
1633
|
+
ChatStore.prototype.disconnect = function() {
|
|
1634
|
+
if (offMsg) {
|
|
1635
|
+
try {
|
|
1636
|
+
offMsg();
|
|
1637
|
+
} catch (e) {
|
|
745
1638
|
}
|
|
746
|
-
|
|
1639
|
+
;
|
|
1640
|
+
offMsg = null;
|
|
747
1641
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
1642
|
+
if (offStatus) {
|
|
1643
|
+
try {
|
|
1644
|
+
offStatus();
|
|
1645
|
+
} catch (e) {
|
|
1646
|
+
}
|
|
1647
|
+
;
|
|
1648
|
+
offStatus = null;
|
|
1649
|
+
}
|
|
1650
|
+
if (offCustom) {
|
|
1651
|
+
try {
|
|
1652
|
+
offCustom();
|
|
1653
|
+
} catch (e) {
|
|
1654
|
+
}
|
|
1655
|
+
;
|
|
1656
|
+
offCustom = null;
|
|
1657
|
+
}
|
|
1658
|
+
this.markStreamingEnd();
|
|
1659
|
+
IMService.disconnect();
|
|
1660
|
+
};
|
|
1661
|
+
module.exports = new ChatStore();
|